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

public enum ClassType {Ship = 0, Missile = 1, Helicopter = 2, Land = 3};

public class Class {
	private string uniqueName;
	private string name;
	private int id;
	private int sprite;
	private Sprite silhouette;
	private float silhouetteScale;

	private ClassType type;
	private Country country;
	private List<ModulePlacement> modulesPlacements;
	private List<ModuleUI> modulesUI;
	private List<Class> missilesAutoDef;
	private List<Class> missilesStrike;
	private List<Class> launchedClasses;
	private List<Loadout> loadouts;


	public Class(JObject json, ClassType type) {
		id = Util.classFreshId();
		this.type = type;

		uniqueName = (string)json["uniqueName"];
		name = (string)json["name"];
		sprite = (int)json["sprite"];

		country = GameController.getCountries()[(int)json["country"] - 1];

		modulesPlacements = new List<ModulePlacement>();

		JToken token;
		foreach (JObject jsonModulePlacement in json["modules"]) {
			string name = (string)jsonModulePlacement["name"];

			float angle = 0.0f;
			if (jsonModulePlacement.TryGetValue("angle", out token)) {
				angle = Mathf.Deg2Rad * (float)jsonModulePlacement["angle"];
			}

			float x = 0.0f;
			float y = 0.0f;
			float z = 0.0f;
			if (jsonModulePlacement.TryGetValue("x", out token)) {
				x = (float)jsonModulePlacement["x"];
			}
			if (jsonModulePlacement.TryGetValue("y", out token)) {
				y = (float)jsonModulePlacement["y"];
			}
			if (jsonModulePlacement.TryGetValue("z", out token)) {
				z = (float)jsonModulePlacement["z"];
			}

			int twin = -1;
			if (jsonModulePlacement.TryGetValue("twin", out token)) {
				twin = (int)jsonModulePlacement["twin"];
			}

			if (GameController.moduleFromUniqueName(name) == null) {
				Debug.LogError("Uknown module " + name);
			}

			modulesPlacements.Add(new ModulePlacement(GameController.moduleFromUniqueName(name), angle, new Vector(x, y, z), twin));
		}

		missilesAutoDef = new List<Class>();
		if (json.TryGetValue("missileAutoDef", out token)) {
			foreach (string uniqueMissileName in json["missileAutoDef"]) {
				missilesAutoDef.Add(GameController.classFromUniqueName(uniqueMissileName));
			}
		}

		missilesStrike = new List<Class>();
		if (json.TryGetValue("missileStrike", out token)) {
			foreach (string uniqueMissileName in json["missileStrike"]) {
				missilesStrike.Add(GameController.classFromUniqueName(uniqueMissileName));
			}
		}

		launchedClasses = new List<Class>();
		if (json.TryGetValue("launchedClasses", out token)) {
			foreach (string uniqueClassName in json["launchedClasses"]) {
				launchedClasses.Add(GameController.classFromUniqueName(uniqueClassName));
			}
		}

		modulesUI = new List<ModuleUI>();
		if (json.TryGetValue("modulesGrid", out token)) {
			foreach (JObject jsonModulesGrid in json["modulesGrid"]) {
				string header = "";
				if (jsonModulesGrid.TryGetValue("header", out token)) {
					header = (string)jsonModulesGrid["header"];
				}

				string description = "";
				if (jsonModulesGrid.TryGetValue("description", out token)) {
					description = (string)jsonModulesGrid["description"];
				}

				List<string> monitoredModules = new List<string>();
				foreach(string uniqueModuleName in jsonModulesGrid["monitoredModules"]) {
					monitoredModules.Add(uniqueModuleName);
				}

				modulesUI.Add(new ModuleUI((string)jsonModulesGrid["name"], header, description,
					(int)jsonModulesGrid["x"], 
					-(int)jsonModulesGrid["y"], 
					(int)jsonModulesGrid["width"],
					(int)jsonModulesGrid["height"],
					(int)jsonModulesGrid["tab"],
					monitoredModules));
			}
		}


		loadouts = new List<Loadout>();
		loadouts.Add(Loadout.empty());
		if (json.TryGetValue("loadouts", out token)) {
			foreach (JObject jsonLoadout in json["loadouts"]) {
				Loadout loadout = new Loadout((string)jsonLoadout["name"]);

				foreach (JObject ammoInfo in jsonLoadout["ammo"]) {
					loadout.addAmmo(GameController.classFromUniqueName((string)ammoInfo["class"]), (int)ammoInfo["count"]);
				}

				loadouts.Add(loadout);
			}
		}

		silhouetteScale = 1.0f;
		if (json.TryGetValue("silhouetteScale", out token)) {
			silhouetteScale = (float)json["silhouetteScale"];
		}

		silhouette = Resources.Load("Sprites/Classes/Silouhettes/" + getUniqueName(), typeof(Sprite)) as Sprite;
	}

	public int getID() {
		return id;
	}

	public string getUniqueName() {
		return uniqueName;
	}

	public string getName() {
		return name;
	}

	public Sprite getSprite(Visibility visibility, Relation relation, bool isStacked, bool differentRelations) {
		return GameController.getSprite(sprite, visibility, relation, isStacked, differentRelations);
	}

	public Sprite getSelection(Visibility visibility, Relation relation) {
		return GameController.getSelection(sprite, visibility, relation);
	}

	public Country getCountry() {
		return country;
	}

	public List<ModulePlacement> getModulePlacements() {
		return modulesPlacements;
	}

	public List<ModuleUI> getModulesUI() {
		return modulesUI;
	}

	public ClassType getClassType() {
		return type;
	}

	public PathConstraints getPathConstraints() {
		switch (type) {
			default:
				return new PathConstraints(Constraint.Sea);
				
			case ClassType.Missile:
				return new PathConstraints(Constraint.Air);

			case ClassType.Helicopter:
				return new PathConstraints(Constraint.Air);

			case ClassType.Land:
				return new PathConstraints(Constraint.Land);
		}
	}

	public bool canHit(ClassType type) {
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is GuidanceModule) {
				return ((GuidanceModule)module.module).canHit(type);
				//maybe change if multiple guidance
			}
		}

		return false;
	}

	public float getDistanceRadarOn() {
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is GuidanceModule) {
				return ((GuidanceModule)module.module).getDistanceRadarOn();
				//maybe change if multiple guidance
			}
		}

		return float.MaxValue;
	}

	public bool needsMissileControl() {
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is GuidanceModule) {
				return ((GuidanceModule)module.module).getNeedsMissileControl();
				//maybe change if multiple guidance
			}
		}

		return false;
	}

	public bool needsIlluminatedTarget() {
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is GuidanceModule) {
				return ((GuidanceModule)module.module).getNeedsTargetIlluminated();
				//maybe change if multiple guidance
			}
		}

		return false;
	}

	public bool hasGPSGuidance() {
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is GuidanceModule) {
				return ((GuidanceModule)module.module).hasGPSGuidance();
				//maybe change if multiple guidance
			}
		}

		return false;
	}

	public List<Class> getMissileAutoDef() {
		return missilesAutoDef;
	}

	public List<Class> getMissilesStrike() {
		return missilesStrike;
	}

	public List<Class> getLaunchedClasses() {
		return launchedClasses;
	}

	public float getLaunchDistance() {
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is CommandModule) {
				return ((CommandModule)module.module).getLaunchDistance();
				//maybe change if multiple guidance
			}
		}

		return 1000;
	}

	//range if at max speed, with full tanks and 1 engine
	public float getOptimalRange() {
		float fuel = 0.0f;
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is FuelTankModule) {
				fuel += ((FuelTankModule)module.module).getFuelQuantity(); // no matter the type, assume only accurate fuel tanks
			}
		}

		float consumption = 0.0f;
		float speed = 0.0f;
		foreach(ModulePlacement module in modulesPlacements) {
			if (module.module is EngineModule) { //assume 1 engine //TODO : Add cruise speed
				consumption = ((EngineModule)module.module).getFuelConsumption(1);

				GraphParams param = new GraphParams();
				speed = ((EngineModule)module.module).getMaxSpeed(param);
			}
		}

		if (consumption == 0.0f) {
			return float.MaxValue;
		}

		return speed * fuel / consumption;
	}

	public Loadout getLoadout(string name) {
		foreach(Loadout loadout in loadouts) {
			if (loadout.getName().ToLower() == name.ToLower()) {
				return loadout;
			}
		}

		return null;
	}

	public Loadout getLoadout(int index) {
		for (int i = 0; i < loadouts.Count; i++) {
			if (i == index) {
				return loadouts[i];
			}
		}

		return null;
	}

	public int getIndexFromLoadout(Loadout loadout) {
		for (int i = 0; i < loadouts.Count; i++) {
			if (loadouts[i] == loadout) {
				return i;
			}
		}

		return 0;
	}

	public List<Loadout> getLoadouts() {
		return loadouts;
	}

	public Sprite getSilhouette() {
		return silhouette;
	}

	public float getSilhouetteScale() {
		return silhouetteScale;
	}

	public static JObject jsonFromFile(string jsonPath) {
		StreamReader reader = File.OpenText(jsonPath);
		return (JObject)JToken.ReadFrom(new JsonTextReader(reader));
	}

	public static Class[] loadClasses(string[] filenames, ClassType type) {
		Array.Sort(filenames);
		Class[] array = new Class[filenames.Length];

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

		return array;
	}
}
