#if UNITY_EDITOR
using UnityEditor;
#endif

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using System.Linq;
using UnityEngine.SceneManagement;

public class GameGenerator : MonoBehaviour {

    [SerializeField]
    public Generator generator;

	public bool generateLevels = true;

    List<Vector3> levelLocations;
    public List<int> levels;

	// Potions aren't collected like other assets, but are placed on the generator beforehand
	// The generator needs to become a prefab for this to work.
	//[HideInInspector]
	public List<GameObject> potions;

    static int oldSeed;

    List<LootChestScript> lootChests;


    PhotonView view;

    public GridHandler gridHandler;

#if UNITY_EDITOR
    [ContextMenu("Collect Assets")]
    void CollectAssets() {

        generator.Clear();
        generator = new Generator();

        List<string[]> myGuids = new List<string[]>();
        string[] lGuids = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/Levels" });
        string[] hGuids = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/Hairstyles" });
        string[] wGuids = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/Prefabs/Resources/Wands" });
		string[] tlGuids = AssetDatabase.FindAssets("t:Prefab", new string[] { "Assets/Do Not Edit OR Delete/Test Levels" });
        string[] mGuids = AssetDatabase.FindAssets("t:Material", new string[] { "Assets/HairColors" });
		string[] tGuids = AssetDatabase.FindAssets("t:Texture", new string[] { "Assets/Textures/Player Textures" });

        myGuids.Add(lGuids);
        myGuids.Add(hGuids);
        myGuids.Add(wGuids);
		myGuids.Add(tlGuids);
        myGuids.Add(mGuids);
        myGuids.Add(tGuids);

        for (int i = 0; i < myGuids.Count; i++) {
            for (int j = 0; j < myGuids[i].Length; j++) {
                string myAssetPath = AssetDatabase.GUIDToAssetPath(myGuids[i][j]);

                if (i < 4)
                    generator.prefabObjects[i].Add(AssetDatabase.LoadAssetAtPath<GameObject>(myAssetPath));
                else if (i == 4)
                    generator.hairColors.Add(AssetDatabase.LoadAssetAtPath<Material>(myAssetPath));
				else
                    generator.playerTextures.Add(AssetDatabase.LoadAssetAtPath<Texture>(myAssetPath));
            }
        }
    }

#endif

    // Use this for initialization
    void Awake() {
        view = GetComponent<PhotonView>();
        lootChests = new List<LootChestScript>();
		if (CheckForAssets ()) {
			int randomSeed = GenerateSeed ();

			if (view && PhotonNetwork.IsConnected && SceneManager.GetActiveScene ().name != "Launcher") {
				SetRandomSeed (oldSeed);
				//StartCoroutine(SendRandomSeed(randomSeed));
				//PhotonDebugger.IncrementNumberOfMessages("seed");
				//view.RPC("SetRandomSeed", RpcTarget.OthersBuffered, randomSeed);

				//SetRandomSeed(randomSeed);
				return;
			} else if (!PhotonNetwork.IsConnected && SceneManager.GetActiveScene ().name != "Launcher") {
				SetRandomSeed (randomSeed);
			}
		}
    }
#if UNITY_EDITOR
	void OnValidate () {
		CollectAssets ();
	}
#endif


    public void SendRandomSeed(int randomSeed) {
        PhotonDebugger.IncrementNumberOfMessages("seed");
        if (!view)
            view = GetComponent<PhotonView>();
        view.RPC("SetRandomSeed", RpcTarget.OthersBuffered, randomSeed);

        SetRandomSeed(randomSeed);
    }


    [PunRPC]
    void SetRandomSeed(int randomSeed) {
        oldSeed = randomSeed;
        Random.InitState(randomSeed);

        BeginGeneratingLevels();
    }

    public static int GetOldRandomSeed() {
        return oldSeed;
    }

    public static void SetRandomToOldSeed() {
        Random.InitState(oldSeed);
    }

    public int GenerateSeed() {
        int randomSeedMilli = System.DateTime.Now.Millisecond;
        int randomSeedSecond = System.DateTime.Now.Second;
        int randomSeedMinutes = System.DateTime.Now.Minute;

        string randomSeedString = randomSeedMinutes.ToString("00") + randomSeedSecond.ToString("00") + randomSeedMilli.ToString("0000");

        return int.Parse(randomSeedString);
    }

    void GenerateLevel() {
        levelLocations = new List<Vector3>();

        for (int i = -1; i < 2; i++) {
            for (int j = -1; j < 2; j++) {
                levelLocations.Add(new Vector3(400 * i, 0, 400 * j));
            }
        }

        levels = Enumerable.Range(0, generator.levels.Count).ToList();

        int[] levelIndexes = new int[9];
        for (int k = 0; k < 9; k++) {
            int index = Random.Range(0, levels.Count);
            levelIndexes[k] = levels[index];
            levels.Remove(levels[index]);
        }

        GenerateLevelFromIndexes(levelIndexes);
    }

    void BeginGeneratingLevels() {
        MaterialDrainValue.ResetCurrentMaterialIndex();
        if (generateLevels)
            if (SceneManager.GetActiveScene().name != "Launcher")
                GenerateLevel();

        StartCoroutine(gridHandler.InitializeGrid());
    }

    void GenerateLevelFromIndexes(int[] indexes) {
        for (int k = 0; k < 9; k++) {
            //Debug.LogError("indexes[" + k + "]: " + indexes[k]);
            GameObject level = Instantiate(generator.levels[indexes[k]], levelLocations[k], Quaternion.Euler(Vector3.zero), transform);
            //generator.levels.Remove(generator.levels[indexes[k]]);
            
            SetLayerAndTagRecursively(level, "Environment");
        }
		transform.position = new Vector3 (-10f, 0f, -10f);
    }

    void SetLayerAndTagRecursively(GameObject obj, string layerName) {
        int layerIndex = LayerMask.NameToLayer(layerName);
        if (null == obj) {
            return;
        }

        obj.layer = layerIndex;
        obj.tag = layerName;

        foreach (Transform child in obj.transform) {
			if (null == child || child.gameObject.layer == LayerMask.NameToLayer ("Level Editor") || child.gameObject.tag == "Wand" || child.gameObject.tag == "Potion") {
                continue;
			}
            SetLayerAndTagRecursively(child.gameObject, layerName);
        }
    }

    bool CheckForAssets() {
        if (generator.levels.Count < 9) {
			for (int i = 0; i < generator.testLevels.Count; i++) {
				generator.levels.Add (generator.testLevels [i]);
				if (generator.levels.Count == 9)
					break;
			}
			if (generator.levels.Count < 9) {
				Debug.LogError ("You need at least nine (9) levels to generate a map. Please add more levels (Prefab) to the Assets/Levels folder.");
				return false;
			}
        }
        if (generator.wands.Count < 1) {
            Debug.LogError("You need at least one (1) wand to generate a map. Please add more wands (Prefab) to the Assets/Wands folder.");
			return false;
        }
        if (generator.hairStyles.Count < 1) {
            Debug.LogError("You need at least one (1) hairstyle to generate a player. Please add more hairstyles (Prefab) to the Assets/HairStyles folder.");
			return false;
        }
        if (generator.hairColors.Count < 1) {
            Debug.LogError("You need at least one (1) haircolor to generate a player. Please add more haircolors (Material) to the Assets/HairColors folder.");
			return false;
        }
        if (generator.playerTextures.Count < 1) {
            Debug.LogError("You need at least one (1) player texture to generate a player. Please add more player textures (Texture) to the Assets/Textures/PlayerTextures folder.");
			return false;
        }

		return true;
    }

    // Update is called once per frame
    void Update() {
        // might do something here to automate asset collection
    }


    // LOOT CHEST RPCs
    [PunRPC]
    public void ChestOpened(int chestIndex) {
        if (lootChests.Count <= 0) {
            GetLootChests();
        }

        LootChestScript chestScript = GetChest(chestIndex);
        if (!chestScript)
            return;
        
        chestScript.ReleaseTheItems();
    }

    [PunRPC]
    public void ForceItemsOutOfChest(int chestIndex, int[] viewIDs) {
        if (lootChests.Count <= 0) {
            GetLootChests();
        }
        LootChestScript chestScript = GetChest(chestIndex);
        if (!chestScript)
            return;

        chestScript.ForceItemsOutOfChest(viewIDs);
    }

    LootChestScript GetChest(int chestIndex) {
        if (lootChests.Count <= 0) {
            Debug.Log("No Chests are in the list");
            return null;
        }

        LootChestScript script = lootChests.Find(chest => chest.chestIndex == chestIndex);

        if (!script) {
            Debug.Log("No chest found with that index");
            return null;
        }

        return script;
    }

    void GetLootChests() {
        GameObject[] objs = GameObject.FindGameObjectsWithTag("LootChest");
        for(int i = 0; i < objs.Length; i++) {
            LootChestScript script = objs[i].GetComponent<LootChestScript>();
            if(script)
                lootChests.Add(script);
        }
        
    }
}

[System.Serializable]
public class Generator {
	[ReadOnly]
    public List<List<GameObject>> prefabObjects;
	[ReadOnly]
    public List<GameObject> levels, hairStyles, wands, testLevels;

	[ReadOnly]
    public List<Material> hairColors;
	[ReadOnly]
    public List<Texture> playerTextures;


    public Generator() {

        levels = new List<GameObject>();
        hairStyles = new List<GameObject>();
        wands = new List<GameObject>();
		testLevels = new List<GameObject>();

        hairColors = new List<Material>();
        playerTextures = new List<Texture>();

        prefabObjects = new List<List<GameObject>>();
        prefabObjects.Add(levels);
        prefabObjects.Add(hairStyles);
        prefabObjects.Add(wands);
		prefabObjects.Add(testLevels);

    }

    public void Clear() {
        levels.Clear();
        hairStyles.Clear();
		wands.Clear();
		testLevels.Clear();
        hairColors.Clear();
        playerTextures.Clear();
        prefabObjects.Clear();
    }
}
