
package hsr.smart;

import edu.neu.ccs.demeterf.lib.*;
import scg.*;
import scg.ParseException;
import scg.protocol.*;
import hsr.*;
import hsr.avatar.HSRAvatar;

import java.util.Random;

/**
 * @author Astro
 */

/** Representation of HSRAvatar */
public class HSRAvatarSmart implements AvatarI{
	
    /** Construct a(n) HSRAvatar Instance */
    public HSRAvatarSmart(){
    }
    /** Is the given object Equal to this HSRAvatar? */
    public boolean equals(Object o){
        if(!(o instanceof HSRAvatar))return false;
        if(o == this)return true;
        HSRAvatar oo = (HSRAvatar)o;
        return true;
    }
    /** Parse an instance of HSRAvatar from the given String */
    public static HSRAvatar parse(String inpt) throws hsr.avatar.ParseException{
        return new hsr.avatar.TheParser(new java.io.StringReader(inpt)).parse_HSRAvatar();
    }
    /** Parse an instance of HSRAvatar from the given Stream */
    public static HSRAvatar parse(java.io.InputStream inpt) throws hsr.avatar.ParseException{
        return new hsr.avatar.TheParser(inpt).parse_HSRAvatar();
    }
    /** Parse an instance of HSRAvatar from the given Reader */
    public static HSRAvatar parse(java.io.Reader inpt) throws hsr.avatar.ParseException{
        return new hsr.avatar.TheParser(inpt).parse_HSRAvatar();
    }


	private Config config;
	
	/* Constructor to be called during registration where you supply config*/
	public HSRAvatarSmart(Config cfg){
		config = cfg;
	}
	
	/**proposing random unique claims which are not in the forbidden list**/
	public List<Claim> propose(List<Claim> forbiddenClaims){
		List<Claim> claims = List.create();
		SCGConfig scg_cfg = config.getScgCfg();
		int maxProposals = scg_cfg.getMaxProposals() -1;
		for(int i =0; i< maxProposals;i++){
			Claim claim = generateRandomClaim();
			while(forbiddenClaims.contains(claim) || claims.contains(claim)){
				claim = generateRandomClaim();
			}
			claims = claims.append(claim);
		}
		return claims;
	}
	
	/**Random oppose method - randomly agrees, refutes or strengthens by a factor of 1 **/
	
	public List<OpposeAction> oppose(List<Claim> claimsToBeOpposed) {
		return claimsToBeOpposed.map(new List.Map<Claim, OpposeAction>() {
			public OpposeAction map(Claim claim) {
				HSRInstance instance = ((HSRInstanceSet) claim.getInstanceSet())
						.getSingleton();
				int n = instance.getN();
				double q = claim.getQuality();
				try {
					if (n <= 2000)
					{
					SolveRequest solveRequest = SolveRequest
							.parse("solve hsr.HSRInstance {{ " + instance
									+ " }} " + claim);
					HSRSolution solution = (HSRSolution) solve(solveRequest);
					int depth = solution.findDepth();
					double myQuality = (double) depth / (double) n;
//					System.out.println("myQuality: " + myQuality);
					if (claim.getProtocol() instanceof ExistsForAll) {
						if (myQuality <= q) 
							return new Refuting();
						else 
							return new Agreement();
					} else if (claim.getProtocol() instanceof ForAllExists) {
						if (myQuality <= q) {
							return new Strengthening(myQuality);
						} else {
							return new Agreement();
						}
					}
					} else {
						Random rand = new Random();
						int randOppose = rand.nextInt(3);
						if(randOppose == 0)
							return new Agreement();
						else if(randOppose == 1){
							int qa = (int)Math.ceil(claim.getQuality() * n);
							SCGConfig scg_cfg = config.getScgCfg();
							if (claim.getProtocol() instanceof ForAllExists){
								if(qa>1)
									return new Strengthening(claim.getQuality() - scg_cfg.getMinStrengthening());
								else
									return new Refuting();
							}else{
								if(qa<n){
									return new Strengthening(claim.getQuality() + scg_cfg.getMinStrengthening());
								}else{
									return new Refuting();
								}
							}		 
						}else 
							return new Refuting();		
					}
				} catch (Exception e) {
						Random rand = new Random();
						int randOppose = rand.nextInt(3);
						if(randOppose == 0)
							return new Agreement();
						else if(randOppose == 1){
							int qa = (int)Math.ceil(claim.getQuality() * n);
							SCGConfig scg_cfg = config.getScgCfg();
							if (claim.getProtocol() instanceof ForAllExists){
								if(qa>1)
									return new Strengthening(claim.getQuality() - scg_cfg.getMinStrengthening());
								else
									return new Refuting();
							}else{
								if(qa<n){
									return new Strengthening(claim.getQuality() + scg_cfg.getMinStrengthening());
								}else{
									return new Refuting();
								}
							}		 
						}else 
							return new Refuting();		            
				}
				return new Refuting();
			}
		});
	}
	
	/** providing instance - in HSR this is trivial as the instanceSet is singleton**/
	public InstanceI provide(Claim claimToBeProvided){
		HSRInstanceSet hsrInstanceSet = (HSRInstanceSet) claimToBeProvided.getInstanceSet();
		HSRInstance hsrInstance = new HSRInstance(hsrInstanceSet.getSingleton().getN(), hsrInstanceSet.getSingleton().getK());
		return hsrInstance;
	}
	
	public static int LEN = 3000; // size of the pascal triangle matrix
	private int[][] initialArray = new int[LEN][];

	public void initializeArray(){
		for(int i=0; i<LEN; i++){
			initialArray[i] = new int[LEN];
		}
		for(int i = 0; i<LEN; i++)
			for(int j = 0; j < 0; j++)
				initialArray[i][j] = 0;
	}
	
	public int[][] getProceedArray(int n, int col){
			initializeArray();
			while (!isExistLargerN(n, col)){
				expandArray();
			}
			return this.initialArray;
	}
	
	private boolean isExistLargerN(int n, int col){
		boolean flag = false;
		for(int i = 0; i < LEN; i++){
			if (initialArray[i][col] >= n) flag = true;
		}
		return flag;
	}
/**
 * expanding the matrix to row by row
 */
	private void expandArray(){
		int c = getCurrentRow();
		if (c == -1){
			System.out.println("Matrix is too small. Please increase the pascal triangle's size.");
		}
		initialArray[c][0] = 1;
		initialArray[0][c] = (int)Math.pow(2, c-1);
		for(int i = 1; i < c; i++) {
			int j = c-i;
			initialArray[i][j] = initialArray[i-1][j] + initialArray[i][j-1];
		}
	}
/**
 * get a new row which is current row	
 * @return
 */
	private int getCurrentRow(){
		int c = 0;
		if (initialArray[0][LEN-1] != 0){
			LEN = 2*LEN;
		}
		for(int i = 0; i < LEN; i++){
			if((i != 0) && (initialArray[0][i] == 0)) return c = i;
		}
		return c;
	}
	
	/** get the row index which contains the number no less than n */
	private int getRootRow(int k, int[][] arr){
		for(int i = 0; i < LEN; i++){
			if (arr[i][k] == 0) return i-1;
		}
		return 0;
	}
	
	/** print the array to string */
	private String printArray(){
		String str = "";
		for(int i = 0; i < LEN; i++){
			str += "\n";
			for(int j = 0; j < LEN; j++)
				str += initialArray[i][j];
		}
		return str;
	}
	
	private int LEAF_INDEX;
	
	/** solving using modified pascal triangle **/
	public SolutionI solve(SolveRequest solveRequest) {
		HSRInstance hsrInstance = (HSRInstance) solveRequest.getInstance();
		int n = hsrInstance.getN();
		int k = hsrInstance.getK();
		
		LEAF_INDEX = 0;
    	HSRAvatarSmart sa = new HSRAvatarSmart();
    	HSRSolution s = sa.smartSolve(n, k);
    	System.gc();
		return s;
	}
	
	private HSRSolution smartSolve(int n, int k){
		//initializeArray(); // what's a good size?
		int[][] arr = getProceedArray(n, k);
		// System.out.println(printArray());
		int r = getRootRow(k, arr);
		HSRSolution solution = solve(r, n, k, arr);
		// System.out.println("Solution Depth: " + solution.findDepth());
		return solution;
	}

	private HSRSolution solve(int r, int n, int k, int[][] arr) {
		HSRSolution leftYes, rightNo;
		if (arr[r][k] == 1) {
			return new Simple(LEAF_INDEX++);
		}
		// construct the left subtree
		if (arr[r][k - 1] == 1) {
			leftYes = new Simple(LEAF_INDEX);
			LEAF_INDEX += 1;
		} else {
			leftYes = solve(r, n, k - 1, arr);
		}
		// construct the right subtree
		if (r == 0) {
			rightNo = solve(r, n, k - 1, arr);
		} else {
			rightNo = solve(r - 1, n, k, arr);
		}
		int question;
		question = getMaxNodeValue(leftYes) + 1;
		if (question > n - 1) {
			return leftYes;
		} else {
			return new Compound(question, leftYes, rightNo);
		}
	}
	
	/** get the maximum value in the nodes of the given tree */
	private int getMaxNodeValue(HSRSolution leftSubTree) {
		int maxNodeValue = 0;
		if (leftSubTree instanceof Compound) {
			int nodeValue = Math.max(getMaxNodeValue(((Compound) leftSubTree)
					.getYes()), getMaxNodeValue(((Compound) leftSubTree)
					.getNo()));
			int rootValue = ((Compound) leftSubTree).getQuestion();
			maxNodeValue = Math.max(rootValue, nodeValue);
		} else {
			maxNodeValue = ((Simple) leftSubTree).getHighest_safe_rung();
		}
		return maxNodeValue;
	}
	
	/** Generates random claim with N >=200
	 * @throws ParseException */
	private Claim generateRandomClaim() {
		Random rand = new Random();
		boolean nextBoolean = rand.nextBoolean();
		
		int randN = 1000 + rand.nextInt(500);
		// check if N value is less than maxN in HSRConfig
		HSRConfig hsr_cfg = (HSRConfig) config.getDomainConfig();
		if (randN > hsr_cfg.getMaxN()) {
			randN = 2 + rand.nextInt(hsr_cfg.getMaxN() - 2);
		}
		int randK = 1 + rand.nextInt(20);
		int randQ = 1 + rand.nextInt(randN - 1);
		HSRInstance singleton = new HSRInstance(randN, randK); // get the
																// maximum
																// allowed n
																// from config
		HSRInstanceSet instanceSet = new HSRInstanceSet(singleton);
		// random claim for finding the solution
		Claim claim = new Claim(instanceSet, nextBoolean ? ForAllExists
				.getInstance() : ExistsForAll.getInstance(), ((double) randQ)
				/ randN, ((double) randQ) / randN);
		try {
			SolveRequest solveRequest = SolveRequest.parse("solve hsr.HSRInstance {{ "
					+ singleton + " }} " + claim);
			HSRSolution solution = (HSRSolution) solve(solveRequest);
			// get the optimal solution
			int depth = solution.findDepth();
			// update the quality for a optimal  claim
			double myQuality = (double) depth / (double) randN;
//			claim = new Claim(instanceSet, ExistsForAll.getInstance(), myQuality,
//					1.0);
			claim = new Claim(instanceSet, nextBoolean ? ForAllExists
					.getInstance() : ExistsForAll.getInstance(), myQuality,
					1.0);
			return claim;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		System.gc();
		return claim;
	}

    /** DGP method from Class Print */
    public String print(){ return hsr.avatar.Print.PrintM(this); }
    /** DGP method from Class PrintToString */
    public String toString(){ return hsr.avatar.PrintToString.PrintToStringM(this); }
    /** DGP method from Class Display */
    public String display(){ return hsr.avatar.Display.DisplayM(this); }

}


