using System.Collections.Generic;
using System.Linq;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.Experimental.UIElements;
using Random = System.Random;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    public int GenerationSeed = 435353451;

    public Text MensajePing;
    public Text MensajeDistancia;
    public Text MensajeFinal;
    public Text MensajeDestruir;
    public bool DebugMode = false;
    public int AntenasPorDestruir = 3;

    public GameObject AntenaComFisicaPrefab;
    public GameObject PingPrefab;
    public AudioClip PingSound;
    public float TiempoDeTransmision = 5.0f;
    public float TiempoDePing = 0.25f;

    protected Random Random;
    protected RedCom RedCom;

    private bool _pinging;
    private bool _pingingLlegoADestino;
    private int _distanciaInicial;

    private float _timerTransmision;
    private float _timerPing;

    private readonly List<GameObject> _antenaComsFisicas = new List<GameObject>();
    private readonly List<GameObject> _textoDistancias = new List<GameObject>();
    private AntenaCom _antenaComTransmisoraMensaje;
    private AntenaCom _antenaComTransmisoraPing;
    private AntenaCom _antenaComOrigenPing;
    private AntenaCom _antenaComDestinoPing;
    private GameObject _pingFisico;

    private bool _pingExitoso;
    private string _pingMensaje;

    void Start ()
    {
        MensajePing.text = "";

        if (GenerationSeed == 0) {
            Random = new Random();
        } else {
            Random = new Random(GenerationSeed);
        }
        
        RedCom = new RedCom();
        RedCom.Generar(Random);

        _antenaComTransmisoraMensaje = RedCom.AntenaComEmisora;
        _antenaComTransmisoraMensaje.MensajeTrasnmitido = true;
        _distanciaInicial = RedCom.AntenaComEmisora.GetDistancia(RedCom.AntenaComReceptora);
        RefreshMensajeDistancia();

        MensajeDestruir.text = "Destruir antena: " + AntenasPorDestruir;
        GameObject identificadorEscena = GameObject.Find("IdentificadorEscena");
        DontDestroyOnLoad(identificadorEscena);

        foreach (var nodo in RedCom.AntenaComs) {
            GameObject antenaComFisica = Instantiate(AntenaComFisicaPrefab, new Vector3(nodo.X, nodo.Y, 0), Quaternion.identity);
            _antenaComsFisicas.Add(antenaComFisica);

            if (nodo == RedCom.AntenaComEmisora)
            {
                antenaComFisica.transform.localScale = new Vector3(0.6f, 0.6f, 1.0f);
                antenaComFisica.GetComponent<SpriteRenderer>().color = Color.yellow;
                antenaComFisica.GetComponent<Animator>().SetInteger("TipoAntena", 1);
                antenaComFisica.GetComponent<Animator>().SetBool("Emitiendo", true);
            }

            if (nodo == RedCom.AntenaComEstacion)
            {
                antenaComFisica.transform.localScale *= 2.0f;
                antenaComFisica.GetComponent<Animator>().SetInteger("TipoAntena", 2);
                antenaComFisica.transform.localScale *= 0.15f;
            } else
            {
                antenaComFisica.GetComponent<Animator>().SetInteger("TipoAntena", 1);
            }

            PropiedadesAntenaCom propiedadesAntenaCom = antenaComFisica.GetComponent<PropiedadesAntenaCom>();
            propiedadesAntenaCom.IdAntenaCom = nodo.Id;
            propiedadesAntenaCom.Grupo = nodo.Grupo;

            if (nodo.AntenaComGrande) {
                antenaComFisica.transform.localScale *= 1.1f;
            }
            
            foreach (var conexion in nodo.Conexiones) {
                GameObject objetoLinea = new GameObject();
                var lineRenderer = objetoLinea.AddComponent< LineRenderer>();
                lineRenderer.positionCount = 2;
                lineRenderer.SetPosition(0, new Vector3(nodo.X, nodo.Y, 5));
                lineRenderer.SetPosition(1, new Vector3(conexion.X, conexion.Y, 5));

                lineRenderer.startWidth = 0.05f;
                lineRenderer.endWidth = 0.02f;

                lineRenderer.material = new Material(Shader.Find("Particles/Additive"));
                lineRenderer.startColor = Color.red;
                lineRenderer.endColor = Color.black;
            }
        }

        if (DebugMode) {
            RefreshTextosDistancias();
        }
    }

	void Update () {
        // Input
        ManageInput();

        // Mensaje
        _timerTransmision += Time.deltaTime;
	    if (_timerTransmision > TiempoDeTransmision) {
	        TransmitirMensaje();
            _timerTransmision = 0;
        }

        // Ping
	    if (_pinging) {
            _timerPing += Time.deltaTime;
	        float alphaPing = _timerPing / TiempoDePing;
	        float pingX = (1 - alphaPing) * _antenaComTransmisoraPing.X +
	                      alphaPing * _antenaComTransmisoraPing.GetRuta(_antenaComDestinoPing).X;
            float pingY = (1 - alphaPing) * _antenaComTransmisoraPing.Y +
                          alphaPing * _antenaComTransmisoraPing.GetRuta(_antenaComDestinoPing).Y;
	        _pingFisico.transform.position = new Vector3(pingX, pingY, -1);
            if (_timerPing > TiempoDePing) {
                TransmitirPing();
                _timerPing = 0;
            }
	    }
	}

    private GameObject GetAntenaComFisica(AntenaCom antenaCom)
    {
        return _antenaComsFisicas.FirstOrDefault(a => a.GetComponent<PropiedadesAntenaCom>().IdAntenaCom == antenaCom.Id);
    }

    private void ManageInput()
    {
        if (Input.GetMouseButtonDown((int) MouseButton.LeftMouse))
        {
            // Ping
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit)) {
                int idAntenaCom = hit.collider.GetComponent<PropiedadesAntenaCom>().IdAntenaCom;
                PingAntenaCom(RedCom.AntenaComs.FirstOrDefault(a => a.Id == idAntenaCom));
            }
        } else if (Input.GetMouseButtonDown((int) MouseButton.RightMouse)) {
            // Destroy
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                int idAntenaCom = hit.collider.GetComponent<PropiedadesAntenaCom>().IdAntenaCom;
                DestruirAntenaCom(RedCom.AntenaComs.FirstOrDefault(a => a.Id == idAntenaCom));
            }
        }
    }

    private void PingAntenaCom(AntenaCom antenaComAPinguear)
    {
        if (_pinging || antenaComAPinguear == RedCom.AntenaComEstacion)
        {
            return;
        }

        RedCom.CalcularRutas(antenaComAPinguear);
        if (RedCom.AntenaComEstacion.GetDistancia(antenaComAPinguear) > 100) {
            return;
        }

        _antenaComOrigenPing = RedCom.AntenaComEstacion;
        _antenaComTransmisoraPing = _antenaComOrigenPing;
        _antenaComDestinoPing = antenaComAPinguear;
        _timerPing = 0;
        _pinging = true;

        _pingFisico = Instantiate(PingPrefab, new Vector3(_antenaComOrigenPing.X, _antenaComOrigenPing.Y, 0), Quaternion.identity);
    }

    private void DestruirAntenaCom(AntenaCom antenaComADestruir)
    {
        if (AntenasPorDestruir == 0
         || antenaComADestruir == _antenaComTransmisoraMensaje || antenaComADestruir == _antenaComTransmisoraPing
         || antenaComADestruir == RedCom.AntenaComEstacion || antenaComADestruir == RedCom.AntenaComEmisora)
        {
            return;
        }
        AntenasPorDestruir--;

        MensajeDestruir.text = "Destruir antena: " + AntenasPorDestruir.ToString();

        RedCom.DestruirAntenaCom(antenaComADestruir);
        GameObject antenaComFisica = GetAntenaComFisica(antenaComADestruir);
        _antenaComsFisicas.Remove(antenaComFisica);
        Destroy(antenaComFisica);

        if (DebugMode) {
            RefreshTextosDistancias();
        }

        if (antenaComADestruir == RedCom.AntenaComReceptora)
        {
            EnemigosDestruidos();
        }

        if (_antenaComTransmisoraMensaje.GetDistancia(RedCom.AntenaComReceptora) > 100)
        {
            MensajeBloqueado();
        }
    }

    private void RefreshTextosDistancias()
    {
        foreach (var texto in _textoDistancias) {
            Destroy(texto);
        }
        _textoDistancias.Clear();

        foreach (var nodo in RedCom.AntenaComs) {
            GameObject antenaComFisica = GetAntenaComFisica(nodo);

            GameObject textDistanceObject = new GameObject();
            _textoDistancias.Add(textDistanceObject);
            textDistanceObject.transform.localScale = new Vector3(0.075f, 0.075f, 0.075f);
            textDistanceObject.transform.position = new Vector3(antenaComFisica.transform.position.x, antenaComFisica.transform.position.y, -1.0f);

            TextMesh distanceText = textDistanceObject.AddComponent<TextMesh>();

            distanceText.text = nodo.GetDistancia(RedCom.AntenaComReceptora) != int.MaxValue ? nodo.GetDistancia(RedCom.AntenaComReceptora).ToString() : "X";
            distanceText.anchor = TextAnchor.MiddleCenter;
            distanceText.color = Color.black;
            distanceText.fontSize = 64;
            distanceText.offsetZ = -1;
        }
    }

    private void TransmitirMensaje()
    {
        foreach(var antena in RedCom.AntenaComs)
        {
            GameObject antenaComFisicaTransmisora = GetAntenaComFisica(antena);
            antenaComFisicaTransmisora.GetComponent<Animator>().SetBool("Emitiendo", false);
        }

        _antenaComTransmisoraMensaje = _antenaComTransmisoraMensaje.GetRuta(RedCom.AntenaComReceptora);
        _antenaComTransmisoraMensaje.MensajeTrasnmitido = true;
        if (_antenaComTransmisoraMensaje == RedCom.AntenaComReceptora) {
            MensajeEnDestino();
        }

        List<AntenaCom> antenasTransmitiendo = RedCom.AntenaComs.Where(a => Random.NextDouble() < 0.4).ToList();
        if (!antenasTransmitiendo.Contains(_antenaComTransmisoraMensaje))
        {
            antenasTransmitiendo.Add(_antenaComTransmisoraMensaje);
        }

        foreach (var antena in antenasTransmitiendo)
        {
            GameObject antenaComFisicaTransmisora = GetAntenaComFisica(antena);
            antenaComFisicaTransmisora.GetComponent<Animator>().SetBool("Emitiendo", true);
        }

        RefreshMensajeDistancia();
    }

    private void TransmitirPing()
    {
        _antenaComTransmisoraPing = _antenaComTransmisoraPing.GetRuta(_antenaComDestinoPing);
        AudioSource audioSource = GetComponent<AudioSource>();
        audioSource.clip = PingSound;
        audioSource.Play();

        if (_pingingLlegoADestino && _antenaComTransmisoraPing == _antenaComOrigenPing) {
            ResultadoPing();
            Destroy(_pingFisico);
            _pinging = false;
        } else {
            if (_antenaComTransmisoraPing == _antenaComDestinoPing)
            {
                _pingingLlegoADestino = true;
                _pingExitoso = _antenaComDestinoPing.MensajeTrasnmitido || _antenaComTransmisoraMensaje.GetRuta(RedCom.AntenaComReceptora) == _antenaComDestinoPing;

                _pingMensaje = "---INFORMACIÓN EN DESTINO / AntenaCom #" + _antenaComDestinoPing.Id + " ---\n";
                int anchoMensaje = _pingMensaje.Count();
                if (_pingExitoso)
                {
                    RedCom.CalcularRutas(_antenaComTransmisoraMensaje);
                    int distancia = _antenaComDestinoPing.GetDistancia(_antenaComTransmisoraMensaje);
                    if (_antenaComDestinoPing.MensajeTrasnmitido) {
                        if (distancia == 0) {
                            _pingMensaje += " ! EMITIENDO MENSAJE !\n";
                        } else {
                            _pingMensaje += "MENSAJE REEMITIDO - Distancia: " + distancia;
                            if (distancia == 1) { _pingMensaje += " salto.\n"; }
                            else { _pingMensaje += " saltos.\n"; }
                        }
                    } else {
                        _pingMensaje += "! RECIBIENDO MENSAJE !\n";
                    }  
                }
                else
                {
                    _pingMensaje += "El mensaje no paso por aquí.\n";
                }

                for (int i = 0; i < anchoMensaje - 1; i++) {
                    _pingMensaje += "-";
                }
                
                _antenaComDestinoPing = _antenaComOrigenPing;
            }
        }
    }


    private void RefreshMensajeDistancia()
    {
        int distanciaActual = _antenaComTransmisoraMensaje.GetDistancia(RedCom.AntenaComReceptora);
        MensajeDistancia.text = (_distanciaInicial - distanciaActual).ToString() + "/" + _distanciaInicial;
    }

    private void EnemigosDestruidos()
    {
        MensajeFinal.text = "! RESISTENCIA DESTRUIDA !";
        Invoke("VolverAlMenu", 3f);
    }

    private void VolverAlMenu()
    {
        SceneManager.LoadScene(0);
    }

    private void MensajeEnDestino()
    {
        SceneManager.LoadScene(2);
    }

    private void MensajeBloqueado()
    {
        MensajeFinal.text = "! MENSAJE BLOQUEADO !";
        Invoke("VolverAlMenu", 3f);
    }

    private void ResultadoPing()
    {
        MensajePing.text = _pingMensaje;
    }
}
