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

public enum LaunchingTechnique {VLS = 0};

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

	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"];
		technique = (LaunchingTechnique)((int)(json["launchingTechnique"]) - 1);

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

	public float getWaitingTime() {
		return launchWaitingTime;
	}

	public LaunchingTechnique getLaunchingTechnique() {
		return technique;
	}

	override public bool canStore(Class unitClass) {
		return canLaunch(unitClass);
	}

	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 : ContainerModuleData {
	private float fireWaitingTime;

	public LauncherModuleData(int parent, Module template, Vector position) : base(parent, template, position) {
		fireWaitingTime = 0.0f;
	}

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

		foreach(StoredUnit stored in storedUnits) {
			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 bool tryToFire(StoredUnit toBeFired, DeployedUnit target) {
		if (fireWaitingTime > 0.0f || !storedUnits.Contains(toBeFired)) {
			return false;
		}

		float angle = (new Vector(1, 0, 0)).angle(target.getPosition() - GameController.fromID(parent).getPosition());

		Requests.fireMissile(GameController.fromID(parent), toBeFired, angle, target);

		DeployedUnit deployed = GameController.fromID(toBeFired.getID());

		Requests.changeTarget(deployed, target);
		Requests.missileMovement(deployed, target, ((LauncherModule)template).getLaunchingTechnique());

		fireWaitingTime = ((LauncherModule)template).getWaitingTime();

		return true;
	}

	public bool tryToLaunch(StoredUnit toBeLaunched) {
		if (fireWaitingTime > 0.0f || !storedUnits.Contains(toBeLaunched)) {
			return false;
		}

		Requests.deployUnit(toBeLaunched, 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(toBeLaunched.getID()), GameController.fromID(parent).getPosition() + displacement * toBeLaunched.getClass().getLaunchDistance());

		fireWaitingTime = ((LauncherModule)template).getWaitingTime();

		return true;
	}

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

	public bool isFiring(Class unitClass) {
		foreach(StoredUnit stored in storedUnits) {
			if (stored.getClass() == unitClass && stored.getLoadingState() == LoadingState.BeingFired) {
				return true;
			}
		}

		return false;
	}

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