// edgegap relay transport.
// reuses KcpTransport with custom KcpServer/Client.

//#if MIRROR <- commented out because MIRROR isn't defined on first import yet
using System;
using System.Text.RegularExpressions;
using UnityEngine;
using Mirror;
using kcp2k;

namespace Edgegap
{
    [HelpURL("https://mirror-networking.gitbook.io/docs/manual/transports/edgegap-transports/edgegap-relay")]
    public class EdgegapKcpTransport : KcpTransport
    {
        [Header("Relay")]
        public string relayAddress = "127.0.0.1";
        public ushort relayGameServerPort = 8888;
        public ushort relayGameClientPort = 9999;

        // mtu for kcp transport. respects relay overhead.
        public const int MaxPayload = Kcp.MTU_DEF - Protocol.Overhead;

        [Header("Relay")]
        public bool relayGUI = true;
        public uint userId = 11111111;
        public uint sessionId = 22222222;

        // helper
        internal static String ReParse(String cmd, String pattern, String defaultValue)
        {
            Match match = Regex.Match(cmd, pattern);
            return match.Success ? match.Groups[1].Value : defaultValue;
        }

        protected override void Awake()
        {
            // logging
            //   Log.Info should use Debug.Log if enabled, or nothing otherwise
            //   (don't want to spam the console on headless servers)
            if (debugLog)
                Log.Info = Debug.Log;
            else
                Log.Info = _ => {};
            Log.Warning = Debug.LogWarning;
            Log.Error = Debug.LogError;

            // create config from serialized settings.
            // with MaxPayload as max size to respect relay overhead.
            config = new KcpConfig(DualMode, RecvBufferSize, SendBufferSize, MaxPayload, NoDelay, Interval, FastResend, false, SendWindowSize, ReceiveWindowSize, Timeout, MaxRetransmit);

            // client (NonAlloc version is not necessary anymore)
            client = new EdgegapKcpClient(
                () => OnClientConnected.Invoke(),
                (message, channel) => OnClientDataReceived.Invoke(message, FromKcpChannel(channel)),
                () => OnClientDisconnected?.Invoke(), // may be null in StopHost(): https://github.com/MirrorNetworking/Mirror/issues/3708
                (error, reason) => OnClientError.Invoke(ToTransportError(error), reason),
                config
            );

            // server
            server = new EdgegapKcpServer(
                (connectionId) => OnServerConnected.Invoke(connectionId),
                (connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, FromKcpChannel(channel)),
                (connectionId) => OnServerDisconnected.Invoke(connectionId),
                (connectionId, error, reason) => OnServerError.Invoke(connectionId, ToTransportError(error), reason),
                config);

            if (statisticsLog)
                InvokeRepeating(nameof(OnLogStatistics), 1, 1);

            Debug.Log("EdgegapTransport initialized!");
        }

        protected override void OnValidate()
        {
            // show max message sizes in inspector for convenience.
            // 'config' isn't available in edit mode yet, so use MTU define.
            ReliableMaxMessageSize = KcpPeer.ReliableMaxMessageSize(MaxPayload, ReceiveWindowSize);
            UnreliableMaxMessageSize = KcpPeer.UnreliableMaxMessageSize(MaxPayload);
        }

        // client overwrites to use EdgegapClient instead of KcpClient
        public override void ClientConnect(string address)
        {
            // connect to relay address:port instead of the expected server address
            EdgegapKcpClient client = (EdgegapKcpClient)this.client;
            client.userId = userId;
            client.sessionId = sessionId;
            client.connectionState = ConnectionState.Checking; // reset from last time
            client.Connect(relayAddress, relayGameClientPort);
        }
        public override void ClientConnect(Uri uri)
        {
            if (uri.Scheme != Scheme)
                throw new ArgumentException($"Invalid url {uri}, use {Scheme}://host:port instead", nameof(uri));

            // connect to relay address:port instead of the expected server address
            EdgegapKcpClient client = (EdgegapKcpClient)this.client;
            client.Connect(relayAddress, relayGameClientPort, userId, sessionId);
        }

        // server overwrites to use EdgegapServer instead of KcpServer
        public override void ServerStart()
        {
            // start the server
            EdgegapKcpServer server = (EdgegapKcpServer)this.server;
            server.Start(relayAddress, relayGameServerPort, userId, sessionId);
        }

        void OnGUIRelay()
        {
            // if (server.IsActive()) return;

            GUILayout.BeginArea(new Rect(300, 30, 200, 100));

            GUILayout.BeginHorizontal();
            GUILayout.Label("SessionId:");
            sessionId = Convert.ToUInt32(GUILayout.TextField(sessionId.ToString()));
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            GUILayout.Label("UserId:");
            userId = Convert.ToUInt32(GUILayout.TextField(userId.ToString()));
            GUILayout.EndHorizontal();

            if (NetworkServer.active)
            {
                EdgegapKcpServer server = (EdgegapKcpServer)this.server;
                GUILayout.BeginHorizontal();
                GUILayout.Label("State:");
                GUILayout.Label(server.state.ToString());
                GUILayout.EndHorizontal();
            }
            else if (NetworkClient.active)
            {
                EdgegapKcpClient client = (EdgegapKcpClient)this.client;
                GUILayout.BeginHorizontal();
                GUILayout.Label("State:");
                GUILayout.Label(client.connectionState.ToString());
                GUILayout.EndHorizontal();
            }

            GUILayout.EndArea();
        }

        // base OnGUI only shows in editor & development builds.
        // here we always show it because we need the sessionid & userid buttons.
#pragma warning disable CS0109
        new void OnGUI()
        {
#if UNITY_EDITOR || DEVELOPMENT_BUILD
            base.OnGUI();
#endif
            if (relayGUI) OnGUIRelay();
        }

        public override string ToString() => "Edgegap Kcp Transport";
    }
#pragma warning restore CS0109
}
//#endif MIRROR <- commented out because MIRROR isn't defined on first import yet
