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

public class LauncherModule : Module {
	private List<string> temporaryCanLaunch;
	private List<Class> launchable;
	private float launchWaitingTime;

	public LauncherModule(JObject json) : base(json) {
		temporaryCanLaunch = new List<string>();
		launchable = new List<Class>();

		foreach (string uniqueClassName in json["canLaunch"]) {
			temporaryCanLaunch.Add(uniqueClassName);
		}

		launchWaitingTime = (float)json["launchWaitingTime"];

		sprite = Resources.Load("Sprites/Modules/Launchers/" + getUniqueName(), typeof(Sprite)) as Sprite;
	}

	public float getWaitingTime() {
		return launchWaitingTime;
	}

	public bool canLaunch(Class unitClass) {
		return launchable.Contains(unitClass);
	}

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

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

		return array;
	}

	override public void updateClasses() {
		foreach(string uniqueClassName in temporaryCanLaunch) {
			launchable.Add(GameController.classFromUniqueName(uniqueClassName));
		}
	}

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

public class LauncherModuleData : ModuleData {
	private List<StoredUnit> readyToLaunch;
	private float waitingTime;

	public LauncherModuleData(int parent, Module template, Vector position) : base(parent, template, position) {
		readyToLaunch = new List<StoredUnit>();
		waitingTime = 0.0f;
	}

	//should only be called from StoredUnit's changeStore
	public void addReadyToLaunch(StoredUnit unit) {
		readyToLaunch.Add(unit);
		//TODO : add delay
	}

	public void removeFromReady(StoredUnit unit) {
		readyToLaunch.Remove(unit);
	}

	public void removeFromReadyIfExists(int unitID) {
		StoredUnit unit = null;

		foreach(StoredUnit stored in readyToLaunch) {
			if (stored.getID() == unitID) {
				unit = stored;
			}
		}

		if (unit == null) {
			return;
		}

		readyToLaunch.Remove(unit);
	}

	public int getNumberReadyToLaunch(Class launchedClass) {
		int num = 0;

		foreach(StoredUnit unit in readyToLaunch) {
			if (!unit.isBeingLoaded() && unit.getClass() == launchedClass) {
				num ++;
			}
		}

		return num;
	}

	public List<Tuple<Class, int>> getReadyToLaunch() {
		Dictionary<Class, int> dic = new Dictionary<Class, int>();

		foreach(StoredUnit stored in readyToLaunch) {
			if (dic.ContainsKey(stored.getClass())) {
				dic[stored.getClass()] ++;
			}
			else {
				dic[stored.getClass()] = 1;
			}
		}

		List<Tuple<Class, int>> res = new List<Tuple<Class, int>>();
		foreach(KeyValuePair<Class, int> entry in dic) {
			res.Add(new Tuple<Class, int>(entry.Key, entry.Value));
		}

		return res;
	}

	public Tuple<bool, DeployedUnit> tryToFire(Class missileClass, DeployedUnit target) {
		if (waitingTime > 0.0f) {
			return new Tuple<bool, DeployedUnit>(false, null);
		}

		bool hasFired = false;
		StoredUnit toBeRemoved = null;
		DeployedUnit deployed = null;

		foreach(StoredUnit unit in readyToLaunch) {
			if (unit.getClass() == missileClass) {

				float angle = (new Vector(1, 0, 0)).angle(target.getPosition() - GameController.fromID(parent).getPosition());
				Requests.fireMissile(GameController.fromID(parent), unit, angle, target);
				deployed = GameController.fromID(unit.getID());
				Requests.changeTarget(deployed, target);
				Requests.moveTowardsTarget(deployed, target);

				toBeRemoved = unit;
				hasFired = true;
				break;
			}
		}

		if (hasFired) {
			toBeRemoved.removeFromStore();
			waitingTime = ((LauncherModule)template).getWaitingTime();
		}

		return new Tuple<bool, DeployedUnit>(hasFired, deployed);
	}

	public bool tryToLaunch(StoredUnit toBeLaunched) {
		if (waitingTime > 0.0f) {
			return false;
		}

		bool hasLaunched = false;
		StoredUnit toBeRemoved = null;

		foreach(StoredUnit unit in readyToLaunch) {
			if (unit == toBeLaunched) {

				Requests.deployUnit(unit, GameController.fromID(parent).getAngle());

				float angle = GameController.fromID(parent).getAngle();
				Vector displacement = new Vector(Mathf.Cos(angle), Mathf.Sin(angle), 0);
				Requests.moveUnit(GameController.fromID(unit.getID()), GameController.fromID(parent).getPosition() + displacement * unit.getClass().getLaunchDistance());

				toBeRemoved = unit;
				hasLaunched = true;
				break;
			}
		}

		if (hasLaunched) {
			toBeRemoved.removeFromStore();
			waitingTime = ((LauncherModule)template).getWaitingTime();
		}

		return hasLaunched;
	}

	//returns a unit of the right class which is not being loaded
	public StoredUnit prepareLoading(Class unitClass) {
		foreach(StoredUnit unit in readyToLaunch) {
			if (unit.getClass() == unitClass && !unit.isBeingLoaded()) {
				return unit;
			}
		}

		return null;
	}

	override public bool isFiring() {
		return waitingTime > 0.0f;
	}

	override public void updateTime(float delta) {
		waitingTime = Mathf.Max(0.0f, waitingTime - delta);
	}
}