package fipm.data.queryfeatures.situation;

import java.util.*;

import de.tum.in.fipm.base.data.Signature;
import de.tum.in.fipm.base.data.feature.QueryFeature;
import de.tum.in.fipm.base.models.util.Constants;
import fipm.data.feature.data.Interval;
import fipm.data.feature.data.IntervalList;

public class ShootingAngleSingle extends QueryFeature {
	private static List<Signature> inputSemantics = new ArrayList<Signature>(1);
	private static List<Signature> outputSemantics = new ArrayList<Signature>(3);
	
	static {
		inputSemantics.add(new Signature("analyzed_player", Signature.Necessity.MUST, "INT", "owl:thing","PID"));
		inputSemantics.add(new Signature("player", Signature.Necessity.MUST, "INT", "owl:thing","PID"));
		inputSemantics.add(new Signature("x_player", Signature.Necessity.MUST, "FLT", "owl:thing", "COX"));
		inputSemantics.add(new Signature("y_player", Signature.Necessity.MUST, "FLT", "owl:thing", "COX"));
		
		outputSemantics.add(new Signature("player", Signature.Necessity.MUST, "INT", "owl:thing", "PID"));
		outputSemantics.add(new Signature("interval_start", Signature.Necessity.MUST, "FLOAT", "owl:thing", "STA"));
		outputSemantics.add(new Signature("interval_end", Signature.Necessity.MUST, "FLOAT", "owl:thing", "STA"));
		outputSemantics.add(new Signature("angle", Signature.Necessity.MUST, "FLOAT", "owl:thing", "STA"));
		outputSemantics.add(new Signature("max_angle", Signature.Necessity.MUST, "FLOAT", "owl:thing", "STA"));
		outputSemantics.add(new Signature("group", Signature.Necessity.MUST, "INT", "owl:thing", "GRP"));
	}
	
	protected static final HashSet<String> NecessaryParameters=new HashSet<String>();
	static {
		NecessaryParameters.add("DEFENDER_RANGE");
	}
	
	{
		parameterBase.setParameter("DEFENDER_RANGE",new Double(1000.0));

	    name="ShootingAngle";
	}
	
	/*
	 * Parameter mit gesetzen Standardwerten
	 */
	private double DEFENDER_RANGE = 1000.0;
	
	private IntervalList calcShootingLine(double[] x, double[] y, int[] id, int player) {
		
		int goal = -1;
		if (id[player] >= 11) {
			goal = 1;
		}
		
		IntervalList goalLine = new IntervalList(new Interval(Constants.GOAL_Y_START, Constants.GOAL_Y_END));
		
		double px = Constants.FIELD_WIDTH / 2 + goal * x[player];	// x-dist to goal
		double py = y[player];
		
		if (px <= 0) {
			if (Constants.GOAL_Y_START < py && py < Constants.GOAL_Y_END) {
				return goalLine;
			} else {
				return new IntervalList();
			}
		}
		
		for (int j = 0; j < id.length; j++) {
			
			if (((id[player] < 11) && (id[j] < 11)) || ((id[player] > 10) && (id[j] > 10))) {
				continue;
			}
			
			double dx = Constants.FIELD_WIDTH / 2 + goal * x[j];
			if (dx >= px || dx < -DEFENDER_RANGE) // Defender behind player or goal
				continue;
			double dy = y[j];
			
			double y0 = py + (dy - py) / (1 - dx / px);	// y-pos at goal
			double ddy;
			if ((id[j] % 11) == 0)
				ddy = 2 * DEFENDER_RANGE * px / (px - dx);
			else
				ddy = DEFENDER_RANGE * px / (px-dx);	// defenderrange at goal
			
			goalLine.subtractInterval(new Interval((float)(y0 - ddy), (float) (y0 + ddy)));
		}
		
		return goalLine;
	}
	
	private double calcAngle(IntervalList goalLine, double dx, double dy, double[] maxangle) {		
		double result = 0.0;
		double maxAngle = 0.0;
		
		Iterator it = goalLine.iterator();
		while (it.hasNext()) {
			Interval i = (Interval)it.next();
			double start=i.getStart();
			double end=i.getEnd();
			
			double angle1 = Math.atan2(dx, start - dy);
			double angle2 = Math.atan2(dx, end - dy);
			double dangle = angle1 - angle2;
			if (dangle < 0) dangle += 2 * Math.PI;
			if (dangle > 2*Math.PI) dangle -= 2 * Math.PI;
			if (maxAngle < dangle) maxAngle = dangle;
			result += dangle;
		}
		maxangle[0] = maxAngle;
		
		return result;
	}

	@Override
	public void calculate() {
		
		/*
		 * Parameter setzen
		 */
//		if (this.parameters.containsKey("DEFENDER_RANGE"))
//		{
		this.DEFENDER_RANGE = Double.parseDouble(parameterBase.getParameter("DEFENDER_RANGE").toString());
//		}
		
		/*
		 * Initialisieren der Ausgabe
		 */
		this.initOutputData();
		
		/*
		 * Bearbeiten der Eingabedaten
		 */
		int numGroups = this.getNumGroups("analyzed_player");
		int outputIndex = 0;
		
		for (int i = 0; i < numGroups; i++) {
			
			int numRows = this.getNumRows("player", i);
			
			int[] id = new int[numRows];
			double[] x = new double[numRows];
			double[] y = new double[numRows];
			int analyzedPlayer = 0;
			
			for (int j = 0; j < numRows; j++) {
				id[j] = Integer.parseInt(this.getValue("player", i, j).toString());
				x[j] = Double.parseDouble(this.getValue("x_player", i, j).toString());
				y[j] = Double.parseDouble(this.getValue("y_player", i, j).toString());
				if (Integer.parseInt(this.getValue("analyzed_player", i, 0).toString()) == id[j]) {
					analyzedPlayer = j;
				}
			}
			
			//
			int j=analyzedPlayer; {
				
				IntervalList intervals = calcShootingLine(x, y, id, j);
				
				double angle;
				double maxAngle=0;
				
				int goal = -1;
				if (id[j] >= 11) {
					goal = 1;
				}
				
				double px = Constants.FIELD_WIDTH / 2 + goal * x[j];	// x-dist to goal
				double py = y[j];
				
				double[] maxangle = new double[1];
				angle = calcAngle(intervals, px, py, maxangle) * 180 / Math.PI;
				maxAngle = maxangle[0] * 180 / Math.PI;
				//
				
				Iterator itr = intervals.iterator();
				if (!itr.hasNext()) {
					this.outputData.addRow(-1);
					this.outputData.setValueAt(String.valueOf(id[j]), outputIndex, 0);
					this.outputData.setValueAt(String.valueOf(angle), outputIndex, 3);
					this.outputData.setValueAt(String.valueOf(maxAngle), outputIndex, 4);
					this.outputData.setValueAt(String.valueOf(i), outputIndex, 5);
					outputIndex++;
				} else {
					while (itr.hasNext()) {
						Interval interval = (Interval) itr.next();
						this.outputData.addRow(-1);
						this.outputData.setValueAt(String.valueOf(id[j]), outputIndex, 0);
						this.outputData.setValueAt(String.valueOf(interval.getStart()), outputIndex, 1);
						this.outputData.setValueAt(String.valueOf(interval.getEnd()), outputIndex, 2);
						this.outputData.setValueAt(String.valueOf(angle), outputIndex, 3);
						this.outputData.setValueAt(String.valueOf(maxAngle), outputIndex, 4);
						this.outputData.setValueAt(String.valueOf(i), outputIndex, 5);
						outputIndex++;
					}
				}
			}
		}
	}

	@Override
	public List<Signature> getInputSemantics() {
		return ShootingAngleSingle.inputSemantics;
	}

	@Override
	public List<Signature> getOutputSemantics() {
		return ShootingAngleSingle.outputSemantics;
	}

	/* (non-Javadoc)
	 * @see de.tum.in.fipm.base.data.feature.QueryFeature#getNecessaryParameters()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public HashSet<String> getNecessaryParameters() {
		return (HashSet)NecessaryParameters.clone();
	}
}
