package edu.umich.soar.hpp;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;

import edu.umich.soar.hpp.HPPlan.PlanType;

/**
 * Example program that constructs a hierarchical-probabilistic plan via console
 * interaction, controls stochasticity, and outputs information from the generated
 * script.
 */
public class HPPTGen {
	
	//////////////////////////////////////////////////////
	// Using enum's gets you no spelling errors for free!
	//////////////////////////////////////////////////////
	
	private enum DayOfWeek { sunday, monday, tuesday, wednesday, thursday, friday, saturday };
	private enum TimeOfDay { morning, lunch, evening };
	private enum Hunger { none, peckish, hungry };
	
	private enum WorkLocation { desk, couch };
	private enum WorkDuration { six, seven, eight, nine, ten };
	
	private enum RelaxationActivity { read, tv };
	private enum RelaxationPosition { sit, lie };
	
	//////////////////////////////////////////////////////
	
	/**
	 * Console usage string
	 */
	private static void usage() {
		System.out.println( "usage: java " + HPPTGen.class.getCanonicalName() + " <agent> <year> <month> <day> <time of day> <hunger> <work> <trial #>" );
		
		System.out.print("time of day: {");
		for (TimeOfDay t : TimeOfDay.values()) {
			System.out.print(" "+t);
		}
		System.out.println(" }");
		
		System.out.print("hunger: {");
		for (Hunger h : Hunger.values()) {
			System.out.print(" "+h);
		}
		System.out.println(" }");
		
		System.out.print("work: {");
		for (Boolean w : new Boolean[]{ true, false }) {
			System.out.print(" "+w);
		}
		System.out.println(" }");
		
		System.out.println("trial: { 0-9 }");
	}

	/**
	 * Main function
	 * 
	 * @param args console arguments
	 */
	public static void main(String[] args) {
		
		// Validate input
		if (args.length!=8) {
			usage();
			System.exit(0);
		}
		
		/////////////////////////////////////////
		// Parse Input => Vars + RNG Seed
		/////////////////////////////////////////
		
		String agentLocation = args[0];
		
		String seedString = "";
		Integer seedValue = null;
		
		//
				
		HPPVariable vDayOfWeek = null;
		{
			GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(args[1]), Integer.parseInt(args[2])-1, Integer.parseInt(args[3]));
			
			for (DayOfWeek d : DayOfWeek.values()) {
				if (d.ordinal() == cal.get(Calendar.DAY_OF_WEEK)-1) {
					vDayOfWeek = new HPPGivenVariable("DayOfWeek", d.toString());
				}
			}
			
			seedString = ( seedString + new SimpleDateFormat("yyMMdd").format(cal.getTime()) );
		}
		
		HPPVariable vTimeOfDay = null;
		{
			for (TimeOfDay t : TimeOfDay.values()) {
				if (t.name().compareTo(args[4])==0) {
					vTimeOfDay = new HPPGivenVariable("TimeOfDay", t.toString());
					seedString = ( seedString + t.ordinal() );
				}
			}
			
			if (vTimeOfDay==null) {
				usage();
				System.exit(0);
			}
		}
		
		HPPVariable vHunger = null;
		{
			for (Hunger h : Hunger.values()) {
				if (h.name().compareTo(args[5])==0) {
					vHunger = new HPPGivenVariable("Hunger", h.toString());
					seedString = ( seedString + h.ordinal() );
				}
			}
			
			if (vHunger==null) {
				usage();
				System.exit(0);
			}
		}
		
		HPPVariable vWork = null;
		{
			for (Boolean w : new Boolean[]{ true, false }) {
				if (w.toString().compareTo(args[6])==0) {
					vWork = new HPPGivenVariable("Work", w.toString());
					seedString = ( seedString + ((w)?("1"):("0")) );
				}
			}
			
			if (vWork==null) {
				usage();
				System.exit(0);
			}
		}
		
		HPPVariable vTrial = null;
		{
			Integer temp = Integer.parseInt( args[7] );
			if ( ( temp >= 0 ) && ( temp <= 9 ) )
			{
				vTrial = new HPPGivenVariable("TrialNumber", args[7]);
				seedString = ( seedString + args[7] );
			}
			
			if (vTrial==null) {
				usage();
				System.exit(0);
			}
		}
		
		HPPVariable vSeed = new HPPGivenVariable("RNGSeed", seedString);
		seedValue = Integer.parseInt(seedString);
					
		/////////////////////////////////////////
		// Variables: Main
		/////////////////////////////////////////
		
		HPPVariable vConsumeMeal = new HPPBinaryVariable("ConsumeMeal", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPConstraint(vHunger, Hunger.none.toString()) }, true, 0.05),
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPConstraint(vHunger, Hunger.peckish.toString()) }, true, 0.4),
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPConstraint(vHunger, Hunger.hungry.toString()) }, true, 0.9),
		});
		
		HPPVariable vDoWork = new HPPBinaryVariable("DoWork", false, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPBinaryConstraint(vWork, true) }, true, 0.95),
		});
		
		HPPVariable vCleanHouse = new HPPBinaryVariable("CleanHouse", false, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{ 
						new HPPBinaryConstraint(vConsumeMeal, true),
						new HPPConstraint(vDayOfWeek, DayOfWeek.monday.toString()),
						new HPPConstraint(vTimeOfDay, TimeOfDay.morning.toString())
				}, true, 0.80),
				new HPPCPTBinaryEntry(new HPPConstraint[]{ 
						new HPPBinaryConstraint(vConsumeMeal, true),
						new HPPConstraint(vDayOfWeek, DayOfWeek.friday.toString()),
						new HPPConstraint(vTimeOfDay, TimeOfDay.evening.toString())
				}, true, 0.05),
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPBinaryConstraint(vConsumeMeal, true) }, true, 0.25),
		});
		
		/////////////////////////////////////////
		// Variables: Meal
		/////////////////////////////////////////
		
		HPPVariable vMealPrepareDrink = new HPPBinaryVariable("BringDrinkToTable", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.5),
		});
		
		HPPVariable vMealRobotBringsMat = new HPPBinaryVariable("MealRobotBringsPlateMat", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.9),
		});
		
		HPPVariable vMealToDo = new HPPBinaryVariable("MealToDo", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.2),
		});
		
		HPPVariable vMealTV = new HPPBinaryVariable("MealTV", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.5),
		});
		
		HPPVariable vMealDrink = new HPPBinaryVariable("MealDrink", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.5),
		});
		
		/////////////////////////////////////////
		// Variables: Work
		/////////////////////////////////////////
		
		HPPVariable vWorkLocation = new HPPMultiVariable("WorkLocation", null, new HPPCPTEntry[] {
				new HPPCPTEntry(
						new HPPConstraint[]{}, 
						new HPPCPTValue[]{ 
								new HPPCPTValue(WorkLocation.couch.toString(), 0.2), 
								new HPPCPTValue(WorkLocation.desk.toString(), 0.8) 
						}
				),
		});
		
		HPPVariable vWorkMusic = new HPPBinaryVariable("WorkMusic", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPConstraint(vWorkLocation, WorkLocation.desk.toString()) }, true, 0.9),
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPConstraint(vWorkLocation, WorkLocation.couch.toString()) }, true, 0.5),
		});
		
		HPPVariable vWorkToDo = new HPPBinaryVariable("WorkToDo", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.5),
		});
		
		HPPVariable vWorkDuration = new HPPMultiVariable("WorkDuration", null, new HPPCPTEntry[] {
				new HPPCPTEntry(
						new HPPConstraint[]{}, 
						new HPPCPTValue[]{ 
								new HPPCPTValue(WorkDuration.six.toString(), 0.1),
								new HPPCPTValue(WorkDuration.seven.toString(), 0.2),
								new HPPCPTValue(WorkDuration.eight.toString(), 0.4),
								new HPPCPTValue(WorkDuration.nine.toString(), 0.2),
								new HPPCPTValue(WorkDuration.ten.toString(), 0.1),
						}
				),
		});
		
		/////////////////////////////////////////
		// Variables: Clean
		/////////////////////////////////////////
		
		HPPVariable vCleanWashBeforeDrawers = new HPPBinaryVariable("WashBeforeDrawers", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.2),
		});
		
		HPPVariable vCleanRobotBringsMat = new HPPBinaryVariable("CleanRobotBringsPlateMat", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPBinaryConstraint(vCleanWashBeforeDrawers, true) }, true, 0.1),
				new HPPCPTBinaryEntry(new HPPConstraint[]{ new HPPBinaryConstraint(vCleanWashBeforeDrawers, false) }, true, 1.0),
		});
		
		HPPVariable vDryAndPutAwayDishes = new HPPBinaryVariable("DryAndPutAwayDishes", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.5),
		});
		
		/////////////////////////////////////////
		// Variables: Relaxation
		/////////////////////////////////////////
		
		HPPVariable vRelaxationActivity = new HPPMultiVariable("RelaxationActivity", null, new HPPCPTEntry[] {
				new HPPCPTEntry(
						new HPPConstraint[]{}, 
						new HPPCPTValue[]{ 
								new HPPCPTValue(RelaxationActivity.read.toString(), 0.3), 
								new HPPCPTValue(RelaxationActivity.tv.toString(), 0.7) 
						}
				),
		});
		
		HPPVariable vRelaxationPosition = new HPPMultiVariable("RelaxationPosition", null, new HPPCPTEntry[] {
				new HPPCPTEntry(
						new HPPConstraint[]{}, 
						new HPPCPTValue[]{ 
								new HPPCPTValue(RelaxationPosition.sit.toString(), 0.1), 
								new HPPCPTValue(RelaxationPosition.lie.toString(), 0.9) 
						}
				),
		});
		
		HPPVariable vRelaxationToDo = new HPPBinaryVariable("RelaxationToDo", null, new HPPCPTBinaryEntry[] {
				new HPPCPTBinaryEntry(new HPPConstraint[]{}, true, 0.25),
		});
		
		/////////////////////////////////////////
		// Plans
		/////////////////////////////////////////
		
		HPPlan myMealMatPreparationPlan = new HPPlan(PlanType.set,
				new HPPVariable[]{
					vMealRobotBringsMat,
				},
				new HPPAction[]{					
					new HPPAction("SendRobotToTableWithPlateMat", new HPPConstraint[]{ new HPPBinaryConstraint(vMealRobotBringsMat, true) }, null),
					new HPPAction("BringPlateMatToTablePersonally", new HPPConstraint[]{ new HPPBinaryConstraint(vMealRobotBringsMat, false) }, null),
				}
		);
		
		HPPlan myMealPreparationPlan = new HPPlan(PlanType.set,
				new HPPVariable[]{
					vMealPrepareDrink,
				},
				new HPPAction[]{
					new HPPAction("BringFoodToTable", new HPPConstraint[]{}, null),
					new HPPAction("BringDrinkToTable", new HPPConstraint[]{ new HPPBinaryConstraint(vMealPrepareDrink, true) }, null),
					new HPPAction("PreparePlateMat", new HPPConstraint[]{}, myMealMatPreparationPlan),					
				}
		);
		
		HPPlan myMealPlan = new HPPlan(PlanType.seq,
				new HPPVariable[]{
					vMealToDo,
					vMealTV,
					vMealDrink,
				},
				new HPPAction[]{
					new HPPAction("PrepareForMeal", new HPPConstraint[]{}, myMealPreparationPlan),
					new HPPAction("SitOnCouch", new HPPConstraint[]{}, null),
					new HPPAction("MealToDo", new HPPConstraint[]{ new HPPBinaryConstraint(vMealToDo, true ) }, null),
					new HPPAction("EatMeal", new HPPConstraint[]{}, null),
					new HPPAction("WatchTV", new HPPConstraint[]{ new HPPBinaryConstraint(vMealTV, true ) }, null),
					new HPPAction("GetDrink", new HPPConstraint[]{ new HPPBinaryConstraint(vMealDrink, true ) }, null),
				}
		);
		
		HPPlan myWorkingPlan = new HPPlan(PlanType.set,
				new HPPVariable[]{
					vWorkDuration,
				},
				new HPPAction[]{
					new HPPAction("WorkForSixMinutes", new HPPConstraint[]{ new HPPConstraint(vWorkDuration, WorkDuration.six.toString() ) }, null),
					new HPPAction("WorkForSevenMinutes", new HPPConstraint[]{ new HPPConstraint(vWorkDuration, WorkDuration.seven.toString() ) }, null),
					new HPPAction("WorkForEightMinutes", new HPPConstraint[]{ new HPPConstraint(vWorkDuration, WorkDuration.eight.toString() ) }, null),
					new HPPAction("WorkForNineMinutes", new HPPConstraint[]{ new HPPConstraint(vWorkDuration, WorkDuration.nine.toString() ) }, null),
					new HPPAction("WorkForTenMinutes", new HPPConstraint[]{ new HPPConstraint(vWorkDuration, WorkDuration.ten.toString() ) }, null),
				}
		);
		
		HPPlan myWorkSitPlan = new HPPlan(PlanType.set,
				new HPPVariable[]{					
				},
				new HPPAction[]{
					new HPPAction("SitOnCouch", new HPPConstraint[]{ new HPPConstraint(vWorkLocation, WorkLocation.couch.toString()), new HPPBinaryConstraint(vConsumeMeal, false) }, null),
					new HPPAction("SitAtDesk", new HPPConstraint[]{ new HPPConstraint(vWorkLocation, WorkLocation.desk.toString()) }, null),
				}
		);
		
		HPPlan myWorkPlan = new HPPlan(PlanType.seq,
				new HPPVariable[]{
					vWorkLocation,
					vWorkMusic,
					vWorkToDo,
				},
				new HPPAction[]{
					new HPPAction("SitDown", new HPPConstraint[]{}, myWorkSitPlan),
					new HPPAction("TurnOnMusic", new HPPConstraint[]{ new HPPBinaryConstraint(vWorkMusic, true) }, null),
					new HPPAction("WorkToDo", new HPPConstraint[]{ new HPPBinaryConstraint(vWorkToDo, true ) }, null),
					new HPPAction("Working", new HPPConstraint[]{}, myWorkingPlan),
				}
		);
		
		HPPlan myKitchenCleanPlan = new HPPlan(PlanType.seq,
				new HPPVariable[]{
				},
				new HPPAction[]{
					new HPPAction("BringPlatesAndTrashToKitchen", new HPPConstraint[]{}, null),
					new HPPAction("TossRubish", new HPPConstraint[]{}, null),
					new HPPAction("LeaveRestInSink", new HPPConstraint[]{}, null),
				}
		);
		
		HPPlan myWashPlan = new HPPlan(PlanType.seq,
				new HPPVariable[]{
					vDryAndPutAwayDishes,
				},
				new HPPAction[]{
					new HPPAction("WashDishes", new HPPConstraint[]{}, null),
					new HPPAction("DryDishes", new HPPConstraint[]{ new HPPBinaryConstraint(vDryAndPutAwayDishes, true) }, null),
					new HPPAction("PutAwayDishes", new HPPConstraint[]{ new HPPBinaryConstraint(vDryAndPutAwayDishes, true) }, null),
				}
		);
		
		HPPlan myRestCleanPlan = new HPPlan(PlanType.seq,
				new HPPVariable[]{
					vCleanWashBeforeDrawers,
					vCleanRobotBringsMat,
				},
				new HPPAction[]{
					new HPPAction("WashDishesEarly", new HPPConstraint[]{ new HPPBinaryConstraint(vCleanWashBeforeDrawers,true) }, myWashPlan),
					new HPPAction("SendRobotToDrawersWithPlateMat", new HPPConstraint[]{ new HPPBinaryConstraint(vCleanRobotBringsMat, true) }, null),
					new HPPAction("BringPlateMatToDrawersPersonally", new HPPConstraint[]{ new HPPBinaryConstraint(vCleanRobotBringsMat, false) }, null),
					new HPPAction("PutAwayPlateMat", new HPPConstraint[]{}, null),
					new HPPAction("WashDishesLate", new HPPConstraint[]{ new HPPBinaryConstraint(vCleanWashBeforeDrawers,false) }, myWashPlan),
				}
		);
		
		HPPlan myCleanPlan = new HPPlan(PlanType.seq,
				new HPPVariable[]{
				},
				new HPPAction[]{
					new HPPAction("Kitchen", new HPPConstraint[]{}, myKitchenCleanPlan),
					new HPPAction("Rest", new HPPConstraint[]{}, myRestCleanPlan),
				}
		);
		
		HPPlan myRelaxStationPlan = new HPPlan(PlanType.set, 
				new HPPVariable[]{
					vRelaxationPosition,
				},
				new HPPAction[]{					
					new HPPAction("SitOnCouch", new HPPConstraint[]{ new HPPConstraint(vRelaxationPosition, RelaxationPosition.sit.toString()) }, null),
					new HPPAction("LieOnCouch", new HPPConstraint[]{ new HPPConstraint(vRelaxationPosition, RelaxationPosition.lie.toString()) }, null),
				}
		);
		
		HPPlan myRelaxActivityPlan = new HPPlan(PlanType.set, 
				new HPPVariable[]{										
				},
				new HPPAction[]{					
					new HPPAction("Read", new HPPConstraint[]{ new HPPConstraint(vRelaxationActivity, RelaxationActivity.read.toString()) }, null),
					new HPPAction("WatchTV", new HPPConstraint[]{ new HPPConstraint(vRelaxationActivity, RelaxationActivity.tv.toString()) }, null),
				}
		);
		
		HPPlan myRelaxPlan = new HPPlan(PlanType.seq, 
				new HPPVariable[]{					
					vRelaxationToDo,
					vRelaxationActivity,
				},
				new HPPAction[]{
					new HPPAction("GetPeriodicalFromDrawers", new HPPConstraint[]{ new HPPConstraint(vRelaxationActivity, RelaxationActivity.read.toString()) }, null),
					new HPPAction("RelaxOnCouch", new HPPConstraint[]{}, myRelaxStationPlan),
					new HPPAction("RelaxToDo", new HPPConstraint[]{ new HPPBinaryConstraint(vRelaxationToDo, true ) }, null),
					new HPPAction("RelaxActivity", new HPPConstraint[]{}, myRelaxActivityPlan),					
				}
		);
		
		HPPlan myPlan = new HPPlan(PlanType.seq, 
				new HPPVariable[]{
					vDayOfWeek,
					vTimeOfDay,
					vHunger,
					vWork,
					vConsumeMeal,
					vDoWork,
					vCleanHouse,
					vSeed
				},
				new HPPAction[]{
					new HPPAction("ConsumeMeal", new HPPConstraint[]{ new HPPBinaryConstraint(vConsumeMeal, true) }, myMealPlan),
					new HPPAction("DoWork", new HPPConstraint[]{ new HPPBinaryConstraint(vDoWork, true) }, myWorkPlan),
					new HPPAction("CleanHouse", new HPPConstraint[]{ new HPPBinaryConstraint(vCleanHouse, true) }, myCleanPlan),
					new HPPAction("Relax", new HPPConstraint[]{}, myRelaxPlan),
				}
		);
		
		/////////////////////////////////////////
		// Generate a script from the HPPlan
		/////////////////////////////////////////
		
		HPPScript script = new HPPScript(myPlan, agentLocation, seedValue);
		
		// Un-comment in order to visualize the plan structure
		// System.out.println(myPlan.toGraphviz());
		
		/////////////////////////////////////////
		// Output KB + script contents
		/////////////////////////////////////////
		
		System.out.println("Resulting KB:");
		{
			for (String v : script.getKB().getVariables()) {
				System.out.print( "  " + v + "=" );
				String val = "";
				Iterator<HPPGivenVariable> it = script.getKB().getVariableValues(v);
				while (it.hasNext()) {
					if (!val.isEmpty()) {
						val = val + ",";
					}
					val = val + ((HPPGivenVariable) it.next()).getValue();
				}
				System.out.println(val);
			}
		}		
		System.out.println();
		
		System.out.println("Resulting Script:");
		{
			System.out.print("  start");
			
			Iterator<String> actionIt = script.getScript();
			while (actionIt.hasNext()) {
				System.out.print(" -> " + (String) actionIt.next());
			}
			System.out.println();
		}
	}
}
