/*
 * Created on Oct 28, 2003
 *
 */
package kareltherobot;
import java.util.Hashtable;

public interface Strategy extends Directions
{
	public static final NullAction NULL = new NullAction();
	
	public interface DelayedExecutor // Robot classes can implement this
	{	public void execute(Action anAction, Hashtable values);
	}
	
	public interface SimpleNew
	{	public Action newInstance(Action decorated);
	}
	
	public interface IfWhileNew
	{	public Action newInstance(Action decorated, Predicate predicate);
	}
	
	public interface IfElseNew
	{	public Action newInstance(Action decorated, Predicate predicate, Action elseAction);
	}
		
	public interface Action extends Directions
	{	public void doIt(ur_Robot karel, Hashtable data);
	}
	
	public interface Predicate extends Action
	{	public boolean query(Robot karel, Hashtable data);
		public String predicateString();
	}
	
	public static class NullAction implements Predicate, SimpleNew
	{	
		public boolean query(Robot karel, Hashtable data)
		{	return false;
		}
		
		public void doIt(ur_Robot karel, Hashtable data)
		{	// nothing
		}
		
		public String predicateString()
		{ return "";
		}
		
		public String toString()
		{	return "";
		}
		
		public Action newInstance(Action decorated)
		{	return this;
		}
		
		private NullAction()
		{
		}
	}
	
	public static class MoveAction implements Action, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			karel.move();
		}
		
		public MoveAction(Action decorated)
		{	this.decorated = decorated;
		}
		
		public Action newInstance(Action decorated)
		{	return new MoveAction(decorated);
		}
		private Action decorated;
		
		public String toString()
		{	return decorated.toString() + "	karel.move();\n";
		}
	}

	public static class TurnLeftAction implements Action, SimpleNew 
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			karel.turnLeft();
		}
		
		public TurnLeftAction(Action decorated)
		{	this.decorated = decorated;
		}
		
		public Action newInstance(Action decorated)
		{	return new TurnLeftAction(decorated);
		}
		private Action decorated;

		public String toString()
		{	return decorated.toString() + "	karel.turnLeft();\n";
		}
	}
	
	public static class TurnOffAction implements Action
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			karel.turnOff();
		}
		
		public TurnOffAction(Action decorated)
		{	this.decorated = decorated;
		}
		
		public Action newInstance(Action decorated)
		{	return new TurnOffAction(decorated);
		}
		private Action decorated;

		public String toString()
		{	return decorated.toString() + "	karel.turnOff();\n";
		}
	}
	
	public static class PickBeeperAction implements Action, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			karel.pickBeeper();
		}
		
		public PickBeeperAction(Action decorated)
		{	this.decorated = decorated;
		}
		
		public Action newInstance(Action decorated)
		{	return new PickBeeperAction(decorated);
		}
		private Action decorated;

		public String toString()
		{	return decorated.toString() + "	karel.pickBeeper();\n";
		}
	}

	public static class PutBeeperAction implements Action, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			karel.putBeeper();
		}
		
		public PutBeeperAction(Action decorated)
		{	this.decorated = decorated;
		}
		
		public Action newInstance(Action decorated)
		{	return new PutBeeperAction(decorated);
		}
		private Action decorated;

		public String toString()
		{	return decorated.toString() + "	karel.putBeeper();\n";
		}
	}
	
	public static class IfBlock implements Action, IfWhileNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			if(aPredicate.query((Robot)karel, data))
			{	aPredicate.doIt(karel, data);
			}
		}
		
		public IfBlock(Action decorated, Predicate predicate)
		{	this.decorated = decorated;
			this.aPredicate = predicate;
		}
		
		public Action newInstance(Action decorated, Predicate predicate) 
		{	return new IfBlock(decorated, predicate);
		}
		
		private Action decorated;
		private Predicate aPredicate;

		public String toString()
		{	return decorated.toString() + "	if("+aPredicate.predicateString()
			+ ")\n	{\n" +aPredicate.toString()
			+"	}";
		}
	}
	
	public static class IfElseBlock implements Action, IfElseNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			if(aPredicate.query((Robot)karel, data))
			{	aPredicate.doIt(karel, data);
			}
			else
			{	elseAction.doIt(karel,data);
			}
		}
		
		public IfElseBlock(Action decorated, Predicate predicate, Action elseAction)
		{	this.decorated = decorated;
			this.aPredicate = predicate;
			this.elseAction = elseAction;
		}
		
		public Action newInstance(Action decorated, Predicate predicate, Action strategy)
		{	return new IfElseBlock(decorated, predicate, strategy);
		}
		
		private Action decorated;
		private Predicate aPredicate;
		private Action elseAction;

		public String toString()
		{	return decorated.toString() + "	if("+aPredicate.predicateString()
			+ ")\n	{\n" +aPredicate.toString()
			+"	}\n	else\n	{\n	"+elseAction.toString()+"	}";
		}
	}
	
	public static class WhileBlock implements Action, IfWhileNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	decorated.doIt(karel, data);
			while(aPredicate.query((Robot)karel, data))
			{	aPredicate.doIt(karel, data);
			}
		}
		
		public WhileBlock(Action decorated, Predicate predicate)
		{	this.decorated = decorated;
			this.aPredicate = predicate;
		}
		
		public Action newInstance(Action decorated, Predicate predicate) 
		{	return new WhileBlock(decorated, predicate);
		}
		
		private Action decorated;
		private Predicate aPredicate;

		public String toString()
		{	return decorated.toString() + "	while("+ aPredicate.predicateString() 
			+ ")\n	{\n" +aPredicate.toString()
			+"	}";
		}
	}
	
	public static class NextToABeeperPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.nextToABeeper();
		}
		
		public NextToABeeperPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new NextToABeeperPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.nextToABeeper()";
		}
	}
	
	public static class FrontIsClearPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.frontIsClear();
		}
		
		public FrontIsClearPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new FrontIsClearPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.frontIsClear()";
		}
	}
	
	public static class NextToARobotPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.nextToARobot();
		}
		
		public NextToARobotPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new NextToARobotPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.nextToARobot()";
		}
	}

	public static class AnyBeepersInBeeperBagPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.anyBeepersInBeeperBag();
		}
		
		public AnyBeepersInBeeperBagPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new AnyBeepersInBeeperBagPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.anyBeepersInBeeperBag()";
		}
	}

	public static class FacingNorthPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.facingNorth();
		}
		
		public FacingNorthPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new FacingNorthPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.facingNorth()";
		}
	}

	public static class FacingEastPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.facingEast();
		}
		
		public FacingEastPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new FacingEastPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.facingEast()";
		}
	}

	public static class FacingWestPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.facingWest();
		}
		
		public FacingWestPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new FacingWestPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.facingWest()";
		}
	}

	public static class FacingSouthPredicate implements Predicate, SimpleNew
	{	public void doIt(ur_Robot karel, Hashtable data)
		{	aStrategy.doIt(karel, data);
		}
		
		public boolean query(Robot karel, Hashtable data)
		{	return karel.facingSouth();
		}
		
		public FacingSouthPredicate(Action strategy)
		{	this.aStrategy = strategy;
		}
		
		public Action newInstance(Action action)
		{	return new FacingSouthPredicate(action);
		}
		
		private Action aStrategy;
		
		public String toString()
		{	return aStrategy.toString();
		}
		
		public String predicateString()
		{	return "karel.facingSouth()";
		}
	}

	public static final class Factory
	{	private static Hashtable storage = new Hashtable();
		private Factory()
		{
		}
		public static final Factory singleton = new Factory();
		
		public static final Factory asObject()
		{	return singleton;
		}
		public static Action get(String key, Action previous, Predicate predicate, Action elseAction)
		{	Action proto = (Action)storage.get(key);
			if( proto == null) return NULL;
			if(previous == null)previous = NULL;
			if(predicate == null)predicate = NULL;
			if(elseAction == null)elseAction = NULL;
			if(proto instanceof IfElseNew)
				return ((IfElseNew)proto).newInstance(previous,predicate,elseAction);
			if(proto instanceof IfWhileNew )
				return ((IfWhileNew)proto).newInstance(previous,predicate);
			return ((SimpleNew)proto).newInstance(previous);
		}
		
		static // Register the prototypes in the factory
		{	storage.put("move", new MoveAction(null));
			storage.put("turnLeft", new TurnLeftAction(null));
			storage.put("turnOff", new TurnOffAction(null));
			storage.put("pickBeeper", new PickBeeperAction(null));
			storage.put("putBeeper", new PutBeeperAction(null));
			storage.put("nextToABeeper", new NextToABeeperPredicate(null));
			storage.put("anyBeepersInBeeperBag",new AnyBeepersInBeeperBagPredicate(null));
			storage.put("nextToARobot", new NextToARobotPredicate(null));
			storage.put("frontIsClear", new FrontIsClearPredicate(null));
			storage.put("facingNorth", new FacingNorthPredicate(null));
			storage.put("facingEast", new FacingEastPredicate(null));
			storage.put("facingSouth", new FacingSouthPredicate(null));
			storage.put("facingWest", new FacingWestPredicate(null));
			
			storage.put("while", new WhileBlock(null, null));
			storage.put("if", new IfBlock(null,null));
			storage.put("ifElse",new IfElseBlock(null, null, null));
			
		}
	}
}

