using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using UnityEngine;

/*
 * Abstract module class that is used to provide similar methods to radar / missile control / illuminator
*/
using UnityEngine.Assertions;


public abstract class AngularModule : Module {
	private float angle;
	private float minVerticalAngle;
	private float maxVerticalAngle;
	private float minRange;
	private float maxRange;
	private float maxHeight;
	private Color displayColor;
	private bool displayIgnoreRaycast;

	public AngularModule(JObject json) : base(json) {
		JToken token;
		if (json.TryGetValue("angle", out token)) {
			angle = Mathf.Deg2Rad * (float)json["angle"];
		}
		else {
			angle = 2 * Mathf.PI;
		}

		if (json.TryGetValue("minVerticalAngle", out token)) {
			minVerticalAngle = -Mathf.Deg2Rad * (float)json["minVerticalAngle"] + Mathf.PI / 2;
		}
		else {
			minVerticalAngle = Mathf.PI;
		}

		if (json.TryGetValue("maxVerticalAngle", out token)) {
			maxVerticalAngle = -Mathf.Deg2Rad * (float)json["maxVerticalAngle"] + Mathf.PI / 2;
		}
		else {
			maxVerticalAngle = 0;
		}

		if (json.TryGetValue("minRange", out token)) {
			minRange = (float)json["minRange"];
		}
		else {
			minRange = 0.0f;
		}

		if (json.TryGetValue("maxRange", out token)) {
			maxRange = (float)json["maxRange"];
		}
		else {
			maxRange = 1000000.0f;
		}

		if (json.TryGetValue("maxHeight", out token)) {
			maxHeight = (float)json["maxHeight"];
		}
		else {
			maxHeight = float.MaxValue;
		}

		if (json.TryGetValue("display", out token)) {
			JObject display = (JObject)json["display"];

			float r = (float)display["r"];
			float g = (float)display["g"];
			float b = (float)display["b"];

			displayColor = new Color(r, g, b, 0.3f);

			if (display.TryGetValue("ignoreRaycast", out token)) {
				displayIgnoreRaycast = (bool)display["ignoreRaycast"];
			}
			else {
				displayIgnoreRaycast = false;
			}

		}
		else {
			displayColor = new Color(0, 0, 0, 0.2f);
		}
	}

	public float getAngle() {
		return angle;
	}

	public float getMinVerticalAngle() {
		return minVerticalAngle;
	}

	public float getMaxVerticalAngle() {
		return maxVerticalAngle;
	}

	public float getMinRange() {
		return minRange;
	}

	public float getMaxRange() {
		return maxRange;
	}

	public float getMaxHeight() {
		return maxHeight;
	}

	public Color getDisplayColor() {
		return displayColor;
	}

	public bool getDisplayIgnoreRaycast() {
		return displayIgnoreRaycast;
	}
}

public abstract class AngularModuleData : ModuleData {
	private float angleOffset;
	
	public AngularModuleData(int parent, Module template, float angleOffset, Vector position) : base(parent, template, position) {
		this.angleOffset = angleOffset;
	}

	public float getAngleOffset() {
		return angleOffset;
	}

	//should only be called when the parent is deployed
	public string isInRangeAndAngleMessage(DeployedUnit target) {
		DeployedUnit parentUnit = GameController.fromID(parent);
		Assert.IsNotNull(parentUnit);

		Vertex position = parentUnit.getPosition();
		Vertex targetPosition = target.getPosition();
		AngularModule angularTemplate = ((AngularModule)template);

		float distance = position.distance(targetPosition);
		if (distance < angularTemplate.getMinRange() || distance > angularTemplate.getMaxRange()) {
			return "out of range";
		}

		if (distance >  Mathf.Sqrt(2*Constants.EARTH_RADIUS) * (Mathf.Sqrt(position.z() + 1000000 + this.getPosition().z() + 1) + Mathf.Sqrt(targetPosition.z()))) { //horizon, A CHANGER QUAND SONAR
			return "not in horizon";
		}

		if ((targetPosition.z() - position.z()) > angularTemplate.getMaxHeight()) {
			return "height too high";
		}

		Vector diff = targetPosition - position;
		float angleTowardsTarget = (new Vector(1, 0, 0)).angle(diff);

		if (diff.norm() > 1 && !isAngleOk(angleTowardsTarget, parentUnit, angularTemplate)) {
			//Debug.Log(diff.norm());
			return "horizontal angle not gud " + diff.norm() + " ; " + isAngleOkMessage(angleTowardsTarget, parentUnit, angularTemplate);
		}

		float verticalAngle = Mathf.Acos(diff.normalized().z());

		if (angularTemplate.getMinVerticalAngle() >= verticalAngle && verticalAngle >= angularTemplate.getMaxVerticalAngle()) {
			return "";
		}
		else {
			return "vertical angle not gud";
		}
	}

	public bool isInRangeAndAngle(DeployedUnit target) {
		return isInRangeAndAngleMessage(target) == "";
	}

	private bool isAngleOk(float angleTowardsTarget, DeployedUnit parentUnit, AngularModule angularTemplate) {

		float angle = (angleTowardsTarget - parentUnit.getAngle() - angleOffset) % (2 * Mathf.PI);

		if (angle > Mathf.PI) {
			angle -= 2 * Mathf.PI;
		}
		else if (angle < -Mathf.PI) {
			angle += 2 * Mathf.PI;
		}

		//angle is now between -PI and PI

		return Mathf.Abs(angle) <= angularTemplate.getAngle() / 2;
	}

	private string isAngleOkMessage(float angleTowardsTarget, DeployedUnit parentUnit, AngularModule angularTemplate) {

		float angle = (angleTowardsTarget - parentUnit.getAngle() - angleOffset) % (2 * Mathf.PI);

		if (angle > Mathf.PI) {
			angle -= 2 * Mathf.PI;
		}
		else if (angle < -Mathf.PI) {
			angle += 2 * Mathf.PI;
		}

		//angle is now between -PI and PI

		return angle + " ; " + ( angularTemplate.getAngle() / 2) + "";
	}

	public bool isAngleOk(float angleTowardsTarget) {
		return isAngleOk(angleTowardsTarget, GameController.fromID(parent), (AngularModule)template);
	}
}