package waiter;

import java.awt.Point;
import java.util.GregorianCalendar;

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

/** 
 * Returns the waiter's preference to use a resource in the bar or to give a product to a 
 * customer. These functions evaluate social interest as the chance to meet a friend in 
 * the near future, thus performing a planned meeting to have chat.
 * 
 * @author Francisco Grimaldo
 */
public class Sociability implements UtilityFunctionInt 
{
	/**
	  * Returns the name of the utility function
	  *
	  * @return	"sociability"
	  */
    public String getId()
	{
		return "sociability";
	}
	
	/**
	  * Returns the waiter's preference to use a resource in the bar or to give a product to a 
	  * customer. These functions evaluate social interest as the chance to meet a friend in 
	  * the near future, thus performing a planned meeting to have chat.
	  *
	  * @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
	{
		Unifier un = new Unifier();
		Literal l, res;
		float utility = 0;

		// 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();
		Literal auctionedAction = (Literal) termArray[0];
		String resource = auctionedAction.getTerm(0).toString();
		String executorAction = termArray[1].toString();
		Literal nextAction = (Literal) termArray[2];
		String executorNextAction = termArray[3].toString();
		String agName = agArch.getAgName();
		
		if (executorAction.equals(agName))
		{
			
		}
		else
		{
			
		}
		
		l = Literal.parseLiteral("friend(" + auctioneer + ")");
		res = agArch.getTS().getAg().findBel(l, un);
		if (res != null)  // Only for friends
		{
			
			// Internal social utility
			
			long tEndAction = 0;
			l = Literal.parseLiteral("t_init_action(_,_,_,_)");
			res = agArch.getTS().getAg().findBel(l, un);
			if (res != null)
			{
				long execTime = executionTime((Literal)res.getTerm(0), resource, agArch);
				long t_init = (Integer.parseInt(res.getTerm(3).toString()) +              // seconds
							   Integer.parseInt(res.getTerm(2).toString())*60 +           // minutes
							   Integer.parseInt(res.getTerm(1).toString())*60*60 )*1000;  // hours
				long t_now = new GregorianCalendar().getTimeInMillis();
				long t_passed = (t_now - t_init) % execTime;				
				tEndAction = execTime - t_passed;				
			}
			long[] intervalA = {tEndAction, tEndAction + executionTime(auctionedAction, resource, agArch)};
			long[] intervalB = {0,executionTime(nextAction, resource, agArch)};
			if ( (intervalB[0] <= intervalA[1] && intervalB[0] >= intervalA[0]) ||
				 (intervalA[0] <= intervalB[1] && intervalA[0] >= intervalB[0]) )   // If intersection
			{
				boolean near = false;
				
				if (nextAction.getFunctor().equals("none"))
					near = computeNear(resource, auctioneer, agArch);
				else if ( auctionedAction.getFunctor().equals("give") && nextAction.getFunctor().equals("give") )
					near = true;
				else 
					near = computeNear(resource, nextAction.getTerm(0).toString(), agArch);
					
				if (near)
					socUtilityInt = 1;
			}
			
			// External social utility
			l = Literal.parseLiteral("state(executeAction,_,_,_)");
			res = agArch.getTS().getAg().findBel(l, un);
			Literal currentAction = Literal.parseLiteral("none");
			if (res != null) 
				currentAction = Literal.parseLiteral(res.getTerm(2).toString());
			tEndAction = 0;
			l = Literal.parseLiteral("t_init_action(_,_,_,_)");
			res = agArch.getTS().getAg().findBel(l, un);
			if (res != null)
			{
				long execTime = executionTime((Literal)res.getTerm(0), resource, agArch);
				long t_init = (Integer.parseInt(res.getTerm(3).toString()) +             // seconds
							  Integer.parseInt(res.getTerm(2).toString())*60 +           // minutes
							  Integer.parseInt(res.getTerm(1).toString())*60*60 )*1000;  // hours
				long t_now = new GregorianCalendar().getTimeInMillis();
				long t_passed = (t_now - t_init) % execTime;				
				tEndAction = execTime - t_passed;				
			}
			intervalA[0] = 0; intervalA[1] = tEndAction;
			intervalB[0] = 0; intervalB[1] = executionTime(auctionedAction, resource, agArch);
			if ( (intervalB[0] <= intervalA[1] && intervalB[0] >= intervalA[0]) ||
				 (intervalA[0] <= intervalB[1] && intervalA[0] >= intervalB[0]) )   // If intersection
			{
				boolean near = false;
				
				if (currentAction.getFunctor().equals("none"))
					near = computeNear(resource, auctioneer, agArch);
				else if ( auctionedAction.getFunctor().equals("give") && currentAction.getFunctor().equals("give") )
					near = true;
				else 
					near = computeNear(resource, currentAction.getTerm(0).toString(), agArch);
					
				if (near)
					socUtilityExt = 1;
			}
		}
		
		return utility;
	}
    
	/* ------------------------ ADDITIONAL FUNCTIONS -----------------------*/

    /** Returns the estimated execution time for an action */	
	private long executionTime(Literal action, String resource, MADeMAgArch agArch)
	{
		if (action.getFunctor().equals("none"))
			return 0;
		
		Unifier un = new Unifier();
		Literal l, res;
		long execTime = 0;
		
		if (action.getFunctor().equals("use"))
		{
			l = Literal.parseLiteral("t_use(" + resource + ",_)");
			res = agArch.getTS().getAg().findBel(l, un);
		    execTime = (long) ((NumberTermImpl) res.getTerm(1)).solve();
		}
        else // give(R)
		{
			l = Literal.parseLiteral("delay(_)");
			res = agArch.getTS().getAg().findBel(l, un);
			long delay = (long) ((NumberTermImpl) res.getTerm(0)).solve();

			l = Literal.parseLiteral("size(_,_)");
			res = agArch.getTS().getAg().findBel(l, un);
			long size = (long) ((NumberTermImpl) res.getTerm(0)).solve();
			execTime = 2 * delay * size;
		}
		
		return execTime; 
	}
	
	/** Tests if the distance between two resources is less than 4 cells */
	private boolean computeNear(String resourceA, String resourceB, MADeMAgArch agArch)
	{
		Unifier un = new Unifier();
		Literal l = Literal.parseLiteral("pos(" + resourceA + ",_,_)");
		Literal res = agArch.getTS().getAg().findBel(l, un);
		if (res == null)
			return false;
		Point posA = new Point((int)((NumberTermImpl)res.getTerm(1)).solve(), (int)((NumberTermImpl)res.getTerm(2)).solve());
		l = Literal.parseLiteral("pos(" + resourceB + ",_,_)");
		res = agArch.getTS().getAg().findBel(l, un);
		if (res == null)
			return false;
		Point posB = new Point((int)((NumberTermImpl)res.getTerm(1)).solve(), (int)((NumberTermImpl)res.getTerm(2)).solve());
						
		int distX = Math.abs((int)posA.getX() - (int)posB.getX());
		int distY = Math.abs((int)posA.getY() - (int)posB.getY());
		if ( (distX <= 4) && (distY <= 4) )
			return true;
		else
			return false;
	}

}

