using System;
using System.Threading.Tasks;
using UnityEngine;
using GameAnvil;
using GameAnvil.Defines;
using RPSGame = Com.Nhn.Sample.Protocol;

public class GameAnvilConnectorTester : MonoBehaviour
{
    private GameAnvilManager gameAnvilManager;
    private GameAnvilConnector connector;
    private GameAnvilUser user;
    private UIManager UI;
    
    public string host
    {
        get => UI.ipInputField.text;
        set => UI.ipInputField.text = value;
    }
    
    public int port
    {
        get => int.Parse(UI.portInputField.text);
        set => UI.portInputField.text = value.ToString();
    }

    public string accountId
    {
        get => UI.accountIdInputField.text;
        set => UI.accountIdInputField.text = value;
    }

    public string password
    {
        get => UI.passwordInputField.text;
        set => UI.passwordInputField.text = value;
    }

    public string deviceId
    {
        get => UI.deviceIdInputField.text;
        set => UI.deviceIdInputField.text = value;
    }

    public string serviceName
    {
        get => UI.serviceNameInputField.text;
        set => UI.serviceNameInputField.text = value;
    }

    public int subId
    {
        get => int.Parse(UI.subIdInputField.text);
        set => UI.subIdInputField.text = value.ToString();
    }
    
    public string userType
    {
        get => UI.userTypeInputField.text;
        set => UI.userTypeInputField.text = value;
    }

    public string channelId
    {
        get => UI.channelIdInputField.text;
        set => UI.channelIdInputField.text = string.IsNullOrEmpty(value) ? "" : value;
    }

    public string roomType
    {
        get => UI.roomTypeInputField.text;
        set => UI.roomTypeInputField.text = value;
    }

    public int roomId
    {
        get => int.Parse(UI.roomIdInputField.text);
        set => UI.roomIdInputField.text = value.ToString();
    }
    
    public string echoReqMessage
    {
        get => UI.echoReqMessageInputField.text;
        set => UI.echoReqMessageInputField.text = value;
    }

    
    private void Start()
    {
        UI = GameObject.FindWithTag("UIManager").GetComponent<UIManager>();
        connector = new GameAnvilConnector();
        // NOTE) 서버에도 RPSGame 프로토콜 등록 필요. 기초 튜토리얼에서는 ConnectorTest를 포함하지 않으므로 생략되어있음. 추후 필요시 추가 바람.
        GameAnvilProtocolManager.RegisterProtocol(RPSGame.RPSGameReflection.Descriptor);
        
        UI.connectButton.onClick.AddListener(Connect);
        UI.disconnectButton.onClick.AddListener(Disconnect);
        UI.authenticationButton.onClick.AddListener(Authenticate);
        UI.createUserButton.onClick.AddListener(CreateUser);
        UI.loginButton.onClick.AddListener(Login);
        UI.logoutButton.onClick.AddListener(Logout);
        UI.echoButton.onClick.AddListener(RequestEcho);
        UI.echoClearButton.onClick.AddListener(ClearEcho);
        UI.createRoomButton.onClick.AddListener(CreateRoom);
        UI.joinRoomButton.onClick.AddListener(JoinRoom);
        UI.leaveRoomButton.onClick.AddListener(LeaveRoom);
        UI.userMatchMakingButton.onClick.AddListener(MatchUserStart);
        UI.roomMatchMakingButton.onClick.AddListener(MatchRoom);
        UI.cancelMatchMakingButton.onClick.AddListener(CancelMatchMaking);
        
        gameAnvilManager = GameAnvilManager.Instance;
    }

    private void Update()
    {
        UI.isAuthenticatedText.text = connector.IsAuthenticated.ToString();
        
        UI.connectState.Set(connector.IsConnected);
        UI.authenticationState.Set(connector.IsAuthenticated);
        UI.loginState.Set(user != null && user.IsLoggedIn);
        UI.joinedRoomState.Set(user != null && user.IsJoinedRoom );
    }

    public async void Connect()
    {
        try
        {
            await connector.Connect(host, port);
            UI.connectExceptionInputField.text = null;
            UI.consoleInputField.text += "Connect Success" + '\n';
        }
        catch (Exception e)
        {
            UI.connectExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
            UI.consoleInputField.text += "Connect Fail" + '\n';
            throw;
        }
    }

    public async void Disconnect()
    {
        try
        {
            await connector.Disconnect();
            UI.connectExceptionInputField.text = null;
            UI.consoleInputField.text += "Disconnect Success" + '\n';
        }
        catch (Exception e)
        {
            UI.connectExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
            UI.consoleInputField.text += "Disconnect Fail" + '\n';
            throw;
        }
    }

    public async void Authenticate()
    {
        try
        {
            var result = await connector.Authentication(deviceId, accountId, password);
            UI.authenticationResultInputField.text = result.ResultCode.ToString();
            UI.authenticationExceptionInputField.text = null;
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
        }
        catch (Exception e)
        {
            UI.authenticationExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
            throw;
        }
    }

    public void CreateUser()
    {
        try
        {
            user = new GameAnvilUser(connector, serviceName, subId);
            UI.createUserExceptionInputField.text = "Create User Success!";
            UI.consoleInputField.text += "Create User Success" + '\n';
        }
        catch(Exception e)
        {
            UI.createUserExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
            throw;
        }
    }
    public async void Login()
    {
        try
        {
            var result = await user.Login(userType, channelId, new Payload());
            UI.loginResultInputField.text = result.ResultCode.ToString();
            UI.loginExceptionInputField.text = null;
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
        }
        catch (Exception e)
        {
            UI.loginExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
            throw;
        }
    }

    public async void Logout()
    {
        try
        {
            var result = await user.Logout();
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
        }
        catch (Exception e)
        {
            UI.consoleInputField.text += e.ToString() + '\n';
        }
    }
    
    public async void CreateRoom()
    {
        try
        {
            var result = await user.CreateRoom(string.Empty, roomType, string.Empty);
            UI.roomResultInputField.text = result.ResultCode.ToString();
            UI.roomIdResultInputField.text = result.Data.RoomId.ToString();
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
            UI.consoleInputField.text += "Room Id : " + result.Data.RoomId + '\n';
            UI.roomExceptionInputField.text = null;
            
            if (result.ResultCode == ResultCodeCreateRoom.CREATE_ROOM_SUCCESS)
            {
                OnEnterRoom();
            }
        }
        catch(Exception e)
        {
            UI.roomResultInputField.text = null;
            UI.roomIdResultInputField.text = null;
            UI.roomExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
        }
    }

    public async void JoinRoom()
    {
        try
        {
            var result = await user.JoinRoom(roomType, roomId, string.Empty);
            UI.roomResultInputField.text = result.ResultCode.ToString();
            UI.roomIdResultInputField.text = result.Data.RoomId.ToString();
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
            UI.consoleInputField.text += "Room Id : " + result.Data.RoomId + '\n';
            UI.roomExceptionInputField.text = null;
            
            if (result.ResultCode == ResultCodeJoinRoom.JOIN_ROOM_SUCCESS)
            {
                OnEnterRoom();
            }
        }
        catch (Exception e)
        {
            UI.roomResultInputField.text = null;
            UI.roomIdResultInputField.text = null;
            UI.roomExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
        }
    }
    
    public async void LeaveRoom()
    {
        try
        {
            var result = await user.LeaveRoom();
            UI.roomResultInputField.text = result.ResultCode.ToString();
            UI.roomIdResultInputField.text = null;
            UI.roomExceptionInputField.text = null;
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
        }
        catch (Exception e)
        {
            UI.roomResultInputField.text = null;
            UI.roomExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
        }
    }

    public async void MatchUserStart()
    {
        try
        {
            user.OnMatchUserDone += OnMatchUserDone;
            var result = await user.MatchUserStart(roomType, "");
            UI.roomResultInputField.text = result.ResultCode.ToString();
            UI.roomIdResultInputField.text = null;
            UI.roomExceptionInputField.text = null;
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
        }
        catch (Exception e)
        {
            UI.roomResultInputField.text = null;
            UI.roomIdResultInputField.text = null;
            UI.roomExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
        }
    }

    public void OnMatchUserDone(GameAnvilUser user, ResultCodeMatchUserDone resultCode, MatchResult matchResult)
    {
        user.OnMatchUserDone -= OnMatchUserDone;
        UI.roomResultInputField.text = resultCode.ToString();
        UI.roomIdResultInputField.text = matchResult.RoomId.ToString();
        UI.roomExceptionInputField.text = null;
        UI.consoleInputField.text += resultCode.ToString() + '\n';
        UI.consoleInputField.text += "Room Id : " + matchResult.RoomId + '\n';
        
        OnEnterRoom();
    }

    public async void MatchRoom()
    {
        try
        {
            var result = await user.MatchRoom(true, true, roomType, "", "");
            UI.roomResultInputField.text = result.ResultCode.ToString();
            UI.roomIdResultInputField.text = result.Data.RoomId.ToString();
            UI.roomExceptionInputField.text = null;
            UI.consoleInputField.text += result.ResultCode.ToString() + '\n';
            UI.consoleInputField.text += "Room Id : " + result.Data.RoomId + '\n';
            
            if (result.ResultCode == ResultCodeMatchRoom.MATCH_ROOM_SUCCESS)
            {
                OnEnterRoom();
            }
        }
        catch (Exception e)
        {
            UI.roomResultInputField.text = null;
            UI.roomIdResultInputField.text = null;
            UI.roomExceptionInputField.text = e.ToString();
            UI.consoleInputField.text += e.ToString() + '\n';
        }
    }

    public async void CancelMatchMaking()
    {
        try
        {
            var result = await user.MatchUserCancel(roomType);
            UI.roomResultInputField.text = result.ToString();
            UI.roomIdResultInputField.text = null;
            UI.roomExceptionInputField.text = null;
            UI.consoleInputField.text += result.ToString() + '\n';
        }
        catch (Exception e)
        {
            UI.roomExceptionInputField.text = e.ToString();
            UI.roomIdResultInputField.text = null;
            UI.roomResultInputField.text = null;
            UI.consoleInputField.text += e.ToString() + '\n';
        }
    }
    
    public async void RequestEcho()
    { 
        RPSGame.EchoReq echoReq = new RPSGame.EchoReq()
        {
            Data = echoReqMessage
        };
        if (connector != null)
            connector.SetMessageCallback<RPSGame.EchoRes>(OnConnectorEchoRes);
        if (user != null)
            user.SetMessageCallback<RPSGame.EchoRes>(OnUserEchoRes);
        UI.echoConnectionResultInputField.text = null;
        UI.echoUserResultInputField.text = null;
        await user.RequestUser<RPSGame.EchoRes>(echoReq);
    }

    public void ClearEcho()
    {
        UI.echoConnectionResultInputField.text = null;
        UI.echoUserResultInputField.text = null;
    }

    public async Task OnConnectorEchoRes(GameAnvilConnector gameAnvilConnector, ResultCode errorCode, RPSGame.EchoRes echoRes)
    {
        UI.echoConnectionResultInputField.text = echoRes.ToString();
        UI.consoleInputField.text += "Connector : " + echoRes.ToString() + '\n';
        connector.RemoveMessageCallback<RPSGame.EchoRes>();
    }
    
    public async Task OnUserEchoRes(GameAnvilUser gameAnvilUser, ResultCode errorCode, RPSGame.EchoRes echoRes)
    {
        UI.echoUserResultInputField.text = echoRes.ToString();
        UI.consoleInputField.text += "User : " + echoRes.ToString() + '\n';
        user.RemoveMessageCallback<RPSGame.EchoRes>();
    }
    
    private void OnEnterRoom()
    {
        Debug.Log("User enter the room.");
        SyncController.Instance.InstantiateSyncObject();
        SyncController.Instance.Instantiate("Character", Vector3.zero, Quaternion.identity);
    }
}
