using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System;

/*
 * Core class of the game
*/
using System.IO;
using UnityEngine.Networking;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class GameController : MonoBehaviour {
    private static List<MapPolygon> map = new List<MapPolygon>();
    private static List<Country> countries = new List<Country>();
	private static List<DTerrain> terrains = new List<DTerrain> ();

    private static Set<TerrainVertex> terrainVertices = new Set<TerrainVertex>();
    private static Set<TerrainSegment> terrainSegments = new Set<TerrainSegment>();
    private static Set<TerrainTriangle> terrainTriangles = new Set<TerrainTriangle>();

	public static float minX = float.MaxValue; // min and max coordinates of the polygons in the svg file
	public static float maxX = float.MinValue;
	public static float minY = float.MaxValue;
	public static float maxY = float.MinValue;

	private static float currentScale = 1.0f;
	private static float gameSpeed = 1.0f; //1 sec IRL = gameSpeed sec in game
	private static bool pause = false;
	private static bool menuShown = false;

	private static bool isSelecting = false;
	private static Vector3 cornerSelection;
	private static Vector3 minSelection;
	private static Vector3 maxSelection;

	public static Material defaultMaterial;
	public static Material fadeMaterial;

	public Camera camera;

	public GameObject nonStaticDefaultUnit;
	public static GameObject defaultUnit;
	public GameObject nonStaticGridObject;
	public static GameObject gridObject;
	public GameObject nonStaticGUI;
	public static GameObject GUI;
	public GameObject nonStaticSelectionRectangle;
	public static GameObject selectionRectangle;

	public GameObject nonStaticGameSpeedText;
	public static GameObject gameSpeedText;
	public GameObject nonStaticPauseText;
	public static GameObject pauseText;
	public GameObject nonStaticMenuPanel;
	public static GameObject menuPanel;
	public GameObject nonStaticPopUp;
	public static GameObject popUp;

	private static List<Module> modules;
	private static List<Class> classes;
	private static List< Dictionary<Relation, Sprite> > sprites;
	private static List< Dictionary<Relation, Sprite> > selections;
	private static Dictionary<Relation, Material> relationMaterials;

	private static List<DeployedUnit> units;
	private static List<DeployedUnit> newUnits;
	private static List<DeployedUnit> destroyedUnits;
	private static List<StoredUnit> storedUnits;
	private static List<ModuleData> moduleDatas;
	private static List<Bullets> bulletsList;
	private static List<Bullets> destroyedBullets;

	private static List<DeployedUnit> selectedUnits;
	private static DeployedUnit infoSelectedUnit;

	private static Country playerCountry;

	private Vector3 oldMousePosition;

    // Use this for initialization
    void Start() {


		defaultMaterial = Resources.Load("Materials/Default", typeof(Material)) as Material;
		fadeMaterial = Resources.Load("Materials/Fade", typeof(Material)) as Material;
		defaultUnit = nonStaticDefaultUnit;
		gridObject = nonStaticGridObject;
		GUI = nonStaticGUI;
		selectionRectangle = nonStaticSelectionRectangle;

		StatusManagement.init();
		GridManagement.init();
		FireManagement.init();
		LaunchManagement.init();

		gameSpeedText = nonStaticGameSpeedText;
		pauseText = nonStaticPauseText;
		menuPanel = nonStaticMenuPanel;
		popUp = nonStaticPopUp;

		createCountries();
		initTerrains ();

		//List<MapPolygon> tempPoly = parseMap();
		//genMap(tempPoly);

		genMap2();

		new VolumeTerrain(); //creates the gameObject used for raycasts

		loadModules();
		loadClasses();
		loadSprites();

		foreach(Module module in modules) {
			module.updateClasses();
		}

		playerCountry = countries[Immortal.getCountry()];

		units = new List<DeployedUnit>();
		newUnits = new List<DeployedUnit>();
		destroyedUnits = new List<DeployedUnit>();

		storedUnits = new List<StoredUnit>();
		moduleDatas = new List<ModuleData>();

		bulletsList = new List<Bullets>();
		destroyedBullets = new List<Bullets>();

		if (NetworkManager.isServer()) {
			units.Add(new DeployedUnit(classFromUniqueName("Arleigh Burke"), new Vertex(500000, -200000, 0)));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("USAntiMissile")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("USAntiMissile")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("USAntiMissile")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("USAntiMissile")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("USAntiMissile")));
			units[0].addReadyToLaunch(new StoredUnit(classFromUniqueName("USAntiMissile")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("USChopper")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("USChopper")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));
			units[0].addToStorage(new StoredUnit(classFromUniqueName("Harpoon")));


			units.Add(new DeployedUnit(classFromUniqueName("Luyang"), new Vertex(500000, -800000, 0)));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addReadyToLaunch(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("Chopper")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("Chopper")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("AntiMissile")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));
			units[1].addToStorage(new StoredUnit(classFromUniqueName("CNHarpoon")));

			units.Add(new DeployedUnit(classFromUniqueName("JapanBoat"), new Vertex(1200000, -500000, 0)));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JAntiMissile")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JAntiMissile")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JAntiMissile")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JAntiMissile")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JAntiMissile")));
			units[2].addReadyToLaunch(new StoredUnit(classFromUniqueName("JAntiMissile")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JChopper")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JChopper")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));
			units[2].addToStorage(new StoredUnit(classFromUniqueName("JHarpoon")));

			/*foreach(DeployedUnit unit in units) {
				bool done = false;
				do {
					float x = UnityEngine.Random.Range(minX, maxX);
					float y = UnityEngine.Random.Range(minY, maxY);

					GameVertex gv = new GameVertex(x, y);

					if (unit.getClass().getPathConstraints().canGoThrough(getTerrainAt(gv))) {
						unit.setPosition(Converter.gameCooToMeters(gv, 0.0f));
						done = true;
					}
				} while (!done);
			}*/
		}


		selectedUnits = new List<DeployedUnit>();
    }

    // Update is called once per frame
    void Update() {

		//Inputs

		if (isSelecting) {
			minSelection = new Vector3();
			maxSelection = new Vector3();

			Vector3 newCornerSelection = Camera.main.ScreenToWorldPoint(Input.mousePosition);

			minSelection.x = Mathf.Min(cornerSelection.x, newCornerSelection.x);
			minSelection.y = Mathf.Min(cornerSelection.y, newCornerSelection.y);
			maxSelection.x = Mathf.Max(cornerSelection.x, newCornerSelection.x);
			maxSelection.y = Mathf.Max(cornerSelection.y, newCornerSelection.y);

			selectionRectangle.GetComponent<RectTransform>().position = minSelection;
			selectionRectangle.GetComponent<RectTransform>().sizeDelta = new Vector2(maxSelection.x - minSelection.x, maxSelection.y - minSelection.y);
		}

        if (Input.GetMouseButtonDown(1)) {
			if (Input.GetKey(KeyCode.LeftControl)) {
				Vector2 clickLocation = Camera.main.ScreenToWorldPoint(Input.mousePosition);
				GameVertex endPoint = new GameVertex(clickLocation);

				foreach(DeployedUnit unit in selectedUnits) {
					if (unit.canBeCommanded()) {
						Requests.setPositionTarget(unit, Converter.gameCooToMeters(endPoint, 0));
					}
				}
			}
			else {
				RaycastHit hit; 
				Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 
				if (! Physics.Raycast (ray, out hit, float.MaxValue, (int)Mathf.Pow(2, 10))) { //check if right click is not on object
					Vector2 clickLocation = Camera.main.ScreenToWorldPoint(Input.mousePosition);
					GameVertex endPoint = new GameVertex(clickLocation);


					foreach(DeployedUnit unit in selectedUnits) {
						if (unit.canBeCommanded()) {
							Requests.moveUnit(unit, Converter.gameCooToMeters(endPoint, 0), Input.GetKey(KeyCode.LeftShift));
						}
					}
				}
			}
        }

		if (Input.GetMouseButton(2)) {
			camera.transform.position -= (Camera.main.ScreenToWorldPoint(Input.mousePosition) - oldMousePosition);
		}

		if (Input.GetAxis("Zoom") != 0.0f) {
			float amount = (1 - Input.GetAxis("Zoom") * Constants.ZOOM_SPEED);

			camera.orthographicSize *= amount;

			camera.transform.position -= (Camera.main.ScreenToWorldPoint(Input.mousePosition) - oldMousePosition);

			DeployedUnit.staticScale(amount);
			foreach(DeployedUnit unit in units) {
				unit.scaleSprite(amount);
			}

			currentScale *=amount;
		}

		camera.transform.position += new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) * Constants.CAMERA_SPEED;

		oldMousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);

		if (Input.GetButtonDown("SpeedUp")) {
			if (gameSpeed < 20) {
				Requests.changeGameSpeed(gameSpeed + 1);
			}
		}

		if (Input.GetButtonDown("SpeedDown")) {
			if (gameSpeed > 1) {
				Requests.changeGameSpeed(gameSpeed - 1);
			}
		}

		if (Input.GetButtonDown("Pause")) {
			Requests.changePause(!pause);
		}

		if (NetworkManager.getPlayerJoining() > 0 && pause == false) {
			Requests.changePause(true);
		}

		if (Input.GetKeyDown(KeyCode.Escape)) {
			menuShown = !menuShown;
			menuPanel.SetActive(menuShown);
		}

		//Updates

		if (!pause) {
			foreach(DeployedUnit unit in units) {
				unit.move(Time.deltaTime * gameSpeed);

				if (NetworkManager.isServer()) {
					unit.updateFireMissiles();
					unit.updateTransferts();
					unit.checkPathOutdated();
					unit.checkMissile(Time.deltaTime * gameSpeed);
					unit.checkLanding(Time.deltaTime * gameSpeed);
					unit.checkAutoDef(Time.deltaTime * gameSpeed);
					unit.checkHull();
				}
			}

			if (NetworkManager.isServer()) {
				foreach(Bullets bullets in bulletsList) {
					if (bullets.hasArrived(Time.deltaTime * gameSpeed)) {
						bullets.dealDamage();
						destroyedBullets.Add(bullets);
					}
				}
			}


			//detection
			foreach(DeployedUnit unit in units) {
				unit.resetVisibility();
			}
			foreach(DeployedUnit unit in units) {
				if (NetworkManager.isServer() || unit.getClass().getCountry().getRelation(playerCountry) == Relation.Player) {
					unit.detectUnits();
				}
			}
			foreach(DeployedUnit unit in units) {
				unit.switchVisibility();
			}
		}

		foreach(DeployedUnit unit in units) {
			unit.updateVisibility();
			unit.updatePathLine();
			unit.updateDirectionLine();
			unit.updateTargetLine();
			unit.updateSprite();
			unit.updateMapInfos();
			unit.updateRadarDisplay();
			unit.updateShowMissileRange();
		}

		if (infoSelectedUnit != null) {
			StatusManagement.updateStatus(infoSelectedUnit);
			GridManagement.updateModules(infoSelectedUnit);
			FireManagement.updateFire(infoSelectedUnit);
			LaunchManagement.updateLaunchAndCommand(infoSelectedUnit);
		}

		if (NetworkManager.isServer()) {

			foreach(ModuleData data in moduleDatas) {
				if (!pause) {
					data.updateTime(Time.deltaTime * gameSpeed);
				}
				data.updateState();
			}
		}

		units.AddRange(newUnits);
		newUnits.Clear();

		if (NetworkManager.isServer() && destroyedUnits.Count > 0) {
			foreach(DeployedUnit unit in units) {
				if (!destroyedUnits.Contains(unit) && unit.getPath() != null && !unit.getPath().isValid()) {
					Requests.removePath(unit);
				}

				foreach(Tuple<Module, ModuleData> tuple in unit.getModules()) {
					tuple._2.checkDestroyedUnits(destroyedUnits);
				}

				if (destroyedUnits.Contains(unit) && unit.getMotherShip() != null) {
					Requests.removeBird(unit.getMotherShip(), unit);
				}
			}
		}

		foreach(DeployedUnit unit in destroyedUnits) {
			units.Remove(unit);

			unit.destroy();

			if (selectedUnits.Contains(unit)) {
				selectedUnits.Remove(unit);

				updateAfterSelectionChange();
			}

			if (infoSelectedUnit == unit) {
				infoSelectedUnit = null;
			}
		}
		destroyedUnits.Clear();

		foreach(Bullets bullets in destroyedBullets) {
			bulletsList.Remove(bullets);
		}
		destroyedBullets.Clear();


		foreach(DeployedUnit unit in units) {
			if (unit.getVisibility(playerCountry) == Visibility.Invisible) {
				if (selectedUnits.Contains(unit)) {
					selectedUnits.Remove(unit);

					updateAfterSelectionChange();
				}

				if (infoSelectedUnit == unit) {
					infoSelectedUnit = null;
				}
			}
		}

		if (NetworkManager.isServer()) {
			foreach(DeployedUnit unit in units) {
				if (unit.getTarget() != null && (!units.Contains(unit.getTarget()) || unit.getTarget().getVisibility(unit.getClass().getCountry()) == Visibility.Invisible)) { //target has been destroyed or is invisible
					Requests.setNullTarget(unit);
				}
			}
		}

		updateGrid();
    }

	void updateGrid() {
		//Delete children (ie existing grid elements)
		GameController.deleteAllChildren(transform);
			
		float size= (int)Mathf.Pow(10, ((int)Mathf.Log10(5000000 * currentScale)));

		float alpha = Mathf.Log10(5000000 * currentScale) - (int)Mathf.Log10(5000000 * currentScale);

		addGridPoints(size, alpha);
		if (size >= 10) {
			addGridPoints(size / 10, (1 - alpha));
		}
	}

	private void addGridPoints(float size, float alpha) {
		float width = Converter.gameCooToMeters((Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, 0)) - Camera.main.ScreenToWorldPoint(new Vector2(0, 0))).x); //width in m
		float height = Converter.gameCooToMeters((Camera.main.ScreenToWorldPoint(new Vector2(0, Screen.height)) - Camera.main.ScreenToWorldPoint(new Vector2(0, 0))).y); //height in m

		float startPositionX = Converter.gameCooToMeters(Camera.main.ScreenToWorldPoint(new Vector2(0, 0)).x);
		float startPositionY = Converter.gameCooToMeters(Camera.main.ScreenToWorldPoint(new Vector2(0, 0)).y);

		int maxPoints = 10000;
		int count = 0;
		for (float x = ((int)(startPositionX / size)) * size; x < startPositionX + width; x += size) {
			
			if (count > maxPoints) {
				break;
			}
			for (float y = ((int)(startPositionY / size)) * size; y < startPositionY + height; y += size) {
				if (count > maxPoints) {
					break;
				}

				GameVertex gridPosition = Converter.metersToGameCoo(new Vertex(x, y, 0));
				if (isOutsideMap(gridPosition)) {
					continue;
				}

				GameObject clone = GameObject.Instantiate(gridObject, transform);

				clone.transform.position = gridPosition.getVector(-1.0f);
				clone.transform.localScale *= currentScale;
				clone.GetComponent<SpriteRenderer>().color = new Color(1, 1, 1, alpha);

				count++;
			}
			count++;
			if (count > maxPoints) {
				break;
			}
		}
	}

	// Init methods
	void initTerrains() {
		terrains.Add(new DTerrain(-100,
			"#3b498b",
			Resources.Load("Materials/Terrains/Water", typeof(Material)) as Material));

		terrains.Add(new DTerrain(0,
			"#b7b42d",
			Resources.Load("Materials/Terrains/Water", typeof(Material)) as Material));

		terrains.Add(new DTerrain(100,
			"#2db78b",
			Resources.Load("Materials/Terrains/Grass", typeof(Material)) as Material));

		terrains.Add(new DTerrain(200,
			"#35a481",
			Resources.Load("Materials/Terrains/Mountains", typeof(Material)) as Material));
	}

    void createCountries() {
        Country usa = new Country("USA");
        countries.Add(usa);

		Country china = new Country("China");
		countries.Add(china);

		countries.Add(new Country("Japan"));
    }

    //Parsing of the svg
    public List<MapPolygon> parseMap() {
        List<MapPolygon> list = new List<MapPolygon>();

        char[] space = { ' ' };
        char[] comma = { ',' };
        char[] semiColon = { ';' };

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load("Assets/Resources/Maps/map2.svg");

        XmlNode svg = xmlDoc.GetElementsByTagName("svg")[0];
        XmlNode g = svg["g"];
        for (int i = 0; i < g.ChildNodes.Count; i++) {
            String path = g.ChildNodes[i].Attributes["d"].InnerText; //polygon

            ContourBuilder contourBuilder = new ContourBuilder();
            String[] points = path.Split(space);
            float x = 0.0f;
            float y = 0.0f;
			bool isCurve = false;
			int curveCount = 0;
            for (int j = 0; j < points.Length; j++) {
                if (points[j].Contains(",")) {

					if (isCurve) {
						curveCount ++;
						if (curveCount % 3 != 0) {
							continue;
						}
					}

                    String[] coo = points[j].Split(comma);

                    x += float.Parse(coo[0]);
                    y += -float.Parse(coo[1]);
					contourBuilder.addVertex(Converter.metersToGameCoo(new Vertex(x * 1000, y * 1000, 0)));
                } 
				else if (points[j] == "L") {
                    x = float.Parse(points[++j]);
                    y = -float.Parse(points[++j]);
					contourBuilder.addVertex(Converter.metersToGameCoo(new Vertex(x * 1000, y * 1000, 0)));
                }
				else if (points[j] == "c") {
					isCurve = true;
					curveCount = 0;
				}
            }

            for (int j = 0; j < contourBuilder.count(); j++) {
                x = contourBuilder.pointAt(j).x();
                y = contourBuilder.pointAt(j).y();

                if (x < minX) {
                    minX = x;
                }
                if (x > maxX) {
                    maxX = x;
                }

                if (y < minY) {
                    minY = y;
                }
                if (y > maxY) {
                    maxY = y;
                }
            }

            String style = g.ChildNodes[i].Attributes["style"].InnerText; // color 
            String[] styles = style.Split(semiColon);

            MapPolygon p = null;

            for (int j = 0; j < styles.Length; j++) {
                if (styles[j].StartsWith("fill:")) {
                    String color = styles[j].Remove(0, "fill:".Length);

                    for (int k = 0; k < terrains.Count; k++) {
                        if (terrains[k].getColor() == color) {
                            
                            p = new MapPolygon(contourBuilder.build(), terrains[k]);
                        }
                    }
                }
            }

            if (p == null) { //in case the fill argument is not present / the color is invalid
                p = new MapPolygon(contourBuilder.build(), terrains[0]);
            }

            list.Add(p);
        }

        return list;
    }

	private void loadModules() {
		modules = new List<Module>();

		modules.AddRange(HullModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Hulls", "*.damodule")));
		modules.AddRange(EngineModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Engines", "*.damodule")));
		modules.AddRange(LauncherModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Launchers", "*.damodule")));
		modules.AddRange(GuidanceModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Guidance", "*.damodule")));
		modules.AddRange(WarheadModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Warheads", "*.damodule")));
		modules.AddRange(CIWSModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/CIWS", "*.damodule")));
		modules.AddRange(FuelTankModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/FuelTanks", "*.damodule")));
		modules.AddRange(CommandModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Commands", "*.damodule")));
		modules.AddRange(StorageModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Storage", "*.damodule")));
		modules.AddRange(ActiveModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Active", "*.damodule")));
		modules.AddRange(PasssiveModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Passive", "*.damodule")));
		modules.AddRange(MissileControlModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/MissileControl", "*.damodule")));
		modules.AddRange(IlluminatorModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Illuminators", "*.damodule")));
		modules.AddRange(PropellerModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Propellers", "*.damodule")));
		modules.AddRange(RudderModule.loadModules(Directory.GetFiles("Assets/Resources/Data/Modules/Rudders", "*.damodule")));
	}

	private void loadClasses() {
		classes = new List<Class>();

		classes.AddRange(Class.loadClasses(Directory.GetFiles("Assets/Resources/Data/Classes/Missiles", "*.daclass"), ClassType.Missile));
		classes.AddRange(Class.loadClasses(Directory.GetFiles("Assets/Resources/Data/Classes/Helicopters", "*.daclass"), ClassType.Helicopter));
		classes.AddRange(Class.loadClasses(Directory.GetFiles("Assets/Resources/Data/Classes/Land", "*.daclass"), ClassType.Land));
		classes.AddRange(Class.loadClasses(Directory.GetFiles("Assets/Resources/Data/Classes/Ships", "*.daclass"), ClassType.Ship));
	}

	private void loadSprites() {
		sprites = new List< Dictionary<Relation, Sprite> >();
		sprites.Add(new Dictionary<Relation, Sprite>());
		sprites[0].Add(Relation.Player, Resources.Load("Sprites/Units/Stack/player", typeof(Sprite)) as Sprite);
		sprites[0].Add(Relation.Ennemy, Resources.Load("Sprites/Units/Stack/ennemy", typeof(Sprite)) as Sprite);
		sprites[0].Add(Relation.Unknown, Resources.Load("Sprites/Units/Stack/unknown", typeof(Sprite)) as Sprite);
		sprites[0].Add(Relation.Multiple, Resources.Load("Sprites/Units/Stack/multiple", typeof(Sprite)) as Sprite);

		sprites.Add(new Dictionary<Relation, Sprite>());
		sprites[1].Add(Relation.Player, Resources.Load("Sprites/Units/Navy/player", typeof(Sprite)) as Sprite);
		sprites[1].Add(Relation.Ennemy, Resources.Load("Sprites/Units/Navy/ennemy", typeof(Sprite)) as Sprite);
		sprites[1].Add(Relation.Unknown, Resources.Load("Sprites/Units/Navy/unknown", typeof(Sprite)) as Sprite);

		sprites.Add(new Dictionary<Relation, Sprite>());
		sprites[2].Add(Relation.Player, Resources.Load("Sprites/Units/Planes/player", typeof(Sprite)) as Sprite);
		sprites[2].Add(Relation.Ennemy, Resources.Load("Sprites/Units/Planes/ennemy", typeof(Sprite)) as Sprite);
		sprites[2].Add(Relation.Unknown, Resources.Load("Sprites/Units/Planes/unknown", typeof(Sprite)) as Sprite);


		selections = new List< Dictionary<Relation, Sprite> >();
		selections.Add(new Dictionary<Relation, Sprite>());
		selections[0].Add(Relation.Player, Resources.Load("Sprites/Selections/Navy/player", typeof(Sprite)) as Sprite);
		selections[0].Add(Relation.Ennemy, Resources.Load("Sprites/Selections/Navy/ennemy", typeof(Sprite)) as Sprite);
		selections[0].Add(Relation.Unknown, Resources.Load("Sprites/Selections/Navy/unknown", typeof(Sprite)) as Sprite);

		selections.Add(new Dictionary<Relation, Sprite>());
		selections[1].Add(Relation.Player, Resources.Load("Sprites/Selections/Planes/player", typeof(Sprite)) as Sprite);
		selections[1].Add(Relation.Ennemy, Resources.Load("Sprites/Selections/Planes/ennemy", typeof(Sprite)) as Sprite);
		selections[1].Add(Relation.Unknown, Resources.Load("Sprites/Selections/Planes/unknown", typeof(Sprite)) as Sprite);


		relationMaterials = new Dictionary<Relation, Material>();
		relationMaterials.Add(Relation.Player, Resources.Load("Materials/RelationsMaterials/Player", typeof(Material)) as Material);
		relationMaterials.Add(Relation.Ennemy, Resources.Load("Materials/RelationsMaterials/Ennemy", typeof(Material)) as Material);
		relationMaterials.Add(Relation.Unknown, Resources.Load("Materials/RelationsMaterials/Unknown", typeof(Material)) as Material);
	}

    //Generates the map based on the simple polygons (that can be overlapping) of the svg file
    public void genMap(List<MapPolygon> mapPolygons) {
        List<SlicedPolygon> remains = new List<SlicedPolygon>(mapPolygons.Count); // one SlicedPolygon per original poly

        foreach (MapPolygon mapPolygon in mapPolygons) { // init
            remains.Add(mapPolygon.getSlicedPolygon());
        }

        for (int i = 0; i < mapPolygons.Count; i++) { // add holes

            //remove from poly, all other poly that are above (<=> with a greater index)
            remains[i] = remains[i].difference(i + 1, remains);
        }

        // At this point, no polygons are overlapping

        for (int i = 0; i < mapPolygons.Count; i++) {
            MapPolygon mapPoly = new MapPolygon(remains[i].cutLongEdges(), mapPolygons[i].getTerrain());
            mapPoly.addToGame();
            map.Add(mapPoly);

            mapPoly.triangulate(terrainVertices, terrainSegments, terrainTriangles);
        }
    }

	private void genMap2() {

		minX = 0;
		minY = -1000;

		maxX = 1000;
		maxY = 0;

		int w = 300;
		int h = 300;

		float size = (maxX - minX) / w;

		Texture2D tex = null;
		byte[] fileData;

		string filepath = "Assets/Resources/Maps/heightmap2.png";

		if (File.Exists(filepath))     {
			fileData = File.ReadAllBytes(filepath);
			tex = new Texture2D(w, h);
			tex.LoadImage(fileData);
		}
		else {
			Debug.LogError("Height map does not exist");
		}

		for (float x = 0; x < w; x++) {
			for (float y = 0; y < h; y++) {
				DTerrain terrain = terrains[0];
				if (tex.GetPixelBilinear(x/w, y/h).grayscale >= 0.05f) {
					terrain = terrains[2];
				}

				MapPolygon mapPoly = new MapPolygon(new GameVertex(minX + x * size, minY + y * size), size, terrain);
				//mapPoly.addToGame();
				map.Add(mapPoly);

				mapPoly.triangulate(terrainVertices, terrainSegments, terrainTriangles);
			}
		}
	}

    public static List<DTerrain> getTerrains() {
        return terrains;
    }

	public static Set<TerrainTriangle> getTerrainTriangles() {
		return terrainTriangles;
	}

	public static List<MapPolygon> getMap() {
		return map;
	}

	public static bool isOutsideMap(GameVertex vertex) {
		return vertex.x() < minX || vertex.x() > maxX || vertex.y() < minY || vertex.y() > maxY;
	}

    public static DTerrain getTerrainAt(GameVertex p) {
        foreach (MapPolygon mapPolygon in map) {
            if (mapPolygon.isPointInside(p)) {
                return mapPolygon.getTerrain();
            }
        }

        return terrains[0];
    }

	public static float altitudeAt(GameVertex p) {
		MapPolygon location = null;
		foreach (MapPolygon mapPolygon in map) {
			if (mapPolygon.isPointInside(p)) {
				location = mapPolygon;
			}
		}

		if (location == null) {
			return -100.0f;
		}
		else{
			return location.getTerrain().getAltitude(); //temporary
		}

		int index = 0;

		for (int i = 0; i < terrains.Count; i++) {
			if (terrains[i] == location.getTerrain()) {
				index = i;
				break;
			}
		}

		if (index == terrains.Count - 1) {
			return location.getTerrain().getAltitude();
		}

		DTerrain terrain = terrains[index];
		float minDist = float.MaxValue;

		foreach (MapPolygon mapPolygon in map) {
			if (mapPolygon.getTerrain() == terrain) {
				float dist = mapPolygon.getSlicedPolygon().distanceTo(p);
				if (dist < minDist) {
					minDist = dist;
				}
			}
		}

		DTerrain upperTerrain = terrains[index + 1];
		float minDistUpper = float.MaxValue;

		foreach (MapPolygon mapPolygon in map) {
			if (mapPolygon.getTerrain() == upperTerrain) {
				float dist = mapPolygon.getSlicedPolygon().distanceTo(p);
				if (dist < minDistUpper) {
					minDistUpper = dist;
				}
			}
		}

		float coeff = minDistUpper / (minDistUpper + minDist);

		return coeff * terrain.getAltitude() + (1 - coeff) * upperTerrain.getAltitude();
	}

	public static float altitudeAt(GameVertex p, PathConstraints constraints) {
		float alt = altitudeAt(p);

		if (alt < 0 && constraints.isSurface()) {
			return 0.0f;
		}
		else {
			return alt;
		}
	}

    public static Set<TerrainVertex> getTerrainVertices() {
        return terrainVertices;
    }

	public static Sprite getSprite(int num, Visibility visibility, Relation relation, bool isStacked, bool differentRelations) {
		if (visibility == Visibility.Invisible) {
			return null;
		}

		int index;

		if (!isStacked) {
			index = num;
		}
		else {
			index = 0;

			if (differentRelations) {
				return sprites[index][Relation.Multiple];
			}
		}

		if (visibility == Visibility.Unknown) {
			return sprites[index][Relation.Unknown];
		}

		return sprites[index][relation];
	}

	public static Sprite getSelection(int num, Visibility visibility, Relation relation) {
		if (visibility == Visibility.Invisible) {
			return null;
		}

		if (visibility == Visibility.Unknown) {
			return selections[num-1][Relation.Unknown];
		}

		return selections[num-1][relation];
	}

	public static Material getRelationMaterial(Relation relation) {
		return relationMaterials[relation];
	}

	public static Country getPlayerCountry() {
		return playerCountry;
	}

	public static List<Country> getCountries() {
		return countries;
	}

	public static GameObject getDefaultUnit() {
		return defaultUnit;
	}

	public static List<DeployedUnit> getDeployedUnits() {
		return units;
	}

	public static List<StoredUnit> getStoredUnits() {
		return storedUnits;
	}

	public static List<ModuleData> getModuleDatas() {
		return moduleDatas;
	}

	public static List<Class> getClasses() {
		return classes;
	}

	public static bool hasAvailableIlluminatingSlots(Country requester, DeployedUnit target) {
		
		foreach(DeployedUnit unit in units) {
			if (unit.getClass().getCountry() == requester && unit.hasAvailableIlluminatingSlots(target)) {
				return true;
			}
		}

		return false;
	}

	public static void illuminateTarget(Country requester, DeployedUnit target) {
		foreach(DeployedUnit unit in units) {
			if (unit.getClass().getCountry() == requester && unit.hasAvailableIlluminatingSlots(target)) {
				unit.illuminateTarget(target);
				return;
			}
		}
	}

	// UI

	public static void leftClick(DeployedUnit unit) {
		if (unit.getOwner() != playerCountry) {
			return;
		}

		if (selectedUnits.Contains(unit)) {
			selectedUnits.Remove(unit);
			unit.setSelected(false);
		}
		else {
			foreach (DeployedUnit otherUnit in selectedUnits) {
				otherUnit.setSelected(false);
			}
			selectedUnits.Clear();

			selectedUnits.Add(unit);
			unit.setSelected(true);
		}

		updateAfterSelectionChange();
	}

	public static void rightClick(DeployedUnit unit) {
		foreach (DeployedUnit otherUnit in selectedUnits) {
			if (otherUnit.getClass().getClassType() != ClassType.Missile && otherUnit.canBeCommanded()) {
				Requests.changeTarget(otherUnit, unit);
			}
		}
	}

	public void beginSelection() {
		if (Input.GetMouseButton(0)) {
			isSelecting = true;
			selectionRectangle.SetActive(true);

			cornerSelection = Camera.main.ScreenToWorldPoint(Input.mousePosition);
		}
	}

	public void endSelection() {
		if (!Input.GetMouseButtonUp(0)) {
			return;
		}
		isSelecting = false;
		selectionRectangle.SetActive(false);

		//add units
		foreach (DeployedUnit unit in selectedUnits) {
			unit.setSelected(false);
		}
		selectedUnits.Clear();

		foreach (DeployedUnit unit in units) {
			if (true || unit.getClass().getCountry() == playerCountry) {
				Vector2 vec = Converter.metersToGameCoo(unit.getPosition()).getVector();
				if (minSelection.x <= vec.x + Constants.TOLERANCE_SELECTION * currentScale 
					&& minSelection.y <= vec.y + Constants.TOLERANCE_SELECTION * currentScale 
					&& maxSelection.x >= vec.x - Constants.TOLERANCE_SELECTION * currentScale 
					&& maxSelection.y >= vec.y - Constants.TOLERANCE_SELECTION * currentScale) {
					selectedUnits.Add(unit);
					unit.setSelected(true);
				}
			}
		}

		updateAfterSelectionChange();
	}

	private static void updateAfterSelectionChange() {
		GUI.SetActive(selectedUnits.Count > 0);

		if (selectedUnits.Count == 0) {
			infoSelectedUnit = null;
			return;
		}

		if ((infoSelectedUnit == null || !selectedUnits.Contains(infoSelectedUnit)) && selectedUnits.Count > 0) {
			infoSelectedUnit = selectedUnits[0];
		}

		StatusManagement.constructStatus(infoSelectedUnit);
		GridManagement.constructGrid(infoSelectedUnit);
		FireManagement.constructFire(infoSelectedUnit);
		LaunchManagement.constructLaunchAndCommand(infoSelectedUnit);
	}

	/*public void selectedUnitToggleChanged(bool value) {
		foreach(Toggle toggle in nonStaticSelectedUnitsContent.GetComponentsInChildren<Toggle>()) {
			if (toggle.isOn) {
				infoSelectedUnit = toggle.GetComponentInParent<SelectedUnitBehavior>().getSelectedUnit();
			}
		}

		constructModulesInfo();
	}

	public void submitOrder() {
		if (infoSelectedUnit == null) {
			return;
		}

		Requests.unitOrder(infoSelectedUnit, unitCommandInput.GetComponent<InputField>().text);
	}*/

	public static void addDeployedUnit(DeployedUnit deployed) {
		newUnits.Add(deployed);
	}

	public static void destroyUnit(DeployedUnit unit) {
		destroyedUnits.Add(unit);
	}

	public static void addStoredUnit(StoredUnit stored) {
		storedUnits.Add(stored);
	}

	public static void removedStoredUnit(StoredUnit stored) {
		storedUnits.Remove(stored);
	}

	public static void addModuleData(ModuleData data) {
		moduleDatas.Add(data);
	}

	public static void removeModuleData(ModuleData data) {
		moduleDatas.Remove(data);
	}

	public static void addBullets(Bullets bullets) {
		bulletsList.Add(bullets);
	}

	public static void setGameSpeed(float newGameSpeed) {
		gameSpeed = newGameSpeed;
		gameSpeedText.GetComponent<Text>().text = "x " + (int)gameSpeed;
	}

	public static void setPause(bool value) {
		pause = value;
		pauseText.SetActive(pause);
	}

	/*private static void updateSelectedUnitsPanel() {

		//Delete children
		List<GameObject> children = new List<GameObject>();
		foreach (Transform child in selectedUnitsContent.transform)  {
			children.Add(child.gameObject); 
		}

		foreach (GameObject child in children) {
			Destroy(child);
		}

		for (int i = 0; i < selectedUnits.Count; i++) {
			GameObject clone = GameObject.Instantiate(selectedUnitToggle, selectedUnitsContent.transform);

			clone.transform.FindChild("UnitName").gameObject.GetComponent<Text>().text = selectedUnits[i].getClass().getName();

			//temporary
			clone.transform.FindChild("Background").gameObject.GetComponent<Image>().sprite = selectedUnits[i].getClass().getSprite(Visibility.Visible, selectedUnits[i].getOwner().getRelation(GameController.getPlayerCountry()), false, false);
			clone.transform.FindChild("Background").FindChild("Checkmark").gameObject.GetComponent<Image>().sprite = selectedUnits[i].getClass().getSelection(Visibility.Visible, selectedUnits[i].getOwner().getRelation(GameController.getPlayerCountry()));

			clone.GetComponent<SelectedUnitBehavior>().setSelectedUnit(selectedUnits[i]);
			clone.GetComponent<Toggle>().isOn = (selectedUnits[i] == infoSelectedUnit);

		}

		constructModulesInfo();
	}

	private static void updateInfoSelectedUnitPanel() {
		if (infoSelectedUnit == null) {
			return;
		}

		infoSelectedUnitContent.transform.FindChild("SpeedText").GetComponent<Text>().text = "Current speed : " + infoSelectedUnit.getcurrentSpeed() + " m/s";
		infoSelectedUnitContent.transform.FindChild("AltitudeText").GetComponent<Text>().text = "Current altitude : " + infoSelectedUnit.getPosition().z() + " m";
	}

	public static void constructModulesInfo() {
		if (infoSelectedUnit == null) {
			return;
		}

		//Delete children
		List<GameObject> children = new List<GameObject>();
		foreach (Transform child in modulesContent.transform)  {
			children.Add(child.gameObject); 
		}

		foreach (GameObject child in children) {
			Destroy(child);
		}

		foreach(Tuple<Module, ModuleData> module in infoSelectedUnit.getModules()) {
			GameObject clone = GameObject.Instantiate(modulePanel, modulesContent.transform);

			clone.GetComponent<ModuleBehavior>().setModule(module);
		}
	}

	public static void updateModulesInfo() {
		foreach (Transform child in modulesContent.transform)  {
			Tuple<Module, ModuleData> module = child.gameObject.GetComponent<ModuleBehavior>().getModule();

			Color color = Util.getColorFromValue(module._2.getHealth() / module._1.getHealth());
			color.a = 0.5f;

			child.gameObject.GetComponent<Image>().color = color;

			child.FindChild("Image").gameObject.GetComponent<Image>().sprite = module._1.getSprite();

			child.FindChild("Image").FindChild("Name").GetComponent<Text>().text = module._1.getName();

			child.FindChild("Image").FindChild("Destroyed").gameObject.SetActive(module._2.isDestroyed());
			child.FindChild("Image").FindChild("Firing").gameObject.SetActive(module._2.isFiring() && (Time.frameCount % 30) < 15);
			child.FindChild("Image").FindChild("Disabled").gameObject.SetActive(module._2.isDisabled() && !module._2.isDestroyed());

			if (module._2 is FuelTankModuleData && ((FuelTankModuleData)module._2).getFuelLeft() == 0.0f) {
				child.FindChild("Image").FindChild("Important").gameObject.SetActive(!module._2.isDestroyed());
				child.FindChild("Image").FindChild("Important").FindChild("Text").GetComponent<Text>().text = "EMPTY";
			}
			if (module._2 is EngineModuleData && !infoSelectedUnit.hasFuelLeft(((EngineModule)module._1).getFuelType())) {
				child.FindChild("Image").FindChild("Important").gameObject.SetActive(!module._2.isDestroyed());
				child.FindChild("Image").FindChild("Important").FindChild("Text").GetComponent<Text>().text = "NO FUEL";
			}

			child.FindChild("Power").gameObject.SetActive(module._1.canBeDisabled() && !module._2.isDestroyed());
			//clone.transform.FindChild("HealthBar").FindChild("RemainingHealth").GetComponent<RectTransform>().localScale = new Vector3(module._2.getHealth() / module._1.getHealth(), 1, 1);
			//clone.transform.FindChild("HealthBar").FindChild("RemainingHealth").GetComponent<RawImage>().color = Util.getColorFromValue(module._2.getHealth() / module._1.getHealth());
		}
	}

	public void buttonDisableModule(Button button) {
		Requests.disableModule(button.GetComponentInParent<ModuleBehavior>().getModule()._2, !button.GetComponentInParent<ModuleBehavior>().getModule()._2.isDisabled());
	}*/

	public static void deleteAllChildren(Transform transform, bool saveInactive = false) {
		List<GameObject> toBeDeleted = new List<GameObject>();

		foreach(Transform child in transform) {
			if (child.gameObject.activeSelf || !saveInactive) {
				toBeDeleted.Add(child.gameObject);
			}
		}

		foreach(GameObject child in toBeDeleted) {
			GameObject.Destroy(child);
		}
	}

	// Network

	public static Class classFromUniqueName(string uniqueName) {
		foreach(Class clas in classes) {
			if (clas.getUniqueName() == uniqueName) {
				return clas;
			}
		}

		return null;
	}

	public static Class classFromID(int ID) {
		foreach(Class clas in classes) {
			if (clas.getID() == ID) {
				return clas;
			}
		}

		return null;
	}

	public static DeployedUnit fromID(int unitID) {
		foreach(DeployedUnit unit in units) {
			if (unit.getID() == unitID) {
				return unit;
			}
		}

		foreach(DeployedUnit unit in newUnits) {
			if (unit.getID() == unitID) {
				return unit;
			}
		}

		return null;
	}

	public static StoredUnit storedFromID(int unitID) {
		foreach(StoredUnit unit in storedUnits) {
			if (unit.getID() == unitID) {
				return unit;
			}
		}

		return null;
	}

	public static Unit unitFromID(int unitID) {
		DeployedUnit deployed = GameController.fromID(unitID);
		if (deployed != null) {
			return deployed;
		}

		StoredUnit stored = GameController.storedFromID(unitID);
		return stored; // return even if null (the unit must have been destroyed)
	}

	public static ModuleData moduleDataFromID(int moduleDataID) {
		foreach(ModuleData data in moduleDatas) {
			if (data.getID() == moduleDataID) {
				return data;
			}
		}

		return null;
	}

	public static Module moduleFromUniqueName(string uniqueName) {
		foreach(Module module in modules) {
			if (module.getUniqueName() == uniqueName) {
				return module;
			}
		}

		return null;
	}

	public static Module moduleFromID(int ID) {
		foreach(Module module in modules) {
			if (module.getID() == ID) {
				return module;
			}
		}

		return null;
	}

	//Info selected unit inputs management
	public static DeployedUnit getInfoSelectedUnit() {
		return infoSelectedUnit;
	}

	public static void switchEMCONSelectedUnit() {
		if (infoSelectedUnit != null) {
			Requests.setEMCON(infoSelectedUnit, !infoSelectedUnit.isEMCON());
		}
	}

	public void NSswitchEMCONSelectedUnit() {
		switchEMCONSelectedUnit();
		hidePopUp(); //in case the popup was activated
	}

	public static void switchDisplayRadarSelectedUnit() {
		if (infoSelectedUnit != null) {
			infoSelectedUnit.setDisplayRadar(!infoSelectedUnit.isDisplayingRadar());
		}
	}

	public static void stopSelectedUnit() {
		if (infoSelectedUnit != null) {
			Requests.removePath(infoSelectedUnit);
		}
	}

	public static void setTargetSpeedSelectedUnit(float speedFactor, int buttonIndex) {
		if (infoSelectedUnit != null) {
			Requests.setTargetSpeed(infoSelectedUnit, infoSelectedUnit.getMaxSpeed() * speedFactor, buttonIndex);
		}
	}

	public static void fireMissilesSelectedUnit(Class missile, int number) {
		if (infoSelectedUnit != null) {
			Requests.unitOrder(infoSelectedUnit, "fire " + number + " " + missile.getUniqueName());
		}
	}

	public static void setAntimissileSalvoSelectedUnit(Class antimissile, int num) {
		if (infoSelectedUnit != null) {
			Requests.setAntimissileSalvo(infoSelectedUnit, antimissile, num);
		}
	}

	public static void showRangeSelected(float range) {
		if (infoSelectedUnit != null) {
			infoSelectedUnit.showRange(range);
		}
	}

	public static void unshowRangeSelected() {
		if (infoSelectedUnit != null) {
			infoSelectedUnit.unshowRange();
		}
	}

	/*public static void launchSelectedUnit(Class launched, int number, Loadout loadout) {
		if (infoSelectedUnit != null) {
			Requests.unitOrder(infoSelectedUnit, "launch " + number + " " + launched.getUniqueName() + " with " + loadout.getName());
		}
	}*/

	public static void prepareSelectedUnit(Class launched, int number, Loadout loadout) {
		if (infoSelectedUnit != null) {
			Requests.unitOrder(infoSelectedUnit, "prepare " + number + " " + launched.getUniqueName() + " with " + loadout.getName());
		}
	}

	public static void takeOffSelectedUnit(StoredUnit stored) {
		if (infoSelectedUnit != null) {
			Requests.unitOrder(infoSelectedUnit, "takeoff " + stored.getID());
		}
	}

	public static void landSelectedUnit() {
		if (infoSelectedUnit != null) {
			Requests.unitOrder(infoSelectedUnit, "land");
		}
	}

	public static void showPopUp() {
		popUp.SetActive(true);
	}

	public static void hidePopUp() {
		popUp.SetActive(false);
	}

	public void NShidePopUp() {
		hidePopUp();
	}

	public void exitGame() {
		Application.Quit();
	}
}