// ** This file was generated with DemFGen (vers:4/15/2011)

package csp.avatar;

import edu.neu.ccs.demeterf.lib.*;
import edu.neu.ccs.demeterf.lib.*;
import java.util.Random;
import java.lang.reflect.Method;
import scg.*;
import scg.protocol.*;
import csp.*;





/** Representation of CSPAvatar */
public class CSPAvatar implements AvatarI{

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

	
	private Config config;
	private	Random random = new Random();
	
	/** Constructor to be called during registration where you supply config */
	public CSPAvatar(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();
		for(int i=0;i<config.getScgCfg().getMaxProposals();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 */
	public List<OpposeAction> oppose(List<Claim> claimsToBeOpposed){
		return claimsToBeOpposed.map(new List.Map<Claim, OpposeAction>() {
			public OpposeAction map(Claim claim){
				Random rand = new Random();
				int randOppose = rand.nextInt(3);
				if(randOppose == 0)
					return new Agreement();
				else if(randOppose == 1){
					double q = claim.getQuality();
					SCGConfig scg_cfg = config.getScgCfg();
					if (claim.getProtocol() instanceof PositiveSecret){
						if(q > scg_cfg.getMinStrengthening())
							return new Strengthening(claim.getQuality() - scg_cfg.getMinStrengthening());
						else
							return new Refuting();
					}else{
						if(q < scg_cfg.getMinStrengthening()){
							return new Strengthening(claim.getQuality() + scg_cfg.getMinStrengthening());
						}else{
							return new Refuting();
						}
					}
				}
				else 
					return new Refuting();
            }
		});
	}
	
	/** providing instance method - provides symmetric Instance of given instanceSet */
	public InstanceI provide(Claim claimToBeProvided){
		CSPInstanceSet cspInstanceSet = (CSPInstanceSet) claimToBeProvided.getInstanceSet();
		CSPConfig cfg = (CSPConfig) config.getDomainConfig();
		int numVars = cfg.getMaxVariables();
		List<Var> vars = List.<Var>create();
		
		/**bug found here, fixed by vars = vars.append... and ident("v"... */
		
		for(int i = 1; i <= numVars;i++){ vars = vars.append(new Var(new ident("v" + i)));}
		CSPInstance cspInstance = new CSPInstance(vars, symmetric(cspInstanceSet.getType().toList(), numVars, 3));
		return cspInstance;
	}
	
	/** solve using random assignment of values to all the variables */
	public SolutionI solve(SolveRequest solveRequest){
		CSPInstance i = (CSPInstance)solveRequest.getInstance();
		Random rand = new Random();
		CSPSolution solution = new CSPSolution(new ListMap<Var, Boolean>(randomAssign(i.getVars(), rand.nextDouble())));
		return solution;
	}
	
	/** Random Assignment to each of the variables */
    private List<Entry<Var, Boolean>> randomAssign(List<Var> vs, final double bias){
        return vs.map(new List.Map<Var, Entry<Var, Boolean>>() {
            public Entry<Var, Boolean> map(Var v){
                return Entry.create(v, random.nextDouble() > bias);
            }
        });
    }
	
	private Claim generateRandomClaim() {
		Random rand = new Random();
		ListSet<Integer> type = ListSet.create();
		CSPConfig cfg = (CSPConfig) config.getDomainConfig();
		if(cfg.getMaxRelNum() >= 127)
			type = type.add(127);
		type = type.add(rand.nextInt(cfg.getMaxRelNum()));
		CSPInstanceSet instanceSet = new CSPInstanceSet(type);
		SCGConfig scgConfig = config.getScgCfg();
		// To Change: The protocol instance must be one of the
		// allowed protocols mentioned in SCGConfig
		Cons<FullyQualifiedClassName> protocolsAllowed = scgConfig.getProtocols();
		ProtocolI protocol = generateRandomAllowedProtocol(protocolsAllowed);
		
		return new Claim(instanceSet, protocol,1,1);
	}
	
	/** generates a random protocol instance from the given protocolsAllowed */
	private ProtocolI generateRandomAllowedProtocol(Cons<FullyQualifiedClassName> protocolsAllowed){
		Random rand = new Random();
		FullyQualifiedClassName randProtocol = protocolsAllowed.lookup(rand.nextInt(protocolsAllowed.length()));
		ProtocolI protocol = null;
		try{
		Class<?> protocolClass = Class.forName(randProtocol.print().trim());
		Method instance = protocolClass.getMethod("parse", String.class);
		protocol = (ProtocolI) instance.invoke(null, randProtocol.print().trim());
		}catch(Exception ex){
			ex.printStackTrace();
		}
		return protocol;
	}
	
	/** Create a symmetric formula using the given Relation numbers */
    public static Cons<csp.Clause> symmetric(List<Integer> rs, int numVars, int rank){
        final List<List<Var>> vars = combs(numVars, rank, new List.Build<Var>(){
            public Var build(int n){ return new Var(new ident("v"+n)); }
        });
        //** Create a list of Clauses for-all Relations with each the variable lists
        return (Cons<csp.Clause>) rs.fold(new List.Fold<Integer,List<Clause>>(){
            public List<Clause> fold(final Integer r, final List<Clause> rst){
                return vars.fold(new List.Fold<List<Var>,List<Clause>>(){
                    public List<Clause> fold(List<Var> vs, List<Clause> othrs){
                        return othrs.push(new Clause(r, 1, vs));
                    }
                }, rst);
            }
        }, List.<Clause>create());
    }
    
    /** N choose K, the dynamic programming way! ;) */
    public static <X> List<List<X>> combs(int n, int k, List.Build<X> b){
        List<List<X>>[][] tbl = new List[n+1][k+1];

        for(int ik = 0; ik <= k; ik++)
            for(int in = 0; in <= n; in++){
                List<List<X>> newlst;
                if(ik == 0)newlst = List.<List<X>>create(List.<X>create());
                else if(in < ik)newlst = List.create();
                else newlst = tbl[in-1][ik].append(tbl[in-1][ik-1].map(new PushN<X>(b.build(in))));   
                tbl[in][ik] = newlst;
            }
        return tbl[n][k];
    }
	
	/** Sort the relations to find the most important one... */
    public static int importantRelation(CSPInstanceSet t){
        int rel = t.getType().toList().sort(new List.Comp<Integer>(){
            public boolean comp(Integer ra, Integer rb){
                return (// Implied
                        ((ra & rb) == ra) ||
                        // A is even, B is odd
                        (ra%2 == 0 && rb%2 == 1) ||
                        // Both even or both odd
                        ((ra%2 == rb%2) && bitsSet(ra) < bitsSet(rb)));
            }
        }).top();
        return rel;
    }
    
    /** Determin the number of bits set in the given relation number */
    public static int bitsSet(int i){
        int c = 0;
        while(i > 0){
            if((i&1) > 0)c++;
            i >>= 1;
        }
        return c;
    }
    
    /** Push the given number onto the front of each list */
    private static class PushN<X> extends List.Map<List<X>,List<X>>{
        X x;
        public PushN(X xx){ x = xx; }
        public List<X> map(List<X> l){ return l.push(x); }
    }

    /** DGP method from Class Display */
    public String display(){ return csp.avatar.Display.DisplayM(this); }
    /** DGP method from Class Print */
    public String print(){ return csp.avatar.Print.PrintM(this); }
    /** DGP method from Class ToStr */
    public String toStr(){ return csp.avatar.ToStr.ToStrM(this); }
    /** DGP method from Class PrintToString */
    public String toString(){ return csp.avatar.PrintToString.PrintToStringM(this); }
    /** DGP method from Class HashCode */
    public int hashCode(){ return csp.avatar.HashCode.HashCodeM(this); }

}


