package actor;

import generated.ConstraintType;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

import data.DerivativeType;
import data.Derivative;
import data.Price;
import game.Store;

/**
 * Derivative creation agent
 * 
 * @author Rukmal Fernando
 * @author Hardik Kotecha
 * @author Radhika Srinivasan
 */
public class CreationAgent
{
	ArrayList<DerivativeType> derivativeTypes;
	String creator;

	/**
	 * only constructor
	 */
	public CreationAgent( ArrayList<DerivativeType> pTakenTypes, String pCreatorName )
	{
		setDerivativeTypes( pTakenTypes );
		setCreator( pCreatorName );
	}
	
	/*========================================================================
	|                           getters and setters
	+-----------------------------------------------------------------------*/
	
	/**
	 * get the derivative types
	 * 
	 * @return ArrayList<RawMaterialType>
	 */
	ArrayList<DerivativeType> getDerivativeTypes()
	{
		return derivativeTypes;
	}

	/**
	 * set the list of raw material types
	 * 
	 * @param pTypes : ArrayList<RawMaterialType>
	 */
	void setDerivativeTypes( ArrayList<DerivativeType> pTypes )
	{
		derivativeTypes = pTypes;
	}

	/**
	 * get the creator 
	 * 
	 * @return String
	 */
	protected String getCreator()
	{
		return creator;
	}

	/**
	 * set the creator
	 * 
	 * @param pCreator : String
	 */
	protected void setCreator( String pCreator )
	{
		creator = pCreator;
	}
	
	
	/*-----------------------------------------------------------------------
	|                       end getters and setters
	+======================================================================*/
	
	/**
	 * create the derivative 
	 * 
	 * @return Derivative
	 */
	public Derivative createDerivative()
	{
		
		// Repeat until a new and unique derivative is created
		Derivative newDerivative;
		while (true) {
			newDerivative = createDerivativeWithMethod1();
			
			if (isUnique(newDerivative.getType())) {
				break;
			}
			
			newDerivative = createDerivativeWithMethod2();
			if (isUnique(newDerivative.getType())) {
				break;
			}
		}
		
		return newDerivative;
	}
	
	private Derivative createDerivativeWithMethod1() {
		// This method picks one of the pure types between 2^(1-6)
		Random r = new Random();
		int bitToSet = 1 + r.nextInt(6);
		int relationType = (int)Math.pow(2, bitToSet);
		
		// added by Radhika for minimum price calc
		
		// Only 0.19 is achievable
		//Price p = new Price(0.3f);
		float littleLessThanCheapest = Store.minDerPriceToBuy - 0.02f;
		float cheap = Math.max(littleLessThanCheapest, 0.3f);
		Price p = new Price(cheap);
		
		// OLD
		// String dummyInput = ("<derivativeType><constraintType><relation>" + relationType + "</relation></constraintType></derivativeType>");
		
		String dummyInput = ("<derivativeType><constraintType><relation>" + relationType + "</relation></constraintType><constraintType><relation>255</relation></constraintType></derivativeType>");
		DerivativeType dt = new DerivativeType(generated.DerivativeType.parse(dummyInput));
		
		return new Derivative(getCreator(), p, dt);
	}

	private Derivative createDerivativeWithMethod2() {
		// This method picks one relation with the first bit set, not the last bit
		// This generates up to 64*64 combinations
		Random r = new Random();
		int relationType1 = r.nextInt(64) * 2 + 1;
		
		// And another with its complement
		int relationType2 = 255 - relationType1;
		
		
		// Price p = new Price(0.55f);
		
		// The price is 0.6. Only 0.5 is achievable
		// Added by Radhika for cheapest Derv Calculation
		
		float littleLessThanCheapest = Store.minDerPriceToBuy - 0.02f;
		float cheap = Math.max(littleLessThanCheapest, 0.6f);
		Price p = new Price(cheap);
		
		String dummyInput = ("<derivativeType><constraintType><relation>" + relationType1 + "</relation></constraintType><constraintType><relation>" + relationType2 + "</relation></constraintType></derivativeType>");
		DerivativeType dt = new DerivativeType(generated.DerivativeType.parse(dummyInput));
		
		return new Derivative(getCreator(), p, dt);
	}

	/** This method checks to see if the given derivative type is not found in the store */
	private boolean isUnique(DerivativeType t) {
		Iterator<DerivativeType> takenTypesIter = getDerivativeTypes().iterator();
		Set<Integer> thisType = getTypes(t);
		
		while (takenTypesIter.hasNext()) {
			DerivativeType takenType = (DerivativeType)takenTypesIter.next();
			Set<Integer> takenTypeRelations = getTypes(takenType);
			if (takenTypeRelations.equals(thisType)) {
				return false;
			}
		}
		
		return true;
	}
	
	private Set<Integer> getTypes(DerivativeType t) {
		TreeSet<Integer> set = new TreeSet<Integer>();
		Enumeration<ConstraintType> enm = t.getInnerType().get_constrainttype_list().elements();
		while (enm.hasMoreElements()) {
			set.add(new Integer(((ConstraintType)enm.nextElement()).get_relation().get_v()));
		}
		return set;
		
	}
	
}