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

public class MissileControlModule : AngularModule {
	private int slots;

	public MissileControlModule(JObject json) : base(json) {
		slots = (int)json["slots"];

		icon = Resources.Load("Sprites/Modules/Icons/MissileControl/" + getUniqueName(), typeof(Sprite)) as Sprite;
		silhouette = Resources.Load("Sprites/Modules/Silhouettes/MissileControl/" + getUniqueName(), typeof(Sprite)) as Sprite;
	}

	public int getSlots() {
		return slots;
	}

	public static MissileControlModule[] loadModules(string[] filenames) {
		Array.Sort(filenames);
		MissileControlModule[] array = new MissileControlModule[filenames.Length];

		for (int i = 0; i < filenames.Length; i++) {
			array[i] = new MissileControlModule(jsonFromFile(filenames[i]));
		}

		return array;
	}

	override public void updateClasses() {
	}

	override public ModuleData getData(int parent, float angleOffset, Vector position) {
		return new MissileControlModuleData(parent, this, angleOffset, position);
	}
}

public class MissileControlModuleData : AngularModuleData {
	Set<int> currentlyManaged;

	public MissileControlModuleData(int parent, Module template, float angleOffset, Vector position) : base(parent, template, angleOffset, position) {
		currentlyManaged = new Set<int>();
	}

	public int getRemainingSlots() {
		return ((MissileControlModule)template).getSlots() - currentlyManaged.count();
	}

	public bool canBeSafelyTurnedOff() {
		return currentlyManaged.count() == 0;
	}

	public void addControlledMissile(Unit missile) {
		currentlyManaged.add(missile.getID());
	}

	//Cannot use the reference as the missile is already dead at clients
	public void removeControlledMissile(int missileID) {
		int toBeRemoved = -1;
		foreach(int controlled in currentlyManaged) {
			if (controlled == missileID) {
				toBeRemoved = controlled;
			}
		}

		currentlyManaged.remove(toBeRemoved);
	}

	override public void checkDestroyedUnits(List<DeployedUnit> destroyedUnits) {
		foreach(DeployedUnit destroyed in destroyedUnits) {
			if (currentlyManaged.contains(destroyed.getID())) {
				Requests.removeFromMissileControl(this, destroyed);
			}
		}
	}

	override public void setHealth(float newHealth) {
		base.setHealth(newHealth);

		if (NetworkManager.isServer() && newHealth == 0.0f) {
			removeAllControlled();
		}
	}

	override public void setDisabled(bool val) {
		base.setDisabled(val);

		if (NetworkManager.isServer() && val) {
			removeAllControlled();
		}
	}

	private void removeAllControlled() {
		foreach(int controlled in currentlyManaged) {
			DeployedUnit unit = GameController.fromID(controlled);

			if (unit == null) {
				//is stored
				DeployedUnit parentUnit = GameController.fromID(parent);

				if (parentUnit != null) {
					parentUnit.cancelFire(controlled);
				}
			}
			else {
				Requests.destroyUnit(unit);
				NetworkManager.addToLog(controlled, "A " + unit.getClass().getName() + " lost its guidance and crashed");
			}
		}

		currentlyManaged.clear();
	}

	override public void updateTime(float delta) {
		DeployedUnit parentUnit = GameController.fromID(parent);
		if (parentUnit == null)  {
			Debug.LogWarning("Null parent for missile controller");
			return;
		}

		List<DeployedUnit> needChange = new List<DeployedUnit>();

		foreach(int controlled in currentlyManaged) { //checks if the missile can still be controlled by this module
			DeployedUnit unit = GameController.fromID(controlled);

			if (unit != null && !isInRangeAndAngle(unit) && parentUnit.getPosition().distance(unit.getPosition()) > 1000) { //distance check avoids bad computations for very close missiles
				Debug.Log(unit.getID() + "; " + this.getID() + " Lost guidance");
				//try to find another controller
				needChange.Add(unit);
			}
		}

		foreach(DeployedUnit unit in needChange) {
			Requests.removeFromMissileControl(this, unit);
		}

		foreach(DeployedUnit unit in needChange) {
			if (parentUnit.availableMissileControlSlots(unit) > 0) {
				parentUnit.addControlledMissile(unit, unit);
			}
			else {
				Requests.destroyUnit(unit);
				NetworkManager.addToLog(unit.getID(), "A " + unit.getClass().getName() + " lost its guidance and crashed YOLO");
			}
		}
	}
}