package waiter;

import jmadem.*;
import jason.asSyntax.*;
import jason.asSemantics.*;
import java.util.*;

/** 
 * Returns the waiter's preference to use a resource in the bar or to give a product to a  
 * a customer. The utility value is computed following an efficiency point of view. This
 * function aims at maximizing the number of tasks being performed at the same time and 
 * represents the waiters' willingness to serve orders as fast as possible. 
 * 
 * @author Francisco Grimaldo
 */
public class Performance implements UtilityFunctionInt 
{
	/**
	  * Returns the name of the utility function
	  *
	  * @return	"performance"
	  */
    public String getId()
	{
		return "performance";
	}
	
	/**
	  * Returns the waiter's preference to use a resource in the bar or to give a product to a  
	  * a customer. The utility value is computed following an efficiency point of view. This
	  * function aims at maximizing the number of tasks being performed at the same time and 
	  * represents the waiters' willingness to serve orders as fast as possible. 
	  *
	  * @param	auctioneer	agent asking for preference
	  * @param	alloc	allocation being evaluated
	  * @param	agArch	architecture of the MADeM agent
	  * 
	  * @throws	UtilityFunctionException when any error occurs
	  * @return	number of leadered workers that are free 
	  */
    public float computeUtility(String auctioneer, Allocation a, MADeMAgArch agArch) throws UtilityFunctionException
	{
		// Allocation of the form: execute_actions(Action, Ag, ActionNext, Auctioneer)
		// where Action and ActionNext can be either use(Resource) or give(Resource, RequestPoint)
		Literal alloc = (Literal)a.getTerm();
		Term [] termArray = alloc.getTermsArray();
		
		// No preference when task is allocated to another agent
		if ( !termArray[1].toString().equals(agArch.getAgName()) )
			return 0;
		
		// Compute utility value according to the type of action
		float utility = 0;
		Literal auctionedAction = (Literal) termArray[0];
		String resource = auctionedAction.getTerm(0).toString();
		int remaining_tasks = 0;
		float twait = 1;
		int NB_MAX_USES = 4;   		// Maximum number of uses for a resource
		Unifier un = new Unifier();
		Literal l, res;
		PredicateIndicator pi;
		String actionAssignedTo;
		Iterator<Literal> it;

		// Compute utility value for an action of the type: use(Resource)
		if (auctionedAction.getFunctor().equals("use"))
		{
			l = Literal.parseLiteral("state(executeAction,_,use(" + resource + "),use)");
			res = agArch.getTS().getAg().findBel(l, un);
			boolean is_using = (res != null);
			int n_uses = 0;
			if (is_using)
			{
				pi = new PredicateIndicator("using", 2);
				it = agArch.getTS().getAg().getBB().getCandidateBeliefs(pi);
				if (it != null)
				{
					while (it.hasNext())
					{
						res = it.next();
						n_uses++;
					}
				}
			}
			
			// Agent is already using the resource and the resource is not complete
			if ( is_using && (n_uses < NB_MAX_USES) ) 
				utility = 1;
			else
			{
				// Get remaining and bidding tasks
				pi = new PredicateIndicator("l_actions", 5);
				it = agArch.getTS().getAg().getBB().getCandidateBeliefs(pi);
				if (it != null)
				{
					while (it.hasNext())
					{
						res = it.next();
						actionAssignedTo = res.getTerm(4).toString();
						if ( (!res.getTerm(3).toString().equals("bidding")) && 
							 (actionAssignedTo.equals("self") || actionAssignedTo.equals("none")) )
							remaining_tasks++;
					}
				}
				
				// Get time to wait to access the resouce 
				// (position-queue+1) or (size-queue+1)
				l = Literal.parseLiteral("pos_in_queue(" + resource + ",_)");
				res = agArch.getTS().getAg().findBel(l, un);
				if (res != null)
				{
					twait += (float) ((NumberTermImpl)res.getTerm(1)).solve();
				}
				else
				{
					l = Literal.parseLiteral("queue_size(" + resource + ",_)");
					res = agArch.getTS().getAg().findBel(l, un);
					if (res != null)
						twait += (float) ((NumberTermImpl)res.getTerm(1)).solve();
				}					
				
				utility = 1 / (remaining_tasks + twait);
			}
		}
		// Compute utility value for an action of the type: use(Resource)
		else if (auctionedAction.getFunctor().equals("give"))
		{ 
			l = Literal.parseLiteral("state(executeAction,_,give(" + resource + ",_),_)");
			res = agArch.getTS().getAg().findBel(l, un);
			boolean is_giving = (res != null);
			boolean has_hand_free = true;
			if (is_giving)
			{
				l = Literal.parseLiteral("hands_busy(_)");
				res = agArch.getTS().getAg().findBel(l, un);
				has_hand_free = ( (res != null) && 
						          (((NumberTermImpl)res.getTerm(0)).solve() < 2) );
			}
			
			// Agent is already giving and has one hand free
			if (is_giving && has_hand_free) 
				utility = 1;
			else
			{
				// Get remaining and bidding tasks
				pi = new PredicateIndicator("l_actions", 5);
				it = agArch.getTS().getAg().getBB().getCandidateBeliefs(pi);
				if (it != null)
				{
					while (it.hasNext())
					{
						res = it.next();
						actionAssignedTo = res.getTerm(4).toString();
						if ( (!res.getTerm(3).toString().equals("bidding")) && 
							 (actionAssignedTo.equals("self") || actionAssignedTo.equals("none")) )
							remaining_tasks++;
					}
				}
			
				if (remaining_tasks > 0)
					utility = 1.0f / remaining_tasks;
				else
					utility = 1;
			}
		}

		return utility;
	}
}

