using System;
using UnityEngine;
using UnityEngine.UI;
using GameAnvil;
using GameAnvil.Defines;
using Protocol;
using UnityEngine.SceneManagement;
using Random = UnityEngine.Random;

public class ConnectHandler : MonoBehaviour
{
    private static ConnectHandler instance;

    [Header("Object Reference")]
    public Text serverInfoText;
    public InputField serverInfoInput;
    public Text clientInfoText;
    public Text logText;

    public string ip = "127.0.0.1";
    public int port = 18200;

    public string deviceId = "";
    public string password = "";
    public string accountId = "";

    public int roomId;

    public GameObject popupCanvas;
    public InputField roomIdInput;

    private static GameAnvilConnector connector;
    private static GameAnvilUser user;

    public GameAnvilConnector getConnector()
    {
        if (connector == null)
        {
            connector = new GameAnvilConnector();
            
            // RegisterProtocol은 Auth 전에 해야 한다.
            GameAnvilProtocolManager.RegisterProtocol(BasicProtocolReflection.Descriptor); 
            GameAnvilProtocolManager.RegisterProtocol(PuzzleReflection.Descriptor); 
        }

        return connector;
    }

    public GameAnvilUser getUser()
    {
        if (user == null)
        {
            user = new GameAnvilUser(getConnector(), "BASIC_SERVICE", 1);
        }

        return user;
    }
    
    void Start()
    {
        // 연결 정보를 화면에 출력합니다.
        serverInfoText.text = "서버정보:";
        serverInfoInput.text = ip;
    }
    
    private void Awake()
    {
        DontDestroyOnLoad(this.gameObject);
    }

    public async void Connect()
    {
        ip = serverInfoInput.text;

        getConnector().OnDisconnect += (resultCode, payload) =>
        {
            Debug.Log("Disconnected");
        };
        
        try
        {
            await getConnector().Connect(ip, port);
            Debug.Log("CONNECT_SUCCESS");
            logText.text = "CONNECT_SUCCESS";
        }
        catch (Exception e)
        {
            Debug.Log("CONNECT_FAIL");
            logText.text = "CONNECT_FAIL";
            throw;
        }
    }


    public async void Auth()
    {
        accountId = Random.Range(1000, 9999) + "";
        clientInfoText.text = "클라이언트정보 : " + accountId;
        
        try
        {
            var result = await connector.Authentication(deviceId, accountId, password);
            Debug.Log(result.ResultCode.ToString());
            logText.text = result.ResultCode.ToString();
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            logText.text = e.ToString();
            throw;
        }
    }

    public async void Login()
    {
        try
        {
            var result = await getUser().Login("USER_TYPE_BASIC", "ch1");
            Debug.Log(result.ResultCode.ToString());
            logText.text = result.ResultCode.ToString();
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            logText.text = e.ToString();
            throw;
        }
    }

    public async void CreateRoom()
    {
        try
        {
            var result = await getUser().CreateRoom(string.Empty, "ROOM_TYPE_BASIC", string.Empty);
            Debug.Log(result.ResultCode.ToString());
            logText.text = result.ResultCode.ToString();
            if (result.ResultCode == ResultCodeCreateRoom.CREATE_ROOM_SUCCESS)
            {
                Debug.Log("Room Id : " + result.Data.RoomId);
                this.roomId = result.Data.RoomId;
                SceneManager.LoadScene("GameScene");
            }
        }
        catch(Exception e)
        {
            Debug.Log(e.ToString());
            logText.text = e.ToString();
        }
    }

    private void Update()
    {
        if (popupCanvas && popupCanvas.activeInHierarchy && Input.GetKeyDown(KeyCode.Return))
        {
            JoinRoom();
        }
    }

    public async void JoinRoom()
    {
        if (!string.IsNullOrEmpty(roomIdInput.text))
        {
            try
            {
                var result = await getUser().JoinRoom("ROOM_TYPE_BASIC", int.Parse(roomIdInput.text), string.Empty);
                Debug.Log(result.ResultCode.ToString());
                logText.text = result.ResultCode.ToString();
                if (result.ResultCode == ResultCodeJoinRoom.JOIN_ROOM_SUCCESS)
                {
                    Debug.Log("Room Id : " + result.Data.RoomId);
                    this.roomId = result.Data.RoomId;
                    SceneManager.LoadScene("GameScene");
                }
            }
            catch (Exception e)
            {
                Debug.Log(e.ToString());
                logText.text = e.ToString();
            }
        }
    }

    public void MatchUser()
    {
        try
        {
            getUser().OnMatchUserDone += OnMatchUserDone;
            var result = getUser().MatchUserStart("ROOM_TYPE_BASIC", string.Empty);
            Debug.Log("MATCHING_USER...");
            logText.text = "MATCHING_USER...";
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            logText.text = e.ToString();
            throw;
        }
    }

    private void OnMatchUserDone(GameAnvilUser user, ResultCodeMatchUserDone resultCode, MatchResult matchResult)
    {
        getUser().OnMatchUserDone -= OnMatchUserDone;
        Debug.Log("Room Id : " + matchResult.RoomId);
        this.roomId = matchResult.RoomId;
        SceneManager.LoadScene("GameScene");
    }
    
    public async void CancelMatchMaking()
    {
        try
        {
            var result = await user.MatchUserCancel("ROOM_TYPE_BASIC");
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            logText.text = e.ToString();
            throw;
        }
    }
    
    public async void MatchRoom()
    {
        try
        {
            var result = await user.MatchRoom(true, true, "ROOM_TYPE_BASIC", "", "");
            if (result.ResultCode == ResultCodeMatchRoom.MATCH_ROOM_SUCCESS)
            {
                Debug.Log("Room Id : " + result.Data.RoomId);
                this.roomId = result.Data.RoomId;
                SceneManager.LoadScene("GameScene");
            }
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            logText.text = e.ToString();
            throw;
        }
    }
    
    private void OnApplicationPause(bool pause)
    {
        if(!getConnector().IsConnected) 
            return;
        
        if (pause)
        {
            // 입력한 시간동안 서버의 clientStateCheck 기능을 정지시킨다.
            // 이시간이 지나면 clientStateCheck 기능이 동작하여 연결이 끊어질 수 있다.
            getConnector().PauseClientStateCheck(10 * 60);
        }
        else
        {
            // 서버의 clientStateCheck 기능을 다시 동작시킨다.
            getConnector().ResumeClientStateCheck();
        }
    }

    public void Quit()
    {
        if (getConnector().IsConnected)
        {
            getConnector().Disconnect();
        }
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#else
        Application.Quit();
#endif
    }
}