package com.useful.ucars;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.util.Vector;

import com.useful.uCarsAPI.uCarsAPI;
import com.useful.ucars.controls.ControlScheme;
import com.useful.ucars.controls.ControlSchemeManager;
import com.useful.ucars.util.UEntityMeta;
import com.useful.ucarsCommon.StatValue;

public class MotionManager {
	
	public static Vector rotateXZVector3dDegrees(Vector original, double degrees){
		double[] out = rotateVector2dRadians(original.getX(), original.getZ(), Math.toRadians(degrees));
		original.setX(out[0]);
		original.setZ(out[1]);
		return original;
	}
	
	public static double[] rotateVector2dDegrees(double x, double y, double degrees){
		return rotateVector2dRadians(x, y, Math.toRadians(degrees));
	}
	
	public static double[] rotateVector2dRadians(double x, double y, double radians){
	    double[] result = new double[2];
	    result[0] = x * Math.cos(radians) - y * Math.sin(radians);
	    result[1] = x * Math.sin(radians) + y * Math.cos(radians);
	    return result;
	}

	public static void move(Player player, float f, float s, boolean jumping) { // 'f' and 's' are values taken in by the vehicle control packet		
		Vector vec = new Vector();
		Entity ent = player.getVehicle();
		if (ent == null) {
			return;
		}
		while (!(ent instanceof Vehicle) && ent.getVehicle() != null) {
			ent = ent.getVehicle();
		}
		if(!ucars.listener.inACar(player)){
		}
		if (!ucars.listener.inACar(player) || !(ent instanceof Vehicle)) {
			return;
		}
		final Vehicle car = (Vehicle) ent;
		if(!player.equals(car.getPassenger())){
			return; //Only allow 1 driver
		}
		// Location loc = car.getLocation();
		// Vector carD = loc.getDirection();
		Vector plaD = player.getEyeLocation().getDirection();
		
		if(jumping){
			if(!UEntityMeta.hasMetadata(player, "ucarsToggleControls")){
				/*player.setMetadata("ucarsToggleControls", new StatValue(true, ucars.plugin));*/
				UEntityMeta.setMetadata(player, "ucarsToggleControls", new StatValue(true, ucars.plugin));
				if(ControlSchemeManager.isControlsLocked(player)){
					player.sendMessage(ucars.colors.getError()+"Cannot toggle control scheme right now! (It's been locked by another plugin)");
				}
				else {
					ControlSchemeManager.toggleControlScheme(player);
					if(!ucars.turningCircles && ControlSchemeManager.getScheme(player).equals(ControlScheme.KEYBOARD)){
						UEntityMeta.removeMetadata(car, "ucarsSteeringDir");
						UEntityMeta.setMetadata(car, "ucarsSteeringDir", new StatValue(plaD.clone().setY(0).normalize(), ucars.plugin));
					}
				}
			}
		}
		else { // !jumping
			if(UEntityMeta.hasMetadata(player, "ucarsToggleControls")){
				UEntityMeta.removeMetadata(player, "ucarsToggleControls");
			}
		}
		
		ControlScheme controls = ControlSchemeManager.getScheme(player);
		boolean keyboardSteering = controls.equals(ControlScheme.KEYBOARD);
		
		Vector carDirection = null;
		try {
			if(UEntityMeta.hasMetadata(car, "ucarsSteeringDir")/*car.hasMetadata("ucarsSteeringDir")*/){
				carDirection = (Vector) UEntityMeta.getMetadata(car, "ucarsSteeringDir")/*car.getMetadata("ucarsSteeringDir")*/.get(0).value();
			}
		} catch (Exception e) {
			carDirection = null;
		}
		if(carDirection == null){
			carDirection = car.getLocation().getDirection();
			//carDirection = plaD.clone().setY(0).normalize();
		}
		if(keyboardSteering || ucars.turningCircles){
			try {
				if(UEntityMeta.hasMetadata(car, "ucarsSteeringDir")){
					carDirection = (Vector) UEntityMeta.getMetadata(car, "ucarsSteeringDir").get(0).value();
				}
			} catch (Exception e) {
				carDirection = null;
			}
			if(carDirection == null){
				carDirection = car.getLocation().getDirection();
				//carDirection = plaD.clone().setY(0).normalize();
			}
		}
		
		CarDirection dir = CarDirection.NONE;
		
		if(f == 0 && !ucars.smoothDrive){
			return;
		}
		
		Location carLoc = car.getLocation().clone();
		boolean inAir = false;
		if(carLoc.getBlock().isEmpty() && carLoc.subtract(0, 1, 0).getBlock().isEmpty()) {
			inAir = true;
		}
		
		double len = car.getWidth() / 2;
		if(!car.getLocation().clone().add(len, -1, 0).getBlock().isEmpty() || !car.getLocation().clone().add(-len, -1, 0).getBlock().isEmpty() ||
				!car.getLocation().clone().add(0, -1, len).getBlock().isEmpty() || !car.getLocation().clone().add(0, -1, -len).getBlock().isEmpty() ||
				!car.getLocation().clone().add(len, -1, len).getBlock().isEmpty() || !car.getLocation().clone().add(len, -1, -len).getBlock().isEmpty() ||
				!car.getLocation().clone().add(-len, -1, len).getBlock().isEmpty() || !car.getLocation().clone().add(-len, -1, -len).getBlock().isEmpty()) { //Check area under car
			inAir = false;
		}
		if(ucars.smoothDrive && inAir){
			f = 0;
			s = 0;
		}
		else if(ucars.smoothDrive) {
			//Not in air
			ControlInput.setFirstAirTime(player, System.currentTimeMillis());
		}
		
		Boolean forwards = true; // if true, forwards, else backwards
		int side = 0; // -1=left, 0=straight, 1=right
		Boolean turning = false;
		if (f < 0) {
			dir = CarDirection.BACKWARDS;
			forwards = false;
		} else if (f > 0){
			dir = CarDirection.FORWARDS;
			forwards = true;
		}
		if (s > 0) {
			side = -1;
			turning = true;
		}
		if (s < 0) {
			side = 1;
			turning = true;
		}

		//player.sendMessage(""+ControlInput.getCurrentAccel(player)+" "+ControlInput.getCurrentDriveDir(player));
		
		long timeSinceOnGround = System.currentTimeMillis() - ControlInput.getFirstAirTime(player);
		
		double y = -0.1 + (-0.003*timeSinceOnGround); // rough gravity of minecraft
		if(y < -1){
			y = -1;
		}
		double d = 27;
		Boolean doDivider = false;
		Boolean doAction = false;
		double divider = 0.5; // x of the (1) speed
		double rotMod = uCarsAPI.getAPI().getMaxCarTurnAmountDegrees(car, 5);
		if (turning) {
			if (side < 0) {// do left action
				if(!keyboardSteering){
					doAction = true;
					UEntityMeta.setMetadata(car, "car.action", new StatValue(true, ucars.plugin));
				}
				else {
					carDirection = rotateXZVector3dDegrees(carDirection, ControlInput.getCurrentDriveDir(player).equals(CarDirection.BACKWARDS) ? rotMod : -rotMod);
				}
			} else if (side > 0) {// do right action
				if(!keyboardSteering){
					doDivider = true;
					UEntityMeta.setMetadata(car, "car.action", new StatValue(true, ucars.plugin));
				}
				else {
					carDirection = rotateXZVector3dDegrees(carDirection, ControlInput.getCurrentDriveDir(player).equals(CarDirection.BACKWARDS) ? -rotMod : rotMod);
				}
			}
		}
		if(!keyboardSteering && ucars.turningCircles && (!ucars.smoothDrive || !inAir)){
			//Rotate 'carDirection' vector according to where they're looking; max of rotMod degrees
			float pYaw = (float) Math.toDegrees(Math.atan2(plaD.getX() , -plaD.getZ())); //Calculate yaw from 'player direction' vector
			float cYaw = (float) Math.toDegrees(Math.atan2(carDirection.getX() , -carDirection.getZ())); //Calculate yaw from 'carDirection' vector
			/*if(ControlInput.getCurrentDriveDir(player).equals(CarDirection.BACKWARDS)*//* && ControlInput.getCurrentAccel(player) > 0*//*){
				pYaw += 180;
			}*/
			float yawDiff = pYaw - cYaw;
			if(yawDiff <= -180){
				yawDiff += 360;
			}
			else if(yawDiff > 180){
				yawDiff -= 360;
			}
			/*Bukkit.broadcastMessage(yawDiff+"");*/
			if(yawDiff < -rotMod){
				yawDiff = (float) -rotMod;
			}
			else if(yawDiff > rotMod){
				yawDiff = (float) rotMod;
			}
			carDirection = rotateXZVector3dDegrees(carDirection, yawDiff/*ControlInput.getCurrentDriveDir(player).equals(CarDirection.BACKWARDS) ? -yawDiff : yawDiff*/);
		}
		if(keyboardSteering || ucars.turningCircles){
			UEntityMeta.removeMetadata(car, "ucarsSteeringDir");
			UEntityMeta.setMetadata(car, "ucarsSteeringDir", new StatValue(carDirection.normalize(), ucars.plugin));
			plaD = carDirection.clone();
		}
		if (forwards) {
			double x = plaD.getX() / d;
			double z = plaD.getZ() / d;
			if (!doDivider) {
				if (UEntityMeta.hasMetadata(car, "car.braking")) {
					UEntityMeta.removeMetadata(car, "car.braking");
				}
			}
			if(!doAction){
				if (UEntityMeta.hasMetadata(car, "car.action")) {
					UEntityMeta.removeMetadata(car, "car.action");
				}
			}
			vec = new Vector(x, y, z);
			final ucarUpdateEvent event = new ucarUpdateEvent(car, vec, player, dir);
			event.setDoDivider(doDivider);
			event.setDivider(divider);
			final Vector v = vec;
			ucars.plugin.getServer().getScheduler()
					.runTask(ucars.plugin, new Runnable() {

						public void run() {
							ControlInput.input(car, v, event);
						}
					});
			return;
		}
		if (!forwards) {
			double x = plaD.getX() / d;
			double z = plaD.getZ() / d;
			if (!doDivider) {
				if (UEntityMeta.hasMetadata(car, "car.braking")) {
					UEntityMeta.removeMetadata(car, "car.braking");
				}
			}
			x = 0 - x;
			z = 0 - z;
			vec = new Vector(x, y, z);
			final Vector v = vec;
			final ucarUpdateEvent event = new ucarUpdateEvent(car, vec, player, dir);
			event.setDoDivider(doDivider);
			event.setDivider(divider);
			Bukkit.getScheduler().runTask(ucars.plugin, new Runnable(){

				@Override
				public void run() {
					ControlInput.input(car, v, event);
					return;
				}});
			return;
		}
	}

}