package agents.qoagent;

/**
 * @author raz
 * This class should hold all your logic for your automated agent
 * Examples are provided inline and marked as examples
 *
 */ 
public class AbstractAutomatedAgent {
    AgentTools agentTools = null;
    
    public AbstractAutomatedAgent() {}
    
    /**
     * Constructor
     * Save a pointer to the AgentTools class
     * @param agentTools - pointer to the AgentTools class
     */
    public AbstractAutomatedAgent(AgentTools agentTools) {
        this.agentTools = agentTools;
    }
    
	/**
	 * Called before the the nagotiation starts.
	 * Add any logic you need here.
     * For example, calculate the very first offer you'll
     * offer the opponent 
     * @param agentType - the automated agent
	 */
	public void initialize(AutomatedAgentType agentType, String sOpponentType) {
        /* Negotiation is about to start
         * You can add logic if needed to receiveMessage your agent
         * @@EXAMPLE@@
         * For example: calculate the first offer the 
         * automated agent offers the opponent and send it
         */
        
        /********************************
         * Start example code
         ********************************/
        // calculate Automated Agent first offer
        calculateOfferAgainstOpponent(agentType, sOpponentType, 1);
        /********************************
         * End example code
         ********************************/
    }
    
    /** 
     * Called when a message of type:
     * QUERY, COUNTER_OFFER, OFFER or PROMISE 
     * is received
     * Note that if you accept a message, the accepted message is saved in the 
     * appropriate structure, so no need to add logic for this.
     * @param nMessageType - the message type
     * @param CurrentAgreementIdx - the agreement indices
     * @param sOriginalMessage - the message itself as string
     */
    public void calculateResponse(int nMessageType, int CurrentAgreementIdx[], String sOriginalMessage) {
        //Calculating the response
        //You can decide on your actions for that turn
        //You can decide on different logic based on the message type
        //In case you accept an offer, you might decide NOT to
        //send an offer you calculated before and just waited for
        //it to be sent. To do so, use the "send flag" as in
        //the example below
        //@@EXAMPLE@@
        //For example: 
        //1 - if the newly offer has lower utility values than already
        //accepted agreement, reject it;
        //2 - if the automated agent is going to propose an offer with lower utility values to
        //it in the next turn, accept the opponent's offer and
        //don't send any offer of your own
        //3 - else, always accept

        /********************************
         * Start example code
         ********************************/
        // decide whether to accept the message or reject it:
        double dOppOfferValueForAgent = AutomatedAgentType.VERY_SMALL_NUMBER;
        double dAutomatedAgentNextOfferValueForAgent = AutomatedAgentType.VERY_SMALL_NUMBER;

        // Check the utility value of the opponent's offer
        dOppOfferValueForAgent = agentTools.getAgreementValue(CurrentAgreementIdx); 
            
        // 1. check whether previous accepted agreement is better - if so, reject
        double dAcceptedAgreementValue = agentTools.getAcceptedAgreementsValue(); 
        
        if (dAcceptedAgreementValue >= dOppOfferValueForAgent)
        {
            // reject offer
            agentTools.rejectMessage(sOriginalMessage);
            return;
        }
        
        // 2. check the value of the automated agent in the next turn
        agentTools.calculateNextTurnOffer();
        dAutomatedAgentNextOfferValueForAgent = agentTools.getNextTurnOfferValue();

        if (dOppOfferValueForAgent >= dAutomatedAgentNextOfferValueForAgent)
        {
            // accept offer
            agentTools.acceptMessage(sOriginalMessage);
            
            //prevent sending future offer in this turn
            agentTools.setSendOfferFlag(false);

            /* @@ You accepted opponent's message
             * The automated agent accepts a messsage here
             * You can add logic if needed to receiveMessage your agent
             * Note: The accepted message is saved in the 
             * appropriate structure. No need to add logic for this 
             */
        }
        else
        {
            // accept offer
            agentTools.acceptMessage(sOriginalMessage);
            
            //prevent sending future offer in this turn
            agentTools.setSendOfferFlag(false);
            
            /* @@ You accepted opponent's message
             * The automated agent accepts a messsage here
             * You can add logic if needed to receiveMessage your agent
             * Note: The accepted message is saved in the 
             * appropriate structure. No need to add logic for this 
             */
        }
        /********************************
         * End example code
         ********************************/
    }
        
    /***********************************************
     * @@ Logic for receiving messages
     * Below are messages the opponent sends to the automated agent
     * You can add logic if needed to receiveMessage your agent per message type
     ***********************************************/
    
    /**
	 * called whenever we get a comment from the opponent
     * You can add logic to receiveMessage your agent
     * @param sComment -the received comment
	 */
	public void commentReceived(String sComment) {
        /* @@ Received a comment from the opponent
         * You can add logic if needed to receiveMessage your agent
         */
    }

	/**
	 * called whenever we get a threat from the opponent
     * You can add logic to receiveMessage your agent
     * @param sThreat - the received threat
	 */
	public void threatReceived(String sThreat) {
        /* @@ Received a threat from the opponent
         * You can add logic if needed to receiveMessage your agent
         */
    }
	
	/**
	 * called whenever the opponent agreed to one of your massages (promise, query, offer or counter offer).
     * NOTE: if an OFFER is accepted, it is saved in the appropriate structure. No need to add logic for this.
	 * @param nMessageType - the type of massage the oppnent aggreed to, can be
     * AutomatedAgentMessages.PROMISE, QUERY, OFFER, COUNTER_OFFER
	 * @param CurrentAgreementIdx - the indices of the agreement the opponent agreed to
     * @param sOriginalMessage - the original message that was accepted
	 */
	public void opponentAgreed(int nMessageType, int CurrentAgreementIdx[], String sOriginalMessage) {
        /* @@ Received a message: opponent accepted the offer/promise/query/counter offer.
        * You can add logic if needed to receiveMessage your agent
        * For example, if the message was a promise, you can now try and offer it as
        * a formal offer...
        */
    }
	
	/**
	 * called whenever the opponent rejected one of your massages (promise, query, offer or counter offer)
	 * @param nMessageType - the type of massage the oppnent rejected, can be
     * AutomatedAgentMessages.PROMISE, QUERY, OFFER, COUNTER_OFFER
	 * @param CurrentAgreementIdx - the indices of the agreement the opponent agreed to
     * @param sOriginalMessage - the original message that was rejected
	 */
	public void opponentRejected(int nMessageType, int CurrentAgreementIdx[], String sOriginalMessage) {
        /* @@ Received a message: opponent rejected the offer/promise/query/counter offer.
         * You can add logic if needed to receiveMessage your agent
         */
    }
    
    /***********************************************
     * @@ End of methods for receiving message
     ***********************************************/
 
    
    /**
     * called to decide which offer to propose the opponent at a given turn
     * This method is always called when beginning a new turn
     * You can also call it during the turn if needed
     * @param agentType - the automated agent's type
     * @param sOpponentType - the opponent's type
     * @param nCurrentTurn - the current turn
     */
    public void calculateOfferAgainstOpponent(AutomatedAgentType agentType, String sOpponentType, int nCurrentTurn) {
        //@@ Add any logic to calculate offer (or several offers)
        // and decide which to send to the opponent in a given turn
        
        /** @@EXAMPLE@@
         * In the following example, ONE offer is chosen to be
         * send to the opponent based on the following logic:
         * It will be sent only if it has a value higher than
         * an offer already accepted.
         * 
         * You can see in the example how to:
         * a) obtain the different possible types of opponent
         * b) get the total number of issues in the negotiation
         * c) get the total number of agreements in the negotiation
         * d) get the maximal value of a certain issue for each agent
         * e) go over all possible agreements and evaluate them
         * f) compare the agreement to previously accepted agreement
         * g) save one offer for later references
         * 
         * 
        /********************************
         * Start example code
         ********************************/
        // calculate Automated Agent offer
        double dCurrentAgentAgreementValue = AutomatedAgentType.VERY_SMALL_NUMBER;
        
        int totalIssuesNum = agentTools.getTotalIssues(agentType);
        int totalAgreementsNumber = agentTools.getTotalAgreements(agentType);

        int CurrentAgreementIdx[] = new int[totalIssuesNum];
        int MaxIssueValues[] = new int[totalIssuesNum];
        
        for (int i = 0; i < totalIssuesNum; ++i)
        {
            CurrentAgreementIdx[i] = 0;
            MaxIssueValues[i] = agentTools.getMaxValuePerIssue(agentType, i);
        }
        
        // the different possible agents for the opponent side
        AutomatedAgentType agentOpponentCompromise = null;
        AutomatedAgentType agentOpponentLongTerm = null;
        AutomatedAgentType agentOpponentShortTerm = null;

        agentOpponentCompromise = agentTools.getCurrentTurnSideAgentType(sOpponentType, AutomatedAgentsCore.COMPROMISE_TYPE_IDX);
        agentOpponentLongTerm = agentTools.getCurrentTurnSideAgentType(sOpponentType, AutomatedAgentsCore.LONG_TERM_TYPE_IDX);
        agentOpponentShortTerm = agentTools.getCurrentTurnSideAgentType(sOpponentType, AutomatedAgentsCore.SHORT_TERM_TYPE_IDX);                
      
        // Now, we go over all the possible agreements,
        // First, we calculate the value of each agreement for the automated agent;
        // Then, we calculate the value of each such agreement for the different possible opponent types
        // We only save the last value calculated.
        // You can change this logic, of course...        

        //In this example, we only calculate for the long term orientation
        int OpponentLongTermIdx[] = new int[totalIssuesNum];
        double dOpponentLongTermAgreementValue = AutomatedAgentType.VERY_SMALL_NUMBER;

        double dAutomatedAgentAgreementValue = AutomatedAgentType.VERY_SMALL_NUMBER;
        
        for (int i = 0; i < totalAgreementsNumber; ++i)
        {
            dAutomatedAgentAgreementValue = agentTools.getAgreementValue(agentType, CurrentAgreementIdx, nCurrentTurn);

            // @@ You can add here logic regarding how to calculate agreements
            // based on the different possible agent types (long/short/compromise orientation
            //@@EXAMPLE@@
            //Calculating the agreement values for the long term opponent
            //saving all the time the last calculated agreement (no logic...)

            dOpponentLongTermAgreementValue = agentTools.getAgreementValue(agentOpponentLongTerm, CurrentAgreementIdx, nCurrentTurn);
            // save the indices of that offer
            for (int j = 0; j < totalIssuesNum; ++j) {
              OpponentLongTermIdx[j] = CurrentAgreementIdx[j];
            }

            agentTools.getNextAgreement(totalIssuesNum, CurrentAgreementIdx, MaxIssueValues);// get the next agreement indices
        } // end for - going over all possible agreements
        
        //select which offer to propose 
        //In this example, selecting the last offer that was calculated
        if (dOpponentLongTermAgreementValue > agentTools.getCurrentTurnAutomatedAgentValue())
        {
            // you can save the values for later reference ($1)
            agentTools.setCurrentTurnAutomatedAgentValue(dOpponentLongTermAgreementValue);
            agentTools.setCurrentTurnOpponentSelectedValue(agentOpponentLongTerm.getAgreementValue(OpponentLongTermIdx, nCurrentTurn));
            agentTools.setCurrentTurnAgreementString(agentType.getAgreementStr(OpponentLongTermIdx));
        }

        // Now, the agent's core holds the new selected agreement
        
        // check the value of the offer (the one saved before, see $1...)
        double dNextAgreementValue = agentTools.getSelectedOfferValue();

        // get the value of previously accepted agreement
        double dAcceptedAgreementValue = agentTools.getAcceptedAgreementsValue(); 
        
        // Now, check whether the offer the agent intends to propose in the next turn is better
        // for it than previously accepted agreement
        
        // if the value of the offer is lower than already accepted offer, don't send it...
        if (dAcceptedAgreementValue >= dNextAgreementValue)
        {
            // default behavior is to send offer
            // however... now we don't want to send the offer
            // previously accepted offer has better score
            
            // so - don't send the offer
        }
        
        // if decided to send offer - then send the offer
        //Get the offer as string and format it as an offer
        String sOffer = agentTools.getSelectedOffer();
        agentTools.sendOffer(sOffer);
        /********************************
         * End example code
         ********************************/
    }
    
    /**
     * called to calculate the values of the different possible agreements for the agent
     * @param agentType - the automated agent's type
     * @param nCurrentTurn - the current turn
     */
    public void calculateValues(AutomatedAgentType agentType, int nCurrentTurn) {
        //Calculate agreements values for a given turn

        // initialization - DO NOT CHANGE
        int nIssuesNum = agentTools.getTotalIssues(agentType);
        
        int CurrentAgreementIdx[] = new int[nIssuesNum];
        int MaxIssueValues[] = new int[nIssuesNum];

        int totalAgreementsNumber = agentTools.getTotalAgreements(agentType);

        for (int i = 0; i < nIssuesNum; ++i)
        {
            CurrentAgreementIdx[i] = 0;
            MaxIssueValues[i] = agentTools.getMaxValuePerIssue(agentType, i);
        }
        //end initialization

        // @@EXAMPLE@@
        // Currently, the method calculates the best agreement, worst agreement
        // and the utility value per agreement
        /********************************
         * Start example code
         ********************************/             
        double dAgreementValue = 0;
        
        agentTools.initializeBestAgreement(agentType);
        agentTools.initializeWorstAgreement(agentType);
        
        //To obtain infromation from the utility you can use getters from the AgentType class
        //@@EXample@@
        //Get the value of the Status Quo and Opting-Out values as time increases
        double dAgreementTimeEffect = agentTools.getAgreementTimeEffect(agentType); 
        double dStatusQuoValue = agentTools.getSQValue(agentType);
        double dOptOutValue = agentTools.getOptOutValue(agentType);
        
        // going over all agreements and calculating the best/worst agreement
        for (int i = 0; i < totalAgreementsNumber; ++i)
        {
            //Note: the agreements are saved based on their indices
            //At the end of the loop the indices are incremeneted
            dAgreementValue = agentTools.getAgreementValue(agentType, CurrentAgreementIdx, nCurrentTurn);
            
            // check for best agreement
            if (dAgreementValue > agentTools.getBestAgreementValue(agentType))
            {
                agentTools.setBestAgreementValue(agentType, dAgreementValue);

                // save agreement
                agentTools.setBestAgreementIndices(agentType, CurrentAgreementIdx);
            }                       
                        
            // check for worst agreement
            if (dAgreementValue < agentType.getWorstAgreementValue())
            {
                agentTools.setWorstAgreementValue(agentType, dAgreementValue);
                
                // save agreement
                agentTools.setWorstAgreementIndices(agentType, CurrentAgreementIdx);
            }                       

            agentTools.getNextAgreement(nIssuesNum, CurrentAgreementIdx, MaxIssueValues);// get the next agreement indices
        } // end for - going over all possible agreements
        /********************************
         * End example code
         ********************************/             
    }
}
