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

namespace ToadieTechnika.GGJ2018Game
{
    public class GameplayController : GenericSingleton<GameplayController>
    {
        [Header("Camera")]
        [SerializeField]
        Camera m_Camera;

        [SerializeField]
        Transform m_CameraTransform;

        [Header("UI Components")]
        [SerializeField]
        CanvasGroup m_WarningGroup;

        [SerializeField]
        TMPro.TextMeshProUGUI m_WarningText;
        
        [Space]
        [SerializeField]
        TMPro.TextMeshProUGUI m_LevelText;

        [SerializeField]
        Image m_ReceptionBar;

        [SerializeField]
        Image m_WarningImageBar;

        [SerializeField]
        TMPro.TextMeshProUGUI m_MessagesLeftText;

        [SerializeField]
        CanvasGroup m_WhiteScreen;

        [Space]
        [SerializeField]
        CanvasGroup m_MessagesLeftPanel;

        [SerializeField]
        CanvasGroup m_MessagesContainer;

        [SerializeField]
        Image m_LackingSignal;

        [Header("Components")]
        [SerializeField]
        LevelCardsController m_LevelCardController;

        List<PhoneUnitScript> m_CurrentPhoneUnits = new List<PhoneUnitScript>();
        TransmissionObject[] m_CurrentTransmissions;
        Collider2D[] m_TransmissionColliders;
        List<Collider2D> m_CurrentSatellites = new List<Collider2D>();
        BluetoothObject[] m_CurrentBluetoothObjects;

        IPhoneUnit m_CurrentPhoneUnit;
        Vector3 m_UnitLastPos;
        int m_CurrentPhoneUnitIndex;
        int m_CollectForEndLeft;
        ContactFilter2D m_SatelliteContactFilter2D;

        bool 
            m_isInit,
            m_isFinished;

        int m_LastWorkingSatellites;
        bool m_SatelliteSoundPlayed;

        #region Properties
        void SetWarningBarColor(string _HexCode)
        {
            Color _GiveColor;
            ColorUtility.TryParseHtmlString(_HexCode, out _GiveColor);
            m_WarningImageBar.color = _GiveColor;
        }

        public BluetoothObject[] CurrentBluetoothObjects
        {
            get { return m_CurrentBluetoothObjects; }
            set { m_CurrentBluetoothObjects = value; }
        }

        public List<Collider2D> CurrentSatellites
        {
            get { return m_CurrentSatellites; }
            set { m_CurrentSatellites = value; }
        }

        public TransmissionObject[] CurrentTransmissions
        {
            get { return m_CurrentTransmissions; }
            set { m_CurrentTransmissions = value; }
        }

        public List<PhoneUnitScript> CurrentPhoneUnits
        {
            get { return m_CurrentPhoneUnits; }
            set { m_CurrentPhoneUnits = value; }
        }

        public int CollectForEndLeft
        {
            get { return m_CollectForEndLeft; }
            set 
            {
                m_CollectForEndLeft = value;
                RefreshMessagingLeftLabel();
            }
        }
        #endregion

        #region Unity Functions
        void Start()
        {
            //Initiate contact filter
            m_SatelliteContactFilter2D = new ContactFilter2D();
            m_SatelliteContactFilter2D.SetLayerMask(1 << LayerMask.NameToLayer("Overlapping"));
            m_SatelliteContactFilter2D.useLayerMask = true;
            m_SatelliteContactFilter2D.useTriggers = true;

            NormalizeGameplay();
            m_isInit = true;
        }

        void OnEnable()
        {
            if (m_isInit)
                NormalizeGameplay();
        }

        void Update()
        {
            UpdateCameraPosToPlayer();
            GameplayControls();
            ClickControls();
            CheckSatellites();
        }
        #endregion

        #region Functions
        void CheckSatellites()
        {
            if (m_isFinished)
                return;

            if (m_CurrentSatellites.Count > 0)
            {
                int _CurrentWorkingSatellites = 0;

                for (int i = 0; i < m_CurrentSatellites.Count; i++)
                {
                    if (m_CurrentSatellites[i].OverlapCollider(m_SatelliteContactFilter2D, m_TransmissionColliders) > 0)
                        _CurrentWorkingSatellites++;
                }

                //Sounds
                if (m_LastWorkingSatellites != _CurrentWorkingSatellites)
                {
                    GameController.instance.PlaySound(SFX.SatelliteConnect);
                    m_LastWorkingSatellites = _CurrentWorkingSatellites;
                }

                m_ReceptionBar.fillAmount = (float)_CurrentWorkingSatellites / (float)m_CurrentSatellites.Count;
            }
            else
                m_ReceptionBar.fillAmount = 1f;

            m_LackingSignal.enabled = m_ReceptionBar.fillAmount < 1f;
            if (m_ReceptionBar.fillAmount == 1f && m_CollectForEndLeft < 1 && !m_isFinished)
                ForceWin();
        }

        void RemovePhoneUnits()
        {
            m_CurrentPhoneUnit = null;

            if (m_CurrentPhoneUnits.Count > 0)
            {
                m_UnitLastPos = m_CurrentPhoneUnits[m_CurrentPhoneUnitIndex].transform.position;
                m_UnitLastPos.z = -10;
            }

            foreach (PhoneUnitScript _PhoneUnit in m_CurrentPhoneUnits)
                _PhoneUnit.RemoveObject();

            m_CurrentPhoneUnits.Clear();
            m_CurrentPhoneUnitIndex = 0;
        }

        void ClickControls()
        {
            if (Input.GetMouseButtonDown(0) && m_CurrentPhoneUnits.Count > 0)
            {
                RaycastHit2D GetClickHit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero, 0, 1 << LayerMask.NameToLayer("Overlapping") | 1 << LayerMask.NameToLayer("Ground"));

                if (GetClickHit)
                {
                    Collider2D _GetCollider = GetClickHit.collider;

                    //Transmission
                    if (_GetCollider.GetComponent<TransmissionObject>() != null)
                        _GetCollider.GetComponent<TransmissionObject>().ToggleLocked();
                    else if (_GetCollider.GetComponent<PhoneUnitScript>() != null)
                    {
                        PhoneUnitScript _PhoneUnit = _GetCollider.GetComponent<PhoneUnitScript>();

                        if (_PhoneUnit.SetSelected)
                            return;

                        m_CurrentPhoneUnits[m_CurrentPhoneUnitIndex].SetSelected = false;
                        m_CurrentPhoneUnitIndex = _PhoneUnit.UnitID;

                        m_CurrentPhoneUnit = m_CurrentPhoneUnits[m_CurrentPhoneUnitIndex];
                        m_CurrentPhoneUnits[m_CurrentPhoneUnitIndex].SetSelected = true;

                        ObjectPooler.instance.CreateObject(PooledObjectsEnum.PlayerSwapPar, m_CurrentPhoneUnits[m_CurrentPhoneUnitIndex].transform.position, Quaternion.identity);
                        GameController.instance.PlaySound(SFX.SwapUnit);
                    }
                }
            }
        }

        void NormalizeGameplay()
        {
            m_WarningGroup.alpha = 0;

            m_LastWorkingSatellites = 0;
            m_WhiteScreen.alpha = 0;
            m_isFinished = false;
            RemovePhoneUnits();
            m_CurrentTransmissions = null;

            //Initiate Level
            m_LevelCardController.InitiateLevel();
            m_CurrentPhoneUnit = m_CurrentPhoneUnits[m_CurrentPhoneUnitIndex];
            UpdateCameraPosToPlayer(_ToNormalize: true);

            //Transmission Colliders
            m_TransmissionColliders = new Collider2D[m_CurrentTransmissions.Length];
            for (int i = 0; i < m_TransmissionColliders.Length; i++)
                m_TransmissionColliders[i] = m_CurrentTransmissions[i].GetComponent<Collider2D>();

            //Pass transmission Colliders to bluetooth objects (if there are)
            for (int i = 0; i < m_CurrentBluetoothObjects.Length; i++)
                m_CurrentBluetoothObjects[i].GetTransmissionColliders = m_TransmissionColliders;

            //Set Level Label
            m_LevelText.text = "Level " + (GameController.instance.PlayerData.CurrentLevel + 1);
        }

        public void GameplayControls()
        {
            //Current Player Controls
            if (m_CurrentPhoneUnit != null)
                m_CurrentPhoneUnit.ControllableFunctions();

            //Escape to Exit, NO PAUSE
            if (Input.GetKeyDown(KeyCode.Escape) && !TransitionScript.instance.isTransitioning && m_CurrentPhoneUnits.Count > 0)
            {
                GameController.instance.PlaySound(SFX.GameButton);
                GoOffGameplay(Panels.Menu);
            }
        }

        public void EndItemCollected()
        {
            m_CollectForEndLeft--;
            RefreshMessagingLeftLabel();

            if (m_CollectForEndLeft < 1)
                GameController.instance.PlaySound(SFX.MessageSent);
        }

        public void RefreshMessagingLeftLabel()
        {
            m_MessagesLeftText.text = m_CollectForEndLeft.ToString();
            
            //Set alphas
            m_MessagesContainer.alpha = m_CollectForEndLeft < 1 ? 1f : 0.5f;
            m_MessagesLeftPanel.alpha = m_CollectForEndLeft < 1 ? 0f : 1f;
        }

        void ForceWin()
        {
            m_isFinished = true;

            //Particle Effects
            GameController.instance.PlaySound(SFX.LevelFinished);

            foreach (PhoneUnitScript _PhoneUnit in m_CurrentPhoneUnits)
            {
                ObjectPooler.instance.CreateObject(PooledObjectsEnum.PlayerSuccessParticle, _PhoneUnit.transform.position, Quaternion.identity);
            }

            //Win message
            m_WarningGroup.alpha = 1;
            SetWarningBarColor("#58B537FF");
            m_WarningText.text = "Messages are sent!";

            StartCoroutine("WinningSequence");
        }

        IEnumerator WinningSequence()
        {
            RemovePhoneUnits();

            yield return new WaitForSeconds(2f);
            if (GameController.instance.PlayerData.CurrentLevel < m_LevelCardController.GetLevelsCount - 1)
            {
                GameController.instance.PlayerData.CurrentLevel++;
                GameController.instance.PlayerDataManipulation(PlayerDataState.Save);


                TransitionScript.instance.TransitionInit(delegate
                {
                    NormalizeGameplay();
                });
            }
            else
            {
                GoOffGameplay(Panels.Finish);
            }
        }

        void GoOffGameplay(Panels _WhichPanel)
        {
            RemovePhoneUnits();

            TransitionScript.instance.TransitionInit(delegate
            {
                m_LevelCardController.SetLevelCardsVisible = false;
                PanelsController.instance.ChangePanel(_WhichPanel);
            });
        }

        void UpdateCameraPosToPlayer(bool _ToNormalize = false)
        {
            Vector3 _TargetPos = m_CurrentPhoneUnits.Count > 0 ? m_CurrentPhoneUnits[m_CurrentPhoneUnitIndex].transform.position : m_UnitLastPos;
            _TargetPos.z = -10;
 
            if (_ToNormalize)
                m_CameraTransform.position = _TargetPos;
            else
                m_CameraTransform.position = Vector3.Lerp(m_CameraTransform.position, _TargetPos, Time.deltaTime * 5f);
        }

        public void PhoneUnitHarmed(bool _isInterence = false, int _PassPhoneUnit = -1)
        {
            if (_isInterence)
                GameController.instance.PlaySound(SFX.Interference);

            //Reason
            m_WarningGroup.alpha = 1;
            SetWarningBarColor("#BF3C5CFF");
            m_WarningText.text = _isInterence ? "Interference" : "Fluctuated";

            if (_PassPhoneUnit > -1)
                m_WarningText.text += _PassPhoneUnit != m_CurrentPhoneUnitIndex ? " (Clone)" : "";
            
            //Dead Partices
            GameController.instance.PlaySound(SFX.PlayerExplode);
            foreach (PhoneUnitScript _PhoneUnit in m_CurrentPhoneUnits)
            {
                ObjectPooler.instance.CreateObject(PooledObjectsEnum.PlayerDeadParticle, _PhoneUnit.transform.position, Quaternion.identity);
            }

            RemovePhoneUnits();

            StartCoroutine("GameplayRestart");
        }

        IEnumerator GameplayRestart()
        {
            //From white screen
            m_WhiteScreen.alpha = 0.7f;

            while (m_WhiteScreen.alpha > 0)
            {
                yield return new WaitForEndOfFrame();
                m_WhiteScreen.alpha = Mathf.MoveTowards(m_WhiteScreen.alpha, 0, Time.deltaTime);
            }

            yield return new WaitForSeconds(1f);
            TransitionScript.instance.TransitionInit(delegate
            {
                NormalizeGameplay();
            });
        }
        #endregion
    } 
}
