/*
 * Group9: Peter, Madhuri, Jeff
 */

package Olympics.g9;

import Olympics.*;
import java.awt.Color;
import java.util.Random;



public class Group9PlayerJ implements IFCPlayer {

	Olympics _olympics;
	RiderInfo[][] all;
	RiderInfo[] us;
	
	final int START = 0, MERGE = 1, SEPARATED = 2, CONVOY = 3, LAST = 4;
	int state = START;
	double targetVelocity, formerVelocity;
	Random rand;
	
	
	////////////////////////////////////////////////////////////
	//required methods
	
	public void register(Olympics __olympics) throws Exception {
		_olympics = __olympics;
		rand = new Random();
	}
	

	public String name() throws Exception {
		return "Smart Player";
	}

	
	public Color color() throws Exception {
		return new Color(0.3f, 0.3f, 0.3f);
	}
	

	public boolean interactive() throws Exception {
		return false;
	}
	
	
	////////////////////////////////////////////////////////////
	//helper methods
	
	private double farthestDistance() throws Exception {
		double minPosition = 9999999;
		for (int i = 0; i < us.length; i++) {
			if (minPosition > us[i].position() && 
					us[i].energy() > 0) {
				minPosition = us[i].position();
			}
		}
		return _olympics.D() - minPosition;
	}
	

	private double nearestDistance() throws Exception {
		double maxPosition = -1;
		for (int i = 0; i < us.length; i++) {
			if (maxPosition < us[i].position() && 
					us[i].energy() > 0) {
				maxPosition = us[i].position();
			}
		}
		return _olympics.D() - maxPosition;
	}

	
	private double minEnergy() {
		double minEnergy = 99999999;
		double tmpMinPos;
		for (int b = 0; b < us.length; b++) {
			if (us[b].energy() < minEnergy && us[b].energy() > 0) {
				minEnergy = us[b].energy();
			}
		}
		return minEnergy;
	}
	

	private int livingRiders() {
		int riders = 0;
		for (int b = 0; b < us.length; b++) {
			if (us[b].energy() > 0) {
				riders++;
			}
		}
		return riders;
	}
	

	private int midpoint() {
		int minLane = 999999, maxLane = -1;
		
		for (int a = 0; a < us.length; a++) {
			if (us[a].energy() <= 0) {
				continue; //this rider is dead
			}
			if (us[a].lane() > maxLane) {
				maxLane = us[a].lane();
			}
			if (us[a].lane() < minLane) {
				minLane = us[a].lane();
			}
		}

		return (int)((maxLane + minLane) / 2); //takes the floor
	}
	
	
	private double convoyVelocity() throws Exception {
		double e = minEnergy();
		double d = farthestDistance() + 1;
		double n = livingRiders();
		double E = e * (10.0 / 7) * (1 - Math.pow(.3, n));
		return Math.pow(E / d, 2.0 / 3);
	}
	

	//same as convoy velocity when there is only a single rider
	private double mergeVelocity() throws Exception {
		double e = minEnergy();
		double d = farthestDistance() + 1;
		return Math.pow(e / d, 2.0 / 3);
	}
	
	/*	
	//is this rider currently being drafted by a rider
	//from a team other than ours?
	private boolean beingDrafted(RiderInfo suspect) throws Exception {
		RiderInfo[][] all = _olympics.TeamStatus();
		for (int t = 0; t < all.length; t++) {
			if (t == _olympics.indexOf(this))
				continue;
			for (int r = 0; r < us.length; r++) {
				RiderInfo cur = all[t][r]; 
				if (suspect.position() > cur.position() &&
						suspect.position() < cur.position() + 4)
					return true;
			}
		}
		return false;
	}
	
	
	//monitors the riders of other teams to determine if
	//any of them would be good to follow. a good leader
	//is near the front, not already being drafted, and
	//does not change lanes very often.
	private void follow() throws Exception { //returns Move to catch this rider
		for (int t = 0; t < all.length; t++) {
			if (t == _olympics.indexOf(this))
				continue;
			for (int r = 0; r < us.length; r++) {
				RiderInfo cur = all[t][r]; 
				if (cur.energy() <= 0 || 
					cur.speed() > targetVelocity * 1.3 ||
					cur.speed() < targetVelocity * .8 ||
					beingDrafted(cur))
					continue;
				
			}
		}
	}
	*/
	
	//is the proposed lane change blocked for any other the
	//riders in our convoy?
	private boolean probablyBlocked(int direction) throws Exception {
		if (direction == 0)
			return false;
		double min = 99999, max = -1;
		int targetLane = 0;
		for (int u = 0; u < us.length; u++) {
			if (us[u].energy() > 0) {
				targetLane = us[u].lane() + direction;
				if (us[u].position() > max)
					max = us[u].position();
				if (us[u].position() < min)
					min = us[u].position(); 
			}
		}
		for(int t = 0; t < all.length; t++) {
			for(int r = 0; r < us.length; r++) {
				RiderInfo cur = all[t][r];
				if ((cur.lane() == targetLane || 
						cur.lane() == targetLane + direction) && 
					cur.position() < (max - 25) &&
					cur.position() > (min + 25)) {
					return true;
				}
			}
		}
		return false;
	}
	
	
	private int state() throws Exception {
		boolean first = true;
		RiderInfo base = null;
		for (int i = 0; i < us.length; i++) {
			if (us[i].energy() > 0) {
				if (first) {
					base = us[i];
					first = false;
				}
				if (us[i].lane() != base.lane()) {
					return MERGE;
				}
			}
		}
		if (livingRiders() == 1) {
			return LAST;
		} else if (farthestDistance() - nearestDistance() > 
				(2 * livingRiders() - 2)) {
			return SEPARATED;
		}
		return CONVOY;
	}

	
	////////////////////////////////////////////////////////////
	//primary methods
	
	private Move runConvoy() throws Exception {
		double[] accs = new double[us.length];
		int[] lanes = new int[us.length];
		double maxPos = -1;
		int leader = -1;
		
		int laneChange = rand.nextInt(3) - 1;
		if (probablyBlocked(laneChange)) {
			laneChange = 0;
		}
		
		for (int i = 0; i < us.length; i++) {
			lanes[i] = laneChange; //no switching lanes yet while in convoy mode
			accs[i] = 1; //follow the leader as closely as possible
			if (maxPos < us[i].position() && us[i].energy() > 0) {
				maxPos = us[i].position();
				leader = i;
			}
		}
		//the leader controls the convoy's velocity
		accs[leader] = targetVelocity - us[leader].speed();
		
		return new Move(accs, lanes);
	}
	
		
	private Move doMerge() throws Exception {
		double[] accs = new double[us.length];
		int[] lanes = new int[us.length];

		double rtVelocity = targetVelocity + (2 * rand.nextDouble() - 1);
		for (int b = 0; b < us.length; b++) {
	       	accs[b] = rtVelocity - us[b].speed();
			if (us[b].lane() > midpoint()) { //midpoint evolves
				lanes[b] = _CMOVELEFT;
			} else if (us[b].lane() < midpoint()) {
				lanes[b] = _CMOVERIGHT;
			} else {
				for (int i = 0; i < us.length; i++) {
					if ((Math.abs(us[i].lane() - us[b].lane()) == 1)
							&& (us[i].position() > us[b].position() - 2)
							&& (us[i].position() < us[b].position() + 2))
						accs[b] = -1.0;
				}
				lanes[b] = _CSTAYPUT;
			}
		}
        return new Move(accs, lanes);
    }
	

	public Move move() throws Exception {
		all = _olympics.TeamStatus();
		us = _olympics.TeamStatus(_olympics.indexOf(this));
		
		//only is activitated if states are progressing
		if (state < state()) { 
			state = state();
			switch (state) {
				case MERGE:
					targetVelocity = mergeVelocity();
					break;
				case SEPARATED:
					//falls through
				case CONVOY:
					targetVelocity = convoyVelocity();
					break;
				default: 
					//pass
			}
		}

		//this will revert to MERGE, if the convoy is split
		switch (state()) {
			case MERGE: 
				return doMerge();
			case SEPARATED:
				//falls through, intentionally
			case CONVOY: 
				return runConvoy();
			case LAST: //update target velocity each time
				targetVelocity = mergeVelocity();
				return runConvoy();
			default: 
				throw new Exception("invalid state");
		}
	}
}
