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

public class BuildHandler : MonoBehaviour {

    #region Inspector Reference

    [SerializeField] GridRecognition m_gridRecognition;		// reference to the Grid Recognition on the Main Camera

	private Transform m_shadowGizmo;						// position of "shadow preview" of the buildable object
    [SerializeField] Color m_active;						// active color for the display icons
    [SerializeField] Color m_inActive;						// in-active color for the display icons

	/// <summary>
	/// The Grid Hud is a class that contains the different types
	/// of buildable structures in the game.
	/// </summary>

    [System.Serializable]
    public class GridHud {
        public Image image;									// 2D image to represent buildable object
        public KeyCode inputKey;							// the key inputed to select this object
        public Text keyText;								// the text object that represents the input key


        public GridHud(Image image, KeyCode inputKey) {
            this.image = image;
            this.inputKey = inputKey;
        }
    }

	/// <summary>
	/// A Buildable object consists of ramps, walls, and floors
	/// the object consists of a Grid Object and a Grid Hud for
	/// each object.
	/// </summary>

    [System.Serializable]
    public class BuildableObject {
        [HideInInspector] public string Name;				// name of the object
        public GridObject gridObject;						// the actual object
        public GridHud gridHud;								// the hud element linked with the object
        public BlockType blockType;


        public BuildableObject(GridObject gridObject, GridHud gridHud, BlockType blockType) {
            this.gridObject = gridObject;
            this.gridHud = gridHud;
            this.blockType = blockType;
        }
    }

    [SerializeField] BuildableObject[] buildableObject;		// all the objects that can be built along with their huds

    #endregion Inspector Reference
    #region Event


    public delegate void ChangeNodeHandler(GridNode gridNode);
    public event ChangeNodeHandler ChangeNode;						// delegate void for changing nodes

    #endregion;
    [SerializeField] private AudioSource m_audioSource;				// audio feedback that plays when you place an object

    private GridObject m_currentBlock;								// current active block
	private Vector3 m_positionOnGrid;								// currently not used but may need to be for downward ramps and ceilings?
	private Quaternion m_currentRotation;							// 
	private GridNode m_currentNode;									// the current node selected/active?
	private bool m_isValid;											// 
	private GridNode gNode;											// node reference
	float m_buildRate = 0.15f;										// the rate at whcih the object is created
	float m_lastBuild;												// time at which the last build was completed
	int m_lastIndex;												// index referencing the last build object type used
    public GridHandler gridHandler;
    PlayerBase localPlayer;

    private void OnValidate() {
        for (int i = 0; i < buildableObject.Length; i++) {
            if (buildableObject[i].gridObject != null) {
                buildableObject[i].Name = buildableObject[i].gridObject.GetType().ToString();
            }
        }
    }

    private void Start() {
        Initialize();												// 
		m_shadowGizmo = new GameObject("GizmoShadow").transform;	// create the object preview
        ChangeNode += OnGridNodeChanged;							// 
        localPlayer = null;
    }

    void Initialize() {
        for (int i = 0; i < buildableObject.Length; i++) {
            BuildableObject cachedBuildableObject = buildableObject[i];
            GridObject cachedGridObject = cachedBuildableObject.gridObject;
            cachedGridObject.Initialize();
        }
    }

    void OnChangeNode(GridNode gridNode) {
        if (m_currentNode != gNode) {
            gNode = m_currentNode;

            if(localPlayer.IsInBuildMode())
                m_audioSource.Play();

            if (ChangeNode == null) return;
            ChangeNode(gNode);

        }
    }

    void Update() {
        if (!localPlayer) {
            if (!GetLocalPlayer()) {
                if (m_currentBlock)
                    m_currentBlock.EnableGizmo(false);
                return;
            }
        }

        if (!localPlayer.IsInBuildMode()) {
            if (m_currentBlock)
                m_currentBlock.EnableGizmo(false);
            return;
        }
        PlayerInput();

        //if(!UpdateBuildHandling()) {
        //    return;
        //}
        //OnBuildInput();
    }

    // returns true if it is ok to build
    public bool UpdateBuildHandling() {
        if (m_currentBlock == null) {
            return false;
        }
        ValidateNode();
        m_currentNode = m_gridRecognition.GridRecognitionUpdate(m_currentBlock.GetBlockType());

        OnChangeNode(m_currentNode);
        if (gNode == null) {
            return false;
        }
        m_currentBlock.Gizmo.SetPosition(gNode.center);
        m_shadowGizmo.transform.rotation = m_currentBlock.Gizmo.transform.rotation;

        m_currentBlock.RotateGizmo(KeyCode.R, m_gridRecognition.Player);

        if (m_currentBlock.Gizmo.transform.rotation != m_shadowGizmo.transform.rotation) {
            m_shadowGizmo.transform.rotation = m_currentBlock.Gizmo.transform.rotation;
            ValidateNode();
        }
        return true;
    }

    void OnGridNodeChanged(GridNode gridNode) {
        if (gridNode == null) return;
        gNode = gridNode;
        m_shadowGizmo.position = gNode.center;
        ValidateNode();
    }

    void ValidateNode() {
        if (!m_currentBlock || gNode == null || !m_shadowGizmo) { 
            m_isValid = false;
            return;
        }
        m_isValid = m_currentBlock.Validate(gNode, m_shadowGizmo);

        if (m_isValid) {
            
        }
        OnGizmoChange();
    }

    bool GetLocalPlayer() {
        GameObject[] obj = GameObject.FindGameObjectsWithTag("Player");
        for (int i = 0; i < obj.Length; i++) {
            PlayerBase playerBase = obj[i].GetComponent<PlayerBase>();

			if(playerBase.photonView && playerBase.photonView.IsMine && PhotonNetwork.IsConnected) {
                localPlayer = playerBase;
            }
			else if(!playerBase.photonView || !PhotonNetwork.IsConnected) {
                localPlayer = playerBase;
            }
        }
        return localPlayer != null;
    }

    void PlayerInput() {
        for (int i = 0; i < buildableObject.Length; i++) {
            if (Input.GetKeyDown(buildableObject[i].gridHud.inputKey)) {
                OnChangeBlock(buildableObject[i].gridObject, i);
            }
        }
    }

    public void DoDamageToBuiltObject(int gridNodeIndex, float damageDone) {
        GridNode node = gridHandler.grid.GetNode(gridNodeIndex);
        if(node == null) {
            Debug.LogError("Node not found with index: " + gridNodeIndex);
            return;
        }
        node.blockHealth.SetHealth(damageDone);
    }

    public void BuildObjectRemotely(int gridNodeIndex, float rotation, BlockType blockType) {
        GridObject gridObj = null;
        for(int i = 0; i < buildableObject.Length; i++) {
            if(buildableObject[i].blockType == blockType) {
                gridObj = buildableObject[i].gridObject;
            }
        }
        if (gridObj == null) {
            Debug.LogError("Didn't find grid Object");
            return;
        }
        GridNode node = gridHandler.grid.GetNode(gridNodeIndex);
        gridObj.CreateBlock(node, new Vector3(0,rotation,0));
        OnGizmoChange();
    }

    void OnGizmoChange() {
        if (!m_currentBlock)
            return;

        if (gNode == null)
            return;
        var m_dir = m_currentBlock.GetBlockType() == BlockType.Floor ? Direction.Down : m_currentBlock.Gizmo.transform.GetDirection();
        bool inUse = gNode.InUse(m_currentBlock.GetBlockType(), m_dir);

        if (localPlayer && !localPlayer.IsInBuildMode())
            inUse = true;

        m_currentBlock.EnableGizmo(inUse == true ? false : true);
        if (m_isValid)
            m_currentBlock.Gizmo.VisualePermission(true);
        else
            m_currentBlock.EnableGizmo(false);
    }

    public GridNode GetGridNode() {
        return gNode;
    }

    public GridObject GetCurrentBlock() {
        return m_currentBlock;
    }

    public float GetRotation() {
        return m_currentBlock.Gizmo.transform.eulerAngles.y;
    }

    public bool OnBuildInput() {
        if (m_isValid && localPlayer && localPlayer.IsInBuildMode()) {
            if (Time.time > m_lastBuild && localPlayer.buildEnergies.HasEnoughEnergy(5)) {
                localPlayer.buildEnergies.SubtractFromList(5f);

                m_audioSource.Play();

                m_currentBlock.CreateBlock(gNode, m_currentBlock.Gizmo.transform.eulerAngles);
                OnGizmoChange();
                m_lastBuild = Time.time + m_buildRate;
                return true;
            }
        }
        return false;
    }

    void OnChangeBlock(GridObject gridObject, int index) {
        ChangeBlock(gridObject, index);
    }

    void ChangeBlock(GridObject newBlock, int index) {
        Vector3 position = new Vector3(0, 0, 0);

        if (m_currentBlock != null) {
            m_currentBlock.EnableGizmo(false);
            var m_prevGizmo = m_currentBlock.Gizmo;
            position = m_prevGizmo == null ? new Vector3(0, 0, 0) : m_shadowGizmo.position;
        }

        if (newBlock != m_currentBlock) {
            m_currentBlock = newBlock;
            m_currentBlock.UseGizmo(position);

            buildableObject[m_lastIndex].gridHud.image.color = m_inActive;
            buildableObject[index].gridHud.image.color = m_active;
            m_lastIndex = index;
        }
    }
}
