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

public partial class CityGen : MonoBehaviour {

	[System.Serializable]
	public class Pin {
		public Vector2 position;
		public Vector2 radius;

		public void genDemo(int id){
			this.position.x=id;
			this.position.y=id;

			this.radius.x=1;
			this.radius.y=1;
		}
	}

	[System.Serializable]
	public class ZoneType {
		public string zoneName;
		public List<int> prefabsIdentifier = new List<int>();
		public Pin[] pins;

		public System.Func<Vector3, Vector3> zoneRule;

		public ZoneType(string name, System.Func<Vector3, Vector3> rule){
			this.zoneName = name;
			this.zoneRule = rule;
		}

		public int getDevelopmentIndex(int prefabId){
			int index = this.prefabsIdentifier.FindIndex(
				id => id == Math.Abs(prefabId)
			);

			if(index == -1){
				print("WARN:\t Couldn't find prefab with id " + prefabId +" in " + zoneName);
			}

			return index;
		}

		public void genDemo(int id){
			this.pins = new Pin[1];
			this.pins[0] = new Pin();
			this.pins[0].genDemo(id);
		}

		public void addPrefabsIdentifiers(int id){
			// Fill the array with id of prefabs within the Zone.
			prefabsIdentifier.Add(id);
		}
	}

	[System.Serializable]
	public class Prefab {
		[HideInInspector]
		public int identifier;
		public GameObject prefab;
		[HideInInspector]
		public Vector2 footprint;
		[HideInInspector]
		public Vector2 offsetForFootprint;

		public void genDemo(int id, string prefabPath){
			this.identifier = id;

			//TODO:add prefab TEST
			this.prefab = Resources.Load<GameObject>(prefabPath);

			this.footprint.x =  prefab.GetComponentInChildren<Renderer>().bounds.max.x +0.5f  -  prefab.GetComponentInChildren<Renderer>().bounds.min.x;
			this.footprint.y =  prefab.GetComponentInChildren<Renderer>().bounds.max.z +0.5f -  prefab.GetComponentInChildren<Renderer>().bounds.min.z;
		
			this.offsetForFootprint.x = prefab.GetComponentInChildren<Renderer>().bounds.min.x;
			this.offsetForFootprint.y = prefab.GetComponentInChildren<Renderer>().bounds.min.y; 
		}
	}

	[System.Serializable]
	public class Tile {
		public int height;
		public Vector3 orientation;
		public string zoneName;
		public int development;		// How developed is the building / equal to index in the zoneType prefabs

		public Tile(){
			this.zoneName = "Empty";
		}

		public Tile(Tile original){
			this.height = original.height;
			this.orientation = original.orientation;
			this.zoneName = original.zoneName;
			this.development = original.development;
		}

		public Tile updateTile(Vector3 value){
			Tile newTile = new Tile(this);
			// update zoneName

			float mostProminent = 0;
			for(int i =0; i < 3; i++ ){
				if(value[i] > mostProminent){
					mostProminent = value[i];
				}
			}

		    if(value.x != 0){
	            newTile.zoneName = "Recidential"; 
	            newTile.development = (int)value.x;
		    } else if (value.y != 0){
	            newTile.zoneName = "Commercial"; 
	            newTile.development = (int)value.y;
		    } else if (value.z != 0){
	           	newTile.zoneName = "Industrial"; 
	            newTile.development = (int)value.z;
		    } else {
		    	newTile.zoneName = "Empty";
	            newTile.development = 0;
		    }

		    return newTile;
		}

		public void genDemo(int id, string zoneName){
			this.height = 4 + id;
			this.zoneName = zoneName;
			this.orientation = new Vector3(1.0f, 0.0f, 0.0f);
			this.development = id;
		}
	}

	[System.Serializable]
	public class City {
		public ZoneType[] zoneTypes;
		public List<Prefab> prefabs;
		public Vector2 layoutDimension;
		[HideInInspector]
		public Tile[,] layout;
		//public Tile[] tiles;

		public Prefab findPrefabById(int id){
			foreach(Prefab prefab in prefabs){
				if(prefab.identifier == id){
					return prefab;
				}
			}
			print("WARN:\t No prefab with id: " + id);
			return null;
		}

		public City() {
			this.zoneTypes = new ZoneType[4];

			this.zoneTypes[0] = new ZoneType(
				"Recidential", 
				(Vector3 tileInfo) => {
					tileInfo.x += 0.5f;
					tileInfo.y += 0.25f;
					tileInfo.z += -0.5f;
					return tileInfo;
				} 
			);

			this.zoneTypes[1] = new ZoneType(
				"Commercial",
				(Vector3 tileInfo) => {
					tileInfo.x += 0.25f;
					tileInfo.y += 0.25f;
					tileInfo.z += -0.25f;
					return tileInfo;
				}
			);

			this.zoneTypes[2] = new ZoneType(
				"Industrial",
				(Vector3 tileInfo) => {
					tileInfo.x += -0.25f;
					tileInfo.y += -0.25f;
					tileInfo.z += 0.75f;
					return tileInfo;
				}
			);

			this.zoneTypes[3] = new ZoneType(
				"Empty",
				(Vector3 tileInfo) => {
					return tileInfo;
					tileInfo.x += 0.25f;
					tileInfo.y += -0.75f;
					tileInfo.z += -0.75f;
				}
			);

			prefabs = new List<Prefab>();
		}

		public ZoneType findZoneByName(string zoneName){
			foreach(ZoneType zoneType in zoneTypes){
				if(zoneType.zoneName == zoneName){
					return zoneType;
				}
			}

			print("WARN:\t Can't find zone: " + zoneName);
			return null;
		}

		public void genDemo(){

			for(int i = 0; i < this.zoneTypes.Length; i++){
				zoneTypes[i].genDemo(i);
			}
 
			prefabs.Add(new Prefab());
			prefabs[0].genDemo(1, "Cubes/SmallCube");
			prefabs.Add(new Prefab());
			prefabs[1].genDemo(2, "Cubes/SmallCube(1)");
			prefabs.Add(new Prefab());
			prefabs[2].genDemo(3, "Cubes/SmallCube(2)");
			prefabs.Add(new Prefab());
			prefabs[3].genDemo(4, "Cubes/MediumCube");
			prefabs.Add(new Prefab());
			prefabs[4].genDemo(5, "Cubes/MediumCube(1)");
			prefabs.Add(new Prefab());
			prefabs[5].genDemo(6, "Cubes/MediumCube(2)");
			prefabs.Add(new Prefab());
			prefabs[6].genDemo(7, "Cubes/LargeCube");
			prefabs.Add(new Prefab());
			prefabs[7].genDemo(8, "Cubes/LargeCube(1)");
			prefabs.Add(new Prefab());
			prefabs[8].genDemo(9, "Cubes/LargeCube(2)");
		
			for(int i = 0; i < 3; i++){
				for(int j = 0; j < 3; j++){
					this.zoneTypes[i].addPrefabsIdentifiers(
						prefabs[j+i*3].identifier
					);
				}
			}

			this.layoutDimension.x = 15;
			this.layoutDimension.y = 15;

			this.layout = new Tile[(int)this.layoutDimension.x, (int)this.layoutDimension.y];//[(int) this.layoutDimension.x,(int) this.layoutDimension.y];
			
			for(int i =0; i < this.layoutDimension.x; i++){
				for(int j = 0; j < this.layoutDimension.y; j++){
					this.layout[j, i] = new Tile();		
				}
			}

			string[] zoneNames = {"Recidential", "Commercial", "Industrial"};

			for(int i = 0; i < 3; i++){
				for(int j = 0; j < 3; j++){
					this.layout[j,i].genDemo(1+j+i*3, zoneNames[j]);//= 1+i+j*3;
				}
			}

			//this.tiles = new Tile[25];

			/*for(int i =0; i < this.layoutDimension.x * this.layoutDimension.y; i++){
				this.tiles[i] = new Tile();
			}
			
			string[] zoneNames = {"Recidential", "Commercial", "Industrial"};
			for(int y = 0; y < 3; y++){
				for(int x = 0; x < 3; x++){
					this.tiles[(int)(x + y * this.layoutDimension.x)].genDemo(1+x+y*3,zoneNames[x]);
				}
			}*/
		}


	}


	public City city;
	private string jsonFilePath = "/CityData/data.json";

	// Use this for initialization
	void Start () {
		city = new City();
		city.genDemo();
		save();
		load();
		simulate();
	}
	
	// Update is called once per frame
	void Update () {
		
	}

	// Save the data about city.
	public void save (string fileName = "data") {
		// Covert city object to json.
		string cityDataAsJson = JsonUtility.ToJson(this.city);
		//print(Application.persistentDataPath + this.jsonFilePath);
		
		// Creates a directory in local disk.
		System.IO.Directory.CreateDirectory(Application.persistentDataPath + "/CityData");
		// Write all the data to a file.
		System.IO.File.WriteAllText(Application.persistentDataPath + this.jsonFilePath, cityDataAsJson);
	}

	// Load the data about city.
	public bool load(string fileName = "data"){
		string cityDataAsJson = System.IO.File.ReadAllText(Application.persistentDataPath + this.jsonFilePath);
		city = JsonUtility.FromJson<City>(cityDataAsJson);
		return (city != null);
	}
}
