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

[PrimaryObject]
public class LootChestScript : MonoBehaviour {

	Animator anim;
	[HideInInspector]
    public GameGenerator chestGenerator;
    public int totalItems;
    public bool guaranteeGoldItem;
	bool canHaveLoot;
	[HideInInspector]
    public List<GameObject> chestItems, potionItems, greyItems, greenItems, blueItems, purpleItems, goldItems;
    bool opened;
	string chestErrorMessage = "";

    PhotonView photonView;

	[HideInInspector]
    public int chestIndex;


    // Use this for initialization
    void Awake() {
        SetAllMeshCollidersToIgnoreRaycastsLayer(transform);
        opened = false;
		if (!canHaveLoot && SceneManager.GetActiveScene().name != "Tutorial Island") {
            MakeChildOfNearestLevelPrefab();
        }
        anim = GetComponent<Animator>();
        GameObject gameManager = GameObject.FindGameObjectWithTag("Game Generator");
        if (!gameManager)
            chestGenerator = FindObjectOfType<GameGenerator>();
        else {
            chestGenerator = gameManager.GetComponent<GameGenerator>();
            photonView = gameManager.GetComponent<PhotonView>();
        }

        CheckMyParents(transform);
        if (!chestGenerator || !canHaveLoot)
            return;

        InitializeChest();
        AssignChestItemsRandomly();
    }

	void ShowErrorMessage (string error) {
		if (error != chestErrorMessage) {
			chestErrorMessage = error;
			Debug.LogError (error);
			Debug.DebugBreak ();
		} else if (error == chestErrorMessage) {
			Debug.DebugBreak ();
		}
	}

    void SetAllMeshCollidersToIgnoreRaycastsLayer(Transform t) {
        for (int i = 0; i < t.childCount; i++) {
            SetAllMeshCollidersToIgnoreRaycastsLayer(t.GetChild(i));
        }

        MeshCollider collider = t.gameObject.GetComponent<MeshCollider>();
        if (collider) {
            t.gameObject.layer = LayerMask.NameToLayer("Ignore Raycast");
        }
    }

    public void MakeChildOfNearestLevelPrefab() {
        GameObject[] objs = GameObject.FindGameObjectsWithTag("Environment");
        GameObject nearest = null;
        float distanceToNearest = Mathf.Infinity;
        for (int i = 0; i < objs.Length; i++) {
            float distance = Vector3.Distance(objs[i].transform.position, transform.position);
            distance = Mathf.Abs(distance);

            if (distance < distanceToNearest) {
                nearest = objs[i];
                distanceToNearest = distance;
            }
        }
        if (nearest) {
            transform.parent = GetHighestParentWithTag(nearest.transform, "Environment");
        }
    }

    Transform GetHighestParentWithTag(Transform child, string tag) {
        if (child.transform.parent && child.transform.parent.tag == tag)
            return GetHighestParentWithTag(child.transform.parent, tag);
        else
            return child;
    }


    void OnDrawGizmos() {
        CheckMyParents(transform);
		if (transform.lossyScale != Vector3.one) {
			if (transform.parent) {
				Transform myParent = transform.parent;
				transform.parent = null;
				transform.localScale = Vector3.one;
				transform.parent = myParent;
			} else {
				transform.localScale = Vector3.one;
			}
		}
    }

    void CheckMyParents(Transform kid) {
		if (!kid.parent && SceneManager.GetActiveScene().name != "Tutorial Island") {
            canHaveLoot = false;
            ShowErrorMessage("The " + transform.name + " chest is not a child of a Level Prefab. Please click on this chest in the Hierarchy and drag it on top of your Level Prefab.");
        }
		else if (kid.parent && !kid.parent.GetComponent<LootRandomizer>() && SceneManager.GetActiveScene().name != "Tutorial Island") {
            if (!kid.parent.parent) {
                canHaveLoot = false;
				ShowErrorMessage("The " + transform.name + " chest is not a child of a Level Prefab. Please click on this chest in the Hierarchy and drag it on top of your Level Prefab.");
            }
            else {
                CheckMyParents(kid.parent);
            }
        }
        else {
			canHaveLoot = true;
			chestErrorMessage = "";
        }
    }

    void OnTriggerStay(Collider col) {
        if (col.gameObject.tag == "Player") {
            if (col.gameObject.GetPhotonView() && !col.gameObject.GetPhotonView().IsMine && PhotonNetwork.IsConnected)
                return;

            if (Input.GetKeyDown(KeyCode.E) && !opened) {
                anim.SetTrigger("OpenChest");
                if (PhotonNetwork.IsConnected && photonView) {
                    Debug.Log("Chest Opened: " + chestIndex);
                    PhotonDebugger.IncrementNumberOfMessages("open chest");
                    photonView.RPC("ChestOpened", RpcTarget.OthersBuffered, chestIndex);
                }
            }
        }
    }

    public void ReleaseTheItems() {
        if (opened) {
            return;
        }
        if (PhotonNetwork.IsConnected && !PhotonNetwork.IsMasterClient) {
            anim.SetTrigger("OpenChest");
            opened = true;
            return;
        }
        anim.SetTrigger("OpenChest");
        opened = true;
        int[] viewIDs = new int[chestItems.Count];
        for (int i = 0; i < chestItems.Count; i++) {
            Vector3 direction = Quaternion.Euler(0, (i * 45f) + -72f, 0) * transform.forward;

            Vector3 pos = transform.position;
            pos.y += 1.75f;

            GameObject item = null;
            if (PhotonNetwork.IsConnected && PhotonNetwork.IsMasterClient) {
                string resourcePath = "";

                if (chestItems[i].name.Contains("Potion"))
                    resourcePath = "Potions/";
                else
                    resourcePath = "Wands/";

                item = PhotonNetwork.InstantiateSceneObject(resourcePath + chestItems[i].name, pos, chestItems[i].transform.rotation);
                viewIDs[i] = item.GetPhotonView().ViewID;
            }
            else if (!PhotonNetwork.IsConnected) {
                item = Instantiate(chestItems[i], pos, chestItems[i].transform.rotation);
            }

            if (item)
                StartCoroutine(WaitAndForceItemsOut(item.GetComponent<ItemScript>(), direction));
        }

        if (PhotonNetwork.IsConnected && photonView) {
            PhotonDebugger.IncrementNumberOfMessages("open chest");
            photonView.RPC("ForceItemsOutOfChest", RpcTarget.OthersBuffered, chestIndex, (object)viewIDs);
        }
    }

    IEnumerator WaitAndForceItemsOut(ItemScript item, Vector3 dir) {
        yield return new WaitForSecondsRealtime(0.5f);
        item.AddForceToItem(450f, dir);
    }

    void InitializeChest() {
        chestItems = new List<GameObject>();
        greyItems = new List<GameObject>();
        greenItems = new List<GameObject>();
        blueItems = new List<GameObject>();
        purpleItems = new List<GameObject>();
        goldItems = new List<GameObject>();

        chestItems.Clear();
        greyItems.Clear();
        greenItems.Clear();
        blueItems.Clear();
        purpleItems.Clear();
        goldItems.Clear();

        AddItemsByRarity();
    }

    void AddItemsByRarity() {
        AddWandsByRarity();
        AddPotions();
    }

    void AddWandsByRarity() {
        for (int i = 0; i < chestGenerator.generator.wands.Count; i++) {
            if (!chestGenerator.generator.wands[i]) {
                Debug.LogError("no wand ?");
                continue;
            }
            if (!chestGenerator.generator.wands[i].GetComponent<WandScript>()) {
                Debug.LogError("no wand script?");
                continue;
            }
            if (chestGenerator.generator.wands[i].GetComponent<WandScript>().wand.rarity == Rarity.Basic) {
                greyItems.Add(chestGenerator.generator.wands[i]);
            }
            else if (chestGenerator.generator.wands[i].GetComponent<WandScript>().wand.rarity == Rarity.Normal) {
                greenItems.Add(chestGenerator.generator.wands[i]);
            }
            else if (chestGenerator.generator.wands[i].GetComponent<WandScript>().wand.rarity == Rarity.Unique) {
                blueItems.Add(chestGenerator.generator.wands[i]);
            }
            else if (chestGenerator.generator.wands[i].GetComponent<WandScript>().wand.rarity == Rarity.Super) {
                purpleItems.Add(chestGenerator.generator.wands[i]);
            }
            else if (chestGenerator.generator.wands[i].GetComponent<WandScript>().wand.rarity == Rarity.Extraordinary) {
                goldItems.Add(chestGenerator.generator.wands[i]);
            }
        }
    }

    void AddPotions() {
        potionItems = chestGenerator.potions;
    }


    void AssignChestItemsRandomly() {
        int[] rarities = new int[totalItems];
        int[] indexes = new int[totalItems];

        int potionCount = 0;

        if (guaranteeGoldItem) {
            int randomGold = Random.Range(0, goldItems.Count);
            rarities[0] = (int)Rarity.Extraordinary;
            indexes[0] = randomGold;

            chestItems.Add(goldItems[randomGold]);
            goldItems.Clear();
        }

        while (chestItems.Count != totalItems) {
            int potionCheck = (potionCount < 2) ? Random.Range(0, 4) : 0;

            int randomVal = (guaranteeGoldItem) ? Random.Range(1, 96) : Random.Range(1, 101);
            if (randomVal <= 35 && greyItems.Count != 0) {
                randomVal = Random.Range(0, greyItems.Count);
                rarities[chestItems.Count] = (int)Rarity.Basic;
                indexes[chestItems.Count] = randomVal;
                AssignChestItem(Rarity.Basic, randomVal);
            }
            else if (randomVal <= 65 && greenItems.Count != 0) {
                if (potionCheck == 3) {
                    AddItemToChestFromList(ref potionItems, 0, false);
                    potionCount++;
                    continue;
                }
                randomVal = Random.Range(0, greenItems.Count);
                rarities[chestItems.Count] = (int)Rarity.Normal;
                indexes[chestItems.Count] = randomVal;
                AssignChestItem(Rarity.Normal, randomVal);
            }
            else if (randomVal <= 85 && blueItems.Count != 0) {
                if (potionCheck == 3) {
                    AddItemToChestFromList(ref potionItems, 1, false);
                    potionCount++;
                    continue;
                }
                randomVal = Random.Range(0, blueItems.Count);
                rarities[chestItems.Count] = (int)Rarity.Unique;
                indexes[chestItems.Count] = randomVal;
                AssignChestItem(Rarity.Unique, randomVal);
            }
            else if (randomVal <= 95 && purpleItems.Count != 0) {
                if (potionCheck == 3) {
                    AddItemToChestFromList(ref potionItems, 2, false);
                    potionCount++;
                    continue;
                }
                randomVal = Random.Range(0, purpleItems.Count);
                rarities[chestItems.Count] = (int)Rarity.Super;
                indexes[chestItems.Count] = randomVal;
                AssignChestItem(Rarity.Super, randomVal);
            }
            else if (goldItems.Count != 0) {
                if (potionCheck == 3) {
                    AddItemToChestFromList(ref potionItems, 3, false);
                    potionCount++;
                    continue;
                }
                randomVal = Random.Range(0, goldItems.Count);
                rarities[chestItems.Count] = (int)Rarity.Extraordinary;
                indexes[chestItems.Count] = randomVal;
                AssignChestItem(Rarity.Extraordinary, randomVal);
            }
            else {
                break;
            }
        }

        //if (PhotonNetwork.IsConnected && PhotonNetwork.IsMasterClient && photonView) {
        //    photonView.RPC("AssignChestItemsRemote", RpcTarget.OthersBuffered, (object)rarities, (object)indexes);
        //}
    }

    void AssignChestItemsRemote(int[] rarities, int[] indexes) {
        for (int i = 0; i < rarities.Length; i++) {
            AssignChestItem((Rarity)rarities[i], indexes[i]);
        }
    }

    void AssignChestItem(Rarity rarity, int index) {
        switch (rarity) {
            case Rarity.Basic:
                AddItemToChestFromList(ref greyItems, index, true);
                break;
            case Rarity.Normal:
                AddItemToChestFromList(ref greenItems, index, true);
                break;
            case Rarity.Unique:
                AddItemToChestFromList(ref blueItems, index, true);
                break;
            case Rarity.Super:
                AddItemToChestFromList(ref purpleItems, index, true);
                break;
            case Rarity.Extraordinary:
                AddItemToChestFromList(ref goldItems, index, true);
                break;
        }
    }

    void AddItemToChestFromList(ref List<GameObject> rarityItemsList, int index, bool removeItem) {
        chestItems.Add(rarityItemsList[index]);
        if (removeItem) {
            rarityItemsList.Remove(rarityItemsList[index]);
        }
    }

    public void ForceItemsOutOfChest(int[] viewIDs) {
        anim.SetTrigger("OpenChest");
        if (PhotonNetwork.IsConnected && !PhotonNetwork.IsMasterClient) {
            for (int i = 0; i < viewIDs.Length; i++) {
                PhotonView view = PhotonView.Find(viewIDs[i]);
                if (!view) {
                    Debug.Log("Passed an invalid viewID");
                }
                GameObject item = view.gameObject;

                Vector3 direction = Quaternion.Euler(0, (i * 45f) + -72f, 0) * transform.forward;
                StartCoroutine(WaitAndForceItemsOut(item.GetComponent<ItemScript>(), direction));
            }
        }
    }

    [ExecuteInEditMode]
    void OnValidate() {
        if (totalItems > 5)
            totalItems = 5;
        if (totalItems < 2)
            totalItems = 2;
    }
}
