package Rectangles;

import ui.*;
import Rectangles.*;
import java.util.TreeSet;
import java.util.Random;
import java.util.Iterator;

public class TracebackRobot extends Robot implements IFCConstants {
	// slight traceback strategy switches
	//private static final boolean TRACEBACK_FROM_ENEMY_CURR_POS = true; 
		// if true, trackback from closest point along trail

	// values for state machine
	private static final int TARGET_ACQUISITION = 1;
	private static final int ENROUTE_TO_TARGET = 2;
	private static final int AT_TARGET = 3;
	private static final int TRACING_BACK = 4;
	private static final int BREAK_IN_TRAIL = 5;

	private Rectangles _rectangles;
	private int _state;
	private EnemyRobot _enemy;
	private Point _closestPoint;
	private Segment _lastSegment;
	private int _lastSegmentIndex;

	public TracebackRobot( int x, int y, int playerIndex, 
		Rectangles rectangles ) throws Exception {
		super( x, y );
		setPlayerIndex( playerIndex );
		_rectangles = rectangles;
		reset();
	}

	public void reset() throws Exception {
		_state = TARGET_ACQUISITION;
		_enemy = null;
		_closestPoint = null;
		_lastSegment = null;
		_lastSegmentIndex = 0;
	}

	public char move( Trail[][] enemyRobotTrails, boolean[][] targetedOpponents ) throws Exception {
		switch( _state ) {
			case TARGET_ACQUISITION:
				//System.out.println( "TARGET_ACQUISITION" );
				_enemy = _acquireTarget( enemyRobotTrails, targetedOpponents );

				if( _enemy == null ) {
					System.out.println( "robot[" + ID() + "] cannot find a target" );
					return( _CSTAY );
				}

				System.out.println( "robot[" + ID() + "] at [" + xpos() + ", " + ypos() + 
					"] targetting robots[" + _enemy.playerIndex() + "][" + _enemy.ID() + 
					"] at [" + _enemy.xpos() + ", " + _enemy.ypos() + "] with score " + 
					_enemy.totalScore() 
				);

				_lastSegment = _enemy.getTrail().closestSegment( xpos(), ypos(), _rectangles );
				_lastSegmentIndex = _enemy.getTrail().search( _lastSegment );
				_closestPoint = _lastSegment.closestPoint( xpos(), ypos() );
				/*
				_lastSegment = ( Segment )( _enemy.getTrail().get( 0 ) );
				_lastSegmentIndex = 0;
				_closestPoint = _lastSegment.end();

				Segment closestSegment = _enemy.getTrail().closestSegment( xpos(), ypos(), _rectangles );
				int closestSegmentIndex = _enemy.getTrail().search( _lastSegment );
				Point closestPointOnSeg = _lastSegment.closestPoint( xpos(), ypos() );

				if( closestPointOnSeg.distanceTo( xpos(), ypos() ) < 
					_closestPoint.distanceTo( xpos(), ypos() ) ) {
					_lastSegment = closestSegment;
					_lastSegmentIndex = closestSegmentIndex;
					_closestPoint = closestPointOnSeg;
				}
				*/

				_state = ENROUTE_TO_TARGET;
			case ENROUTE_TO_TARGET:
				//System.out.println( "ENROUTE_TO_TARGET" );
				// perhaps, reset() if target point changes color?

				if( Math.abs( _closestPoint.xpos() - xpos() ) == 1 && _closestPoint.ypos() == ypos() ) {
					_state = AT_TARGET;

					if( _closestPoint.xpos() - xpos() < 0 ) {
						return( _CWEST );
					}
					
					return( _CEAST );
				}
				else if( Math.abs( _closestPoint.ypos() - ypos() ) == 1 && xpos() == _closestPoint.xpos() ) {
					_state = AT_TARGET;

					if( _closestPoint.ypos() - ypos() < 0 ) {
						return( _CNORTH );
					}
					
					return( _CSOUTH );
				}

				if( _closestPoint.xpos() != xpos() || _closestPoint.ypos() != ypos() ) {
					// move towards the target; randomly goes vertically/horizontally 
					// until it reaches target
					char[] moveVector = new char[2];
					moveVector[0] = _CSTAY; 
					moveVector[1] = _CSTAY;

					if( _closestPoint.xpos() < xpos() ) {
						moveVector[0] = _CWEST;
					}
					else if( _closestPoint.xpos() > xpos() ) {
						moveVector[0] = _CEAST;
					}

					if( _closestPoint.ypos() < ypos() ) {
						moveVector[1] = _CNORTH;
					}
					else if( _closestPoint.ypos() > ypos() ) {
						moveVector[1] = _CSOUTH;
					}

					int dim = ( new Random() ).nextInt( 2 );

					if( moveVector[dim] == _CSTAY ) {
						return( moveVector[dim ^ 1] );
					}

					return( moveVector[dim] );
				}
			case AT_TARGET:
				//System.out.println( "AT_TARGET" );
				_state = TRACING_BACK;
			case TRACING_BACK:
				//System.out.println( "TRACING_BACK" );
				Segment currentSegment = null;
				
				try {
					// if reached the start of the segment, advance to the next segment
					if( _lastSegment.start().xpos() == xpos() && 
						_lastSegment.start().ypos() == ypos() ) {
						_lastSegment = ( Segment )( _enemy.getTrail().get( ++_lastSegmentIndex ) );
					}

					int occupied[][] = _rectangles.colors();
					boolean conquered[][] = _rectangles.filled();

					// go the opposite direction of the segment along the trail;
					// first, if the next space along the trail has changed color,
					// reset and find another target; else continue along trail
					switch( _lastSegment.direction() ) {
						case _CEAST:
							if( occupied[xpos() + 1][ypos()] != 
								_enemy.getTrail().playerIndex() || 
								conquered[xpos() + 1][ypos()] ) {
								break;
							}

							return( _CWEST );
						case _CWEST:
							if( occupied[xpos() - 1][ypos()] != 
								_enemy.getTrail().playerIndex() || 
								conquered[xpos() - 1][ypos()] ) {
								break;
							}

							return( _CEAST );
						case _CNORTH:
							if( occupied[xpos()][ypos() - 1] != 
								_enemy.getTrail().playerIndex() || 
								conquered[xpos()][ypos() - 1] ) {
								break;
							}

							return( _CSOUTH );
						case _CSOUTH:
							if( occupied[xpos()][ypos() + 1] != 
								_enemy.getTrail().playerIndex() || 
								conquered[xpos()][ypos() + 1] ) {
								break;
							}

							return( _CNORTH );
						default:
					};
				}
				catch( ArrayIndexOutOfBoundsException e ) {
					// do nothing; just reset in the next case
					//e.printStackTrace();
				}
			case BREAK_IN_TRAIL:
				//System.out.println( "BREAK_IN_TRAIL" );
				targetedOpponents[_enemy.playerIndex()][_enemy.ID()] = false;

				System.out.println( "robot[" + ID() + "] at [" + xpos() + ", " + ypos() + 
					"] _DONE_ targetting robots[" + _enemy.playerIndex() + "][" + _enemy.ID() + 
					"] at [" + _enemy.xpos() + ", " + _enemy.ypos() + "] with score " + 
					_enemy.totalScore() 
				);

				reset();
				return( _CSTAY );
			default:
				break;
		};

		return( _CSTAY );
	}

	/*
	private boolean _adjacentSquareIsFilled( int x, int y ) {
		
	}
	*/

	// ranks enemy robots by combination of proximity and player's score
	private EnemyRobot _acquireTarget( Trail[][] enemyRobotTrails, boolean[][] targetedOpponents ) 
		throws Exception {
		Robot[][] robots = _rectangles.allRobots();
		TreeSet enemyRobotPriorities = new TreeSet(); // will sort by using EnemyRobot.compareTo()

		for( int i = 0; i < robots.length; i++ ) {
			if( i == playerIndex() ) {
				continue;
			}
			
			for( int j = 0; j < robots[i].length; j++ ) {
				//System.out.println( "adding robot " + j + " for player " + i );
				// creates a new EnemyRobot object w/ the last know Trail

				if( !targetedOpponents[i][j] ) {
					EnemyRobot target = new EnemyRobot( robots[i][j], 
						( Trail )( enemyRobotTrails[i][j].clone() ), 
						xpos(), ypos(), _rectangles 
					);
					enemyRobotPriorities.add( target );
					/*
					System.out.println( "enemy robot[" + target.playerIndex() + "][" +
						target.ID() + "] has total score of " + target.totalScore() + 
						"(distance = " + target.distanceScore() + ", points = " + 
						target.pointsScore() + ")"
					);
					*/
				}
			}
		}

		/*
		Iterator rankedOpponents = enemyRobotPriorities.iterator();

		while( rankedOpponents.hasNext() ) {
			EnemyRobot opponent = ( EnemyRobot )( rankedOpponents.next() );

			if( !targetedOpponents[opponent.playerIndex()][opponent.ID()] ) {
				targetedOpponents[opponent.playerIndex()][opponent.ID()] = true;
				return( opponent );
			}
		}

		return( null );
		*/

		EnemyRobot opponent = ( EnemyRobot )( enemyRobotPriorities.first() );
		targetedOpponents[opponent.playerIndex()][opponent.ID()] = true;
		return( opponent );
	}
}
