package robocup.lab.intelligent;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

import org.apache.log4j.Logger;

import robocup.lab.Brain;
import robocup.lab.Literal;

import atan.model.ActionsPlayer;
import atan.model.ControllerPlayer;
import atan.model.enums.Errors;
import atan.model.enums.Flag;
import atan.model.enums.Line;
import atan.model.enums.Ok;
import atan.model.enums.PlayMode;
import atan.model.enums.RefereeMessage;
import atan.model.enums.ViewAngle;
import atan.model.enums.ViewQuality;
import atan.model.enums.Warning;

/**
 * The Class IntelligentController.
 */
public class IntelligentController implements ControllerPlayer {
	
    /**
     * The log.
     */
    private static Logger 					log = Logger.getLogger(IntelligentController.class);
    
    /**
     * The controller brain.
     */
    private static Brain 					brain = new Brain();
    
    /**
     * The cycle sensors.
     */
    private Set<Literal>					cycleSensors = new HashSet<Literal>();
    
    /**
     * The lock. Used to avoid sensing before the kick-off playmode.
     */
    private boolean 						lock = true;
    
    /**
     * The random.
     */
    private Random 							random = null;
    
    /**
     * The count.
     */
    private static int 						count = 0;
    
    /**
     * The player.
     */
    private ActionsPlayer 					player;
    
    /**
     * The see ball.
     */
    private boolean							seeBall;
    
    /**
     * The see own goal.
     */
    private boolean							seeOwnGoal;
    
    /**
     * The see other goal.
     */
    private boolean							seeOtherGoal;
    
    /**
     * The see other player.
     */
    private boolean							seeOtherPlayer;
    
    /**
     * The see other flag penalty.
     */
    private boolean 						seeOtherFlagPenalty;
    
    /**
     * The distance ball.
     */
    private double							distanceBall;
    
    /**
     * The direction ball.
     */
    private double 							directionBall;
    
    /**
     * The direction own goal.
     */
    private double        					directionOwnGoal;
    
    /**
     * The distance own goal.
     */
    private double        					distanceOwnGoal;
    
    /**
     * The direction other goal.
     */
    private double        					directionOtherGoal;
    
    /**
     * The distance other goal.
     */
    private double        					distanceOtherGoal;
    
    /**
     * The distance other player.
     */
    private double 							distanceOtherPlayer;
    
    /**
     * The direction other player.
     */
    private double							directionOtherPlayer;
    
    /**
     * The distance other flag penalty.
     */
    private double 							distanceOtherFlagPenalty;
    
    /**
     * The direction other flag penalty.
     */
    private double							directionOtherFlagPenalty;
    
    /**
     * The last action.
     */
    private String							lastAction = "";
    
    /**
     * Instantiates a new intelligent controller.
     */
    public IntelligentController() {
        count++;
        random = new Random(System.currentTimeMillis() + count);
        initState();
    }
    
    
    
    /**
     * Inits the player sensing state.
     */
    private void initState() {
        seeBall = false;
        seeOwnGoal = false;
        seeOtherGoal = false;
        seeOtherPlayer = false;
        seeOtherFlagPenalty = false;
        distanceBall = 0;
        directionBall = 0;
        directionOwnGoal = 0;
        distanceOwnGoal = 0;
        directionOtherGoal = 0;
        distanceOtherGoal = 0;
        distanceOtherPlayer = 0;
        directionOtherPlayer = 0;
        distanceOtherFlagPenalty = 0;
        directionOtherFlagPenalty = 0;
    }

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#getPlayer()
	 */
	@Override
	public ActionsPlayer getPlayer() {
		return player;
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#getType()
	 */
	@Override
	public String getType() {
		return null;
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoHearError(atan.model.enums.Errors)
	 */
	@Override
	public void infoHearError(Errors error) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoHearOk(atan.model.enums.Ok)
	 */
	@Override
	public void infoHearOk(Ok ok) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoHearPlayMode(atan.model.enums.PlayMode)
	 */
	@Override
	public void infoHearPlayMode(PlayMode playMode) {
		
		log.error(playMode);
		
        if (this.getPlayer().getNumber() == 6 && playMode == PlayMode.BEFORE_KICK_OFF) {
	        
            this.pause(1000);
	        
        	this.getPlayer().move(0, -32);
	        
        	this.lock = true;
        } else if (playMode == PlayMode.KICK_OFF_OWN) {
        	this.lock = false;
        }
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoHearPlayer(double, java.lang.String)
	 */
	@Override
	public void infoHearPlayer(double direction, String message) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoHearReferee(atan.model.enums.RefereeMessage)
	 */
	@Override
	public void infoHearReferee(RefereeMessage refereeMessage) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoHearWarning(atan.model.enums.Warning)
	 */
	@Override
	public void infoHearWarning(Warning warning) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeBall(double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeBall(double distance, double direction,
			double distChange, double dirChange, double bodyFacingDirection,
			double headFacingDirection) {
		// TODO Auto-generated method stub
		
		if (!lock) {

			this.seeBall = true;
	        
			this.distanceBall  = distance;
	        this.directionBall = direction;
	        
	        if (distance < 0.7) {
	        	cycleSensors.add(new Literal("can-see-ball"));
	        	cycleSensors.add(new Literal("have-ball"));
	        } else {
	        	cycleSensors.add(new Literal("can-see-ball"));
	        }
		}
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagCenter(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagCenter(Flag flag, double distance, double direction,
			double distChange, double dirChange, double bodyFacingDirection,
			double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagCornerOther(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagCornerOther(Flag flag, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagCornerOwn(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagCornerOwn(Flag flag, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagGoalOther(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagGoalOther(Flag flag, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		
		if (!lock) {
	        if (flag == Flag.CENTER) {
	        	
	        	this.seeOtherGoal = true;
	        	
	    		this.distanceOtherGoal = distance;
	    		this.directionOtherGoal = direction;
	    		
	    		if	(distance < 20) {
	    			cycleSensors.add(new Literal("can-score"));
	    		} else {
	    			cycleSensors.add(new Literal("can-see-goal"));
	    		}
	        }	
		}
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagGoalOwn(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagGoalOwn(Flag flag, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagLeft(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagLeft(Flag flag, double distance, double direction,
			double distChange, double dirChange, double bodyFacingDirection,
			double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagOther(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagOther(Flag flag, double distance, double direction,
			double distChange, double dirChange, double bodyFacingDirection,
			double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagOwn(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagOwn(Flag flag, double distance, double direction,
			double distChange, double dirChange, double bodyFacingDirection,
			double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagPenaltyOther(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagPenaltyOther(Flag flag, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		
		if (!lock) {
			
	        if (flag == Flag.CENTER) {
			
	        	this.seeOtherFlagPenalty = true;
	        	
	    		this.distanceOtherFlagPenalty = distance;
	    		this.directionOtherFlagPenalty = direction;
	    		
	    		cycleSensors.add(new Literal("can-see-area"));
	    	
	        }
				
		}

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagPenaltyOwn(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagPenaltyOwn(Flag flag, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		// TODO Auto-generated method stub
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeFlagRight(atan.model.enums.Flag, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeFlagRight(Flag flag, double distance, double direction,
			double distChange, double dirChange, double bodyFacingDirection,
			double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeeLine(atan.model.enums.Line, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeeLine(Line line, double distance, double direction,
			double distChange, double dirChange, double bodyFacingDirection,
			double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeePlayerOther(int, boolean, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeePlayerOther(int number, boolean goalie, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		
		if (!lock) {
			if (distance < 10 && Math.abs(direction) < 30) {
				
				seeOtherPlayer = true;
				
				distanceOtherPlayer = distance;
				directionOtherPlayer = direction;
				
			}	
		}
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSeePlayerOwn(int, boolean, double, double, double, double, double, double)
	 */
	@Override
	public void infoSeePlayerOwn(int number, boolean goalie, double distance,
			double direction, double distChange, double dirChange,
			double bodyFacingDirection, double headFacingDirection) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#infoSenseBody(atan.model.enums.ViewQuality, atan.model.enums.ViewAngle, double, double, double, double, double, double, int, int, int, int, int, int, int, int)
	 */
	@Override
	public void infoSenseBody(ViewQuality viewQuality, ViewAngle viewAngle,
			double stamina, double unknown, double effort, double speedAmount,
			double speedDirection, double headAngle, int kickCount,
			int dashCount, int turnCount, int sayCount, int turnNeckCount,
			int catchCount, int moveCount, int changeViewCount) {
		// TODO Auto-generated method stub

	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#postInfo()
	 */
	@Override
	public void postInfo() {
		
		// TODO Auto-generated method stub
		
		if (!lock) {
			
			cycleSensors.add(new Literal("clear-field"));
			
			if (seeOtherPlayer) {
				cycleSensors.remove(new Literal("clear-field"));
			}
			
	    	if (!cycleSensors.isEmpty()) {
	            Iterator<Literal> iter = cycleSensors.iterator();
	            while (iter.hasNext()) {
	            	brain.addSense(iter.next());
	            }
	        }
	    	
	    	brain.tryReplan();
			
			String currentAction = brain.readAction();
			
			if (currentAction != null && !currentAction.equals(lastAction))
				log.error("ACTION " + currentAction);
			
			lastAction = currentAction;
			
			if (currentAction != null){
        	
	        		
				if (currentAction.equals("find-ball")) {
					if (!seeBall) {
						getPlayer().turn(this.randomTurnValue());
					} else {
						brain.consumeAction();
					}
						
				} 

				else if (currentAction.equals("run-to-ball")) {

					if (directionBall != 0) {
						turnTowardBall();	
					} else if (distanceBall < 5) {
						getPlayer().dash(70);
					} else if (distanceBall > 0.7) {
						getPlayer().dash(100);
					} else if (distanceBall <= 0.7) {
						brain.consumeAction();
					}

				}

				else if (currentAction.equals("find-goal")) {
					if (!seeOtherGoal) {
						if (distanceBall <= 0.7) {
							int d = randomDirection(90);
							getPlayer().kick(5, d);
							getPlayer().turn(d);
						} else {
							turnTowardBall();
						}
					} else {
						brain.consumeAction();
					}
				}

				else if (currentAction.equals("find-area")) {
					if (!seeOtherFlagPenalty) {
						if (distanceBall <= 0.7) {
							int d = randomDirection(90);
							getPlayer().kick(5, d);
							getPlayer().turn(d);
						} else {
							if (directionBall != 0) {
								turnTowardBall();	
							} else if (distanceBall < 5) {
								getPlayer().dash(70);
							} else if (distanceBall > 0.7) {
								getPlayer().dash(100);
							}
						}
					} else {
						brain.consumeAction();
					}
				}

				else if (currentAction.equals("run-to-goal")) {
					
					if (seeOtherGoal && distanceOtherGoal > 20) {
						if (distanceBall <= 0.7){
							getPlayer().kick(10, directionOtherGoal);
						} else {
							if (directionBall != 0) {
								turnTowardBall();	
							} else if (distanceBall < 5) {
								getPlayer().dash(70);
							} else if (distanceBall > 0.7) {
								getPlayer().dash(100);	
							}
						}
					} else {
						brain.consumeAction();
					}
				}   

				else if (currentAction.equals("score-goal")) {
					if (distanceBall < 0.7) {
						getPlayer().kick(100, directionOtherGoal);
						brain.consumeAction();
					} else {
						if (directionBall != 0) {
							turnTowardBall();	
						} else if (distanceBall < 5) {
							getPlayer().dash(70);
						} else if (distanceBall > 0.7) {
							getPlayer().dash(100);	
						}
					}
				}

				else if (currentAction.equals("dribble")) {
					if (seeOtherPlayer) {
						if (distanceBall <= 0.7) {
							int d = (int) (Math.abs(directionOtherPlayer) + 20);
							log.error("PLAYER DIR " + directionOtherPlayer);
							log.error("PLAYER DIST " + distanceOtherPlayer);
							if (directionOtherPlayer > 0) {
								d = -d;
							}
							getPlayer().kick(20, d);
							getPlayer().turn(d);
						} else {
							turnTowardBall();
						}
					} else {
						brain.consumeAction();
					}
				}


			} else {
				brain.restart();
			}
	        
	        //initialization
	        
	        initState();
	
		}
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#preInfo()
	 */
	@Override
	public void preInfo() {
		// TODO Auto-generated method stub
        
		if (!lock) {
	        
			brain.resetSensors();
			
			cycleSensors.clear();
			
		}                         
		
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#setPlayer(atan.model.ActionsPlayer)
	 */
	@Override
	public void setPlayer(ActionsPlayer p) {
		this.player = p;
	}

	/* (non-Javadoc)
	 * @see atan.model.ControllerPlayer#setType(java.lang.String)
	 */
	@Override
	public void setType(String newType) {
		// TODO Auto-generated method stub

	}
	
    /**
     * Random dash value slow.
     *
     * @return the int
     */
    private int randomDashValueSlow() {
        return -10 + (int) (random.nextDouble() * 50.0);
    }
    
    /**
     * Random turn value.
     *
     * @return the double
     */
    private double randomTurnValue() {
        return -180 + (random.nextDouble() * 360);
    }
    
    /**
     * Random dash value fast.
     *
     * @return the int
     */
    private int randomDashValueFast() {
        return 30 + (int) (random.nextDouble() * 100.0);
    }
    
    /**
     * Random kick direction value.
     *
     * @return the int
     */
    private int randomKickDirectionValue() {
        return -45 + (int) (random.nextDouble() * 90.0);
    }
    
    /**
     * Random x position.
     *
     * @return the int
     */
    private int randomXPosition() {
    	return random.nextInt(55) - 54;
    }
    
    /**
     * Random y position.
     *
     * @return the int
     */
    private int randomYPosition() {
    	return random.nextInt(65) - 32; 
    }
    
    /**
     * Random direction.
     *
     * @param direction the direction
     * @return the int
     */
    private int randomDirection(int direction) {
    	if (random.nextBoolean())
    		return direction;
    	return -direction;
    }
    
    /**
     * Turn toward ball.
     */
    private void turnTowardBall() {
        getPlayer().turn(directionBall);
    }
    
    /**
     * Turn toward other goal.
     */
    private void turnTowardOtherGoal() {
        getPlayer().turn(directionOtherGoal);
    }
    
    /**
     * Pause.
     *
     * @param ms the ms
     */
    private synchronized void pause(int ms) {
    	try {
    		this.wait(ms);
    	} catch (InterruptedException ex) {}
    }
}
