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

public class MaterialDrainValue : MonoBehaviour {

    static int currentIndex;
	[HideInInspector]
    public int index;
	[HideInInspector]
    public float drainValue = 0f, gScale = 0f, drain = 0.5f, timer = 0f, totalEnergy, objVolume = 0, cubedRoot;
	bool drained = false, timerStart = false;
	[HideInInspector]
	public Material[] objMats, energyMats;
	[HideInInspector]
	public List<Color> objColors;
	bool llama = false;

    Build buildScript;

    // This needs to be in Start because it has to come after the color changer settings
    void Start () {
        index = currentIndex++;

		if (GetComponent<Tinkercad> ()) {
			TinkerSetUp ();
		} else if (GetComponent<ColorChanger> ()) {
			ColorChangerSetUp ();
		} else if (GetComponent<Llama> ()) {
			LlamaSetUp ();
		} else {
			ColorChangerSetUp ();
		}

        GameObject buildMan = GameObject.FindGameObjectWithTag("Build Manager");
        if (!buildMan) {
            Debug.Log("No Build Manager Found");
            return;
        }
        buildScript = buildMan.GetComponent<Build>();
    }

    public static void ResetCurrentMaterialIndex() {
        currentIndex = 0;
    }

    public static MaterialDrainValue GetMaterialDrainValue(int index) {
        MaterialDrainValue[] materialDrainValues = Resources.FindObjectsOfTypeAll<MaterialDrainValue>();
        for(int i = 0; i < materialDrainValues.Length; i++) {
            if (materialDrainValues[i].index == index) {
                return materialDrainValues[i];
            }
        }
        return null;
    }

	void TinkerSetUp () {
		
		objMats = new Material[GetComponent<Tinkercad> ().rends.Length];
		energyMats = new Material[GetComponent<Tinkercad> ().rends.Length];
		objColors = new List<Color> ();
		objColors.Clear ();
		Vector3 lhw = Vector3.one;
		bool gotVolume = false;

		for (int i = 0; i < GetComponent<Tinkercad> ().rends.Length; i++) {
			objMats [i] = GetComponent<Tinkercad> ().rends [i].material;
			energyMats[i] = GetComponent<Tinkercad> ().rends [i].sharedMaterial;
			objColors.Add (GetComponent <Tinkercad> ().totalColors [i]);
			if (gotVolume)
				continue;
			for (int c = 0; c < transform.childCount; c++) {
				if (transform.GetChild (i) != transform.GetChild (c) && transform.GetChild (i).GetComponent<MeshRenderer> () && transform.GetChild (c).GetComponent<MeshRenderer> ()) {
					if (transform.GetChild (i).GetComponent<MeshRenderer> ().bounds.size == transform.GetChild (c).GetComponent<MeshRenderer> ().bounds.size) {
						lhw = transform.GetChild (i).GetComponent<MeshRenderer> ().bounds.size;
						gotVolume = true;
						break;
					}
				}
			}
		}

		objVolume = lhw.x * lhw.y * lhw.z;
		cubedRoot = Mathf.Pow (objVolume, 1f / 3f);
		totalEnergy = ((69f / 1672f) * Mathf.Pow (cubedRoot, 2)) + ((1467f / 1672f) * cubedRoot) - (175f / 418f);
	}

	void ColorChangerSetUp () {
		if (!GetComponent<MeshRenderer> ())
			return;

		objMats = GetComponent<MeshRenderer> ().materials;
		energyMats = GetComponent<MeshRenderer> ().sharedMaterials;
		objColors = new List<Color> ();
		objColors.Clear ();
		int matInt = 0;
		foreach (Material mat in objMats) {
			if (mat.shader != Shader.Find ("Custom/BattleRoyaleDefault")) {
				mat.shader = Shader.Find ("Custom/BattleRoyaleDefault");
			}

			if (!GetComponent <ColorChanger> ()) {
				objColors.Add (mat.color);
			} else {
				objColors.Add (GetComponent <ColorChanger> ().totalColors [matInt]);
			}
			matInt++;
		}

		Vector3 lhw = GetComponent<MeshRenderer> ().bounds.size;
		objVolume = lhw.x * lhw.y * lhw.z;
		cubedRoot = Mathf.Pow (objVolume, 1f / 3f);
		totalEnergy = ((69f / 1672f) * Mathf.Pow (cubedRoot, 2)) + ((1467f / 1672f) * cubedRoot) - (175f / 418f);
	}

	void LlamaSetUp () {
		objMats = GetComponent<MeshRenderer> ().materials;
		energyMats = GetComponent<MeshRenderer> ().sharedMaterials;
		objColors = new List<Color> ();
		objColors.Clear ();
		objColors.Add (GetComponent<MeshRenderer> ().material.color);
		totalEnergy = 250f;

		llama = true;
	}


	public bool DrainColor (PlayerBase player) {
		if (drained) {
			if (timerStart) {
				//Debug.Log (timer);
				timerStart = false;
			}
			return false;
		}

		if (player.gainedEnergy + player.buildEnergies.TotalEnergy () >= 500)
			return false;

		timerStart = true;

        /*
		if (objVolume <= 1f)
			gScale += drainRate / 1000 / 0.25f;// * Time.deltaTime;
		else
			gScale += drainRate / 1000 / Mathf.Log(objVolume);// * Time.deltaTime;
		*/
		float inc = (llama) ? 15f : 5f;
		float change = (Time.deltaTime * inc) / totalEnergy;

		if (buildScript) {
			buildScript.AddGrayScaleUpdate (index, change);
		}

		player.gainedEnergy += (Time.deltaTime * inc);
		if (player.gainedEnergy < 0)
			player.gainedEnergy = 0;

        Drain(change);

		return true;
	}

    public void Drain(float change) {
        gScale += change;

        if (gScale > 1)
            gScale = 1;

        foreach (Material mat in objMats) {
            if (!mat) {
                Debug.LogError("no material?");
            }
            else
                mat.SetFloat("_Grayscale", gScale);
        }
    }

	public void GetEnergy (PlayerBase player) {

		player.gainedEnergy = player.gainedEnergy / (float)objColors.Count;

		if (player.buildEnergies.list.Count > 0) {
			int playerEnergies = player.buildEnergies.list.Count;
			for (int c = 0; c < objColors.Count; c++) {
				bool exists = false;
				for (int i = 0; i < playerEnergies; i++) {
					if (player.buildEnergies.list[i].color == objColors[c]) {
						player.buildEnergies.list[i].AddEnergy (player.gainedEnergy);
						exists = true;
						break;
					} else {
						//Debug.Log (player.buildEnergies.list[i].material.name + ", " + energyMats[m].name);
					}
				}
				if(!exists)
					player.buildEnergies.list.Add (new Energy (objColors[c], player.gainedEnergy));
			}
		} else {
			foreach (Color col in objColors)
				player.buildEnergies.list.Add (new Energy (col, player.gainedEnergy));
		}

		player.gainedEnergy = 0f;
	}

	/* Keep as reference for now
	public void GetEnergyOld (PlayerBase player) {

		player.gainedEnergy = player.gainedEnergy / (float)energyMats.Length;

		if (player.buildEnergies.list.Count > 0) {
			int playerEnergies = player.buildEnergies.list.Count;
			for (int m = 0; m < energyMats.Length; m++) {
				bool exists = false;
				for (int i = 0; i < playerEnergies; i++) {
					if (player.buildEnergies.list[i].material.name == energyMats[m].name) {
						player.buildEnergies.list[i].AddEnergy (player.gainedEnergy);
						exists = true;
						break;
					} else {
						//Debug.Log (player.buildEnergies.list[i].material.name + ", " + energyMats[m].name);
					}
				}
				if(!exists)
					player.buildEnergies.list.Add (new Energy (energyMats[m], player.gainedEnergy));
			}
		} else {
			foreach (Material mat in energyMats)
				player.buildEnergies.list.Add (new Energy (mat, player.gainedEnergy));
		}

		player.gainedEnergy = 0f;
	}
	*/

	// Update is called once per frame
	void Update () {
		if (gScale >= 1f)
			drained = true;

		// used for debugging purposes, to be removed later
		if (timerStart)
			timer += Time.deltaTime;
	}
}

/// <summary>
/// Base class for storing energy.
/// </summary>
[System.Serializable]
public class Energy {
	[ReadOnly]
	public Material material;
	[ReadOnly]
	public Color color;
	[ReadOnly]
	public float amount;


	/// <summary>
	/// Creates new energy based on a material (mat), color (col), and float value (val).
	/// </summary>
	public Energy (Material mat, Color col, float val){
		material = mat;
		amount = val;
		color = col;
	}

	/// <summary>
	/// Creates new energy based on a material (mat) and float value (val).
	/// </summary>
	public Energy (Material mat, float val){
		material = mat;
		amount = val;
	}

	/// <summary>
	/// Creates new energy based on a color (col) and float value (val).
	/// </summary>
	public Energy (Color col, float val){
		color = col;
		amount = val;
	}

	/// <summary>
	/// adds more value (val) to an existing energy
	/// </summary>
	public void AddEnergy (float val){
		amount += val;
	}

    /// <summary>
	/// subtracts value (val) from an existing energy
	/// </summary>
    public void SubtractEnergy(float val) {
        amount -= val;
    }
}

[System.Serializable]
public class EnergyList {
	[ReadOnly]
	public List<Energy> list;

	public float TotalEnergy () {
		float totalEnergy = 0;
		for (int i = 0; i < list.Count; i++) {
			totalEnergy += list [i].amount;
		}
		return totalEnergy;
	}

    public bool HasEnoughEnergy(float required) {
        float total = 0f;
        for(int i = 0; i < list.Count; i++) {
            total += list[i].amount;
            if (total > required)
                return true;
        }
        return false;
    }

    public void SubtractFromList(float val) {
        for(int i = list.Count - 1; i >= 0; i--) {
            if(list[i].amount > val) {
                list[i].SubtractEnergy(val);
                val = 0;
                if (list[i].amount <= 0)
                    list.RemoveAt(i);
                return;
            }
            
            float remainder = val - list[i].amount;
            list[i].SubtractEnergy(list[i].amount);
            val = remainder;
            list.RemoveAt(i);
        }
    }
}
