using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Dof;
using Guru;
using TMPro;
using UnityEngine.UIElements;

public class GameClient : MonoBehaviour
{
    public TextMeshProUGUI infoText;
    public TMP_InputField roomIdInput;
    public TMP_InputField nicknameInput;
    public GameObject[] goes;
    public string ipAddress = "35.194.59.155";//"192.168.32.119";
    public string roomId = "room01";
    public string nickName = "player01";
    public string uid = "001";
    public int port = 7417;

    private KcpDofClient _kcpDofClient;
    private string levelId;
    private List<string> _levelIds = new ();
    private int _levelIndex;
    private int _pointId;

    private void Awake()
    {
        Loom.Initialize();

        for (int i = 0; i < goes.Length; i++)
        {
            if(i > 0)
            {
                goes[i].SetActive(false);
            }
        }

        roomIdInput.text = roomId;
        nicknameInput.text = nickName;

        _kcpDofClient = new KcpDofClient();
        _kcpDofClient.OnConnected += OnConnected;
        _kcpDofClient.OnServerMessage += OnServerMessage;
    }

    private void OnDestroy()
    {
        _kcpDofClient.OnConnected -= OnConnected;
        _kcpDofClient.OnServerMessage -= OnServerMessage;

        _kcpDofClient.Close();
    }

    IEnumerator _DelayCall(float delay_secs, Action callback)
    {
        if (delay_secs > Mathf.Epsilon)
        {
            yield return new WaitForSeconds(delay_secs);
        }

        try
        {
            callback?.Invoke();
        }
        catch (Exception ex)
        {
            Debug.LogError($"[GameClient]_DelayCall: {ex.Message}, trace: {ex.StackTrace}");
        }
    }   

    public void DelayCall(float secs, Action callback)
    {
        StartCoroutine(_DelayCall(secs, callback));
    }

    public void OnConnectClick()
    {
        Debug.Log($"[GameClient]OnConnectClick");
        _kcpDofClient.Connect(ipAddress, port);
    }

    public void OnPlayerEnterClick()
    {
        Debug.Log($"[GameClient]OnPlayerEnterClick");
        roomId = roomIdInput.text;
        nickName = nicknameInput.text;
        infoText.text = $"Request PlayerEnter {roomId}, Nickname {nickName}";

        var msg = new ClientMessage
        {
            PlayerEnter = new PlayerEnter
            {
                RoomId = roomId,
                Uid = uid,
                NickName = nickName,
                Country = "CN"
            }
        };

        _kcpDofClient.Send(msg);
    }

    public void OnPointClick(GameObject obj)
    {
        obj.SetActive(false);
        SendPointFound(_pointId);
        _pointId++;
    }
        
    public void SendLevelPrepared(string level_id)
    {
        Debug.Log($"[GameClient]LevelPrepared");
        infoText.text = $"Request LevelPrepared {level_id}";

        var msg = new ClientMessage
        {
            LevelPrepared = new LevelPrepared
            {
                Cid = _kcpDofClient.Cid,
                Level = level_id
            }
        };

        _kcpDofClient.Send(msg);
    }

    public void SendPointFound(int point_id)
    {
        Debug.Log($"[GameClient]SendPointFound");

        infoText.text = $"Request PointFound {point_id} on level {levelId}";

        var msg = new ClientMessage
        {
            PointFound = new PointFound
            {
                Cid = _kcpDofClient.Cid,
                Level = levelId,
                PointId = point_id
            }
        };

        _kcpDofClient.Send(msg);
    }

    public void SendLevelEnd(string level_id)
    {
        Debug.Log($"[GameClient]SendLevelEnd");
        infoText.text = $"Request LevelEnd {level_id}";

        var msg = new ClientMessage
        {
            LevelEnd = new LevelEnd
            {
                Cid = _kcpDofClient.Cid,
                Level = level_id
            }
        };

        _kcpDofClient.Send(msg);
    }

    public void SendAllLevelEnd()
    {
        Debug.Log($"[GameClient]SendAllLevelEnd");
        infoText.text = $"Request AllLevelEnd";

        var msg = new ClientMessage
        {
            AllLevelEnd = new AllLevelEnd
            {
                Cid = _kcpDofClient.Cid
            }
        };

        _kcpDofClient.Send(msg);
    }

    private void OnConnected()
    {
        goes[0].SetActive(false);
        infoText.text = $"Client connected to Server {ipAddress}:{port}";
        goes[1].SetActive(true); // Enable PlayerEnter button
    }

    void OnServerMessage(ServerMessage msg)
    {
        if (msg.PlayerEntered != null)
        {
            infoText.text = $"{nickName} Enter cid: {msg.PlayerEntered.Cid}, Waiting for other player";
            goes[1].SetActive(false);            
        }
        else if (msg.GameStart != null)
        {
            infoText.text = $"Game Start";
            foreach (var item in msg.GameStart.LevelResources)
            {
                _levelIds.Add(item.LevelId);
            }

            levelId = _levelIds[_levelIndex];
            DelayCall(3f, () => {
                SendLevelPrepared(levelId);
            });
        }
        else if (msg.LevelStart != null)
        {
            levelId = msg.LevelStart.Level;
            infoText.text = $"Level Start {levelId}, Press point button to play";
            goes[2].SetActive(true);
            var trans = goes[2].transform;

            for (int i = 0; i < trans.childCount; i++)
            {
                trans.GetChild(i).gameObject.SetActive(true);
            }
        }
        else if (msg.PointFound != null)
        {
            infoText.text = $"Point Found {msg.PointFound.PointId} on level {msg.PointFound.Level}";
            CheckLevelEnd(msg.PointFound);
        }
        else if (msg.GameFinish != null)
        {
            infoText.text = $"Game Finish";
        }
    }

    void CheckLevelEnd(PointFound point_found)
    {
        if (point_found.PointId < 3) return;

        var level = point_found.Level;

        if(_levelIndex == _levelIds.Count - 1)
        {
            SendAllLevelEnd();
            return;
        }

        SendLevelEnd(level);
        _levelIndex++;
        DelayCall(1f, () => {
            SendLevelPrepared(_levelIds[_levelIndex]);
        });
    }

}
