import edu.mit.six825.bn.functiontable.*;
import edu.mit.six825.bn.bayesnet.*;
import edu.mit.six825.bn.inputs.*;

import java.util.*;

/**
 * A wrapper that lets us run any given solver in any given
 * configuration against any of the specified queries. See below for
 * invocation syntax.
 *
 * @author nocturne
 */
public class RunTest {

    public static Solver PickSolver (String ss) {
	if (ss.equals("enum")) {
	    return(new EnumerationSolver());
	} else if (ss.equals("ve")) {
	    return(new VESolver());
	} else if (ss.equals("lw")) {
	    return(new LWSolver());
        } else if (ss.equals("gibbs")) {
            return(new GibbsSolver());
	} else {
	    throw new IllegalArgumentException("Unknown solver type " + ss);
	}
    }

    public static VESolver.ElimOrderer PickElim (String ss) {
	if (ss.equals("dumb")) {
	    System.out.println("Using dumb orderer.");
	    return((new VESolver.DumbOrder()));
	} else if (ss.equals("random")) {
	    System.out.println("Using random orderer.");
	    return((new VESolver.RandomOrder()));
	} else if (ss.equals("greedy")) {
	    System.out.println("Using greedy orderer.");
	    return((new VESolver.GreedyOrder()));
	} else {
	    throw new IllegalArgumentException("Unknown elim type " + ss);
	}
    }

    /**
     * Intended argument syntax:
     *    --solver ( enum | ve | lw )
     *    --elim   ( dumb | random | greedy )
     *    --weight (num)
     *    --samples (num)
     *    --discard (prefixlen)
     *    --randorder
     *    ( 1.[123] | 2.[1234] )
     *
     * specifying an elimination orderer is only relevant if you're
     * using the VE solver.
     * specifying a required weight is only relevant for LW solving.
     * number of samples, discard prefix len, and random ordering are
     * only relevant to Gibbs sampling.
     * 
     * Sample args: "--solver ve --elim dumb 2.2"
     *              "--solver lw 1.3 --weight 100"
     *              "--solver gibbs samples 1000 --discard 500 --randorder"
     *
     */
    public static void main(String[] args) {

	Solver      solver  = new EnumerationSolver();
	VESolver.ElimOrderer elimord = new VESolver.DumbOrder();
	String   whichquery = "1.1";
        double   requiredWeight = 100.0;
        int      requiredSamples = 1000;
        int      discardPrefixLen = 500;
        boolean  randOrder = false;

	for (int i=0; i < args.length; i++) {
	    String arg = args[i];
	    if (arg.equals("--solver")) {
		i++;
		if (i < args.length) {
		    solver = PickSolver(args[i]);
		} else {
		    throw new IllegalArgumentException("--solver requires"
						       + " an argument");
		}
	    } else if (arg.equals("--elim")) {
		i++;
		if (i < args.length) {
		    elimord = PickElim(args[i]);
		} else {
		    throw new IllegalArgumentException("--elim requires"
						       + " an argument");
		}
	    } else if (arg.equals("--weight")) {
		i++;
		if (i < args.length) {
		    requiredWeight = Double.parseDouble(args[i]);
		} else {
		    throw new IllegalArgumentException("--weight requires"
						       + " an argument");
		}
	    } else if (arg.equals("--samples")) {
		i++;
		if (i < args.length) {
		    requiredSamples = Integer.parseInt(args[i]);
		} else {
		    throw new IllegalArgumentException("--weight requires"
						       + " an argument");
		}
	    } else if (arg.equals("--discard")) {
		i++;
		if (i < args.length) {
		    discardPrefixLen = Integer.parseInt(args[i]);
		} else {
		    throw new IllegalArgumentException("--discard requires"
						       + " an argument");
		}
	    } else if (arg.equals("--randorder")) {
                randOrder = true;
	    } else {
		whichquery = arg;
	    }
	}

	if (solver.getClass() == VESolver.class) {
	    ((VESolver)solver).setOrderer(elimord);
        } else if (solver.getClass() == LWSolver.class) {
            ((LWSolver)solver).setRequiredWeight(requiredWeight);
        } else if (solver.getClass() == GibbsSolver.class) {
            ((GibbsSolver)solver).setRequiredSamples(requiredSamples);
            ((GibbsSolver)solver).setDiscardPrefixLen(discardPrefixLen);
            ((GibbsSolver)solver).randomFlipChoice(randOrder);
        }

	BayesNet   bnet;
	String []  evidencearr;
	String     query;

	if (whichquery.equals("1.1")) {
	    bnet = Nets.getBurglary();
	    evidencearr = new String[] {"JohnCalls", "true",
					"MaryCalls", "true"};
	    query = "Burglary";
	} else if (whichquery.equals("1.2")) {
	    bnet = Nets.getBurglary();
	    evidencearr = new String[] {"Burglary",  "true",
					"JohnCalls", "true"};
	    query = "Earthquake";
	} else if (whichquery.equals("1.3")) {
	    bnet = Nets.getInsurance();
	    evidencearr = new String[] {"Age", "Adolescent",
					"Airbag", "False",
					"Mileage", "TwentyThou"};
	    query = "PropCost";
	} else if (whichquery.equals("2.1")) {
	    bnet = Nets.getInsurance();
	    evidencearr = new String[] {"Age", "Adolescent",
					"Airbag", "False",
					"MakeModel", "Luxury",
					"Mileage", "TwentyThou"};
	    query = "PropCost";
	} else if (whichquery.equals("2.2")) {
	    bnet = Nets.getInsurance();
	    evidencearr = new String[] {"Age", "Adolescent",
					"Airbag", "False",
					"GoodStudent", "True",
					"Mileage", "TwentyThou"};
	    query = "PropCost";
	} else if (whichquery.equals("2.3")) {
	    bnet = Nets.getCarpo();
	    evidencearr = new String[] {"N116", "0",
					"N41", "2",
					"N84", "1"};
	    query = "N104";
	} else if (whichquery.equals("2.4")) {
	    bnet = Nets.getCarpo();
	    evidencearr = new String[] {"N116", "0",
					"N152", "1",
					"N43", "1"};
	    query = "N73";
	} else {
	    throw new IllegalArgumentException("Unknown query "
					       + whichquery);
	}

	solver.setBayesNet(bnet);

	Assignment evidence;
	try {
	    evidence =
		VESolver.ConstructEvidence(bnet.nodes, evidencearr);

	    solver.setEvidence(evidence);
	}
	catch (IllegalArgumentException e) {
	    System.out.println("ERROR: " + e);
	    return;
	}

	BayesNetNode querynode;
	try {
	    querynode = bnet.nodes.getNode(query);
	}
	catch (IllegalArgumentException e) {
	    System.out.println("ERROR: " + e);
	    return;
	}

	long starttime = System.currentTimeMillis();
	Function answer = solver.query(querynode);
	long endtime = System.currentTimeMillis();

	System.out.println("Query:    " + query);
	System.out.println("Evidence: " + evidence);
	System.out.println(answer);
	System.out.println("Solver time elapsed: " + (endtime-starttime) +
			   " milliseconds.");
    }
}
