using Rokid.MRC;
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;

//连接状态
public enum ConnectStatus
{
    Undefined,
    Success,
    ServerFull,               //服务端连接数已满
    LoggedInAgain,            //其他客户端登录，掉线
    UserRequestedDisconnect,  //客户端主动断开连接
    GenericDisconnect,        //断开连接
    Reconnecting,             //断线重连
    IncompatibleBuildType,    //客户端的Build类型不符合服务端的要求。如果客户端是Debug版本，不允许连接
    HostEndedSession,         //Host服务端主动关闭连接
    StartHostFailed,          //启动Host服务端失败
    StartClientFailed,         //启动Client失败
    AlreadyHaveVideoClient,              //只能有一个视频流
}

public struct ReconnectMessage
{
    public int CurrentAttempt;
    public int MaxAttempt;

    public ReconnectMessage(int currentAttempt, int maxAttempt)
    {
        CurrentAttempt = currentAttempt;
        MaxAttempt = maxAttempt;
    }
}

public struct ConnectionEventMessage : INetworkSerializeByMemcpy
{
    public ConnectStatus ConnectStatus;
    public string PlayerName;
}

public static class EventDefine
{
    //网络连接
    public static readonly string ServerConnectionEvent = "ServerConnectionEvent";
    public static readonly string ClientReConnectionEvent = "ClientReConnectionEvent";
    public static readonly string ClientDisConnectionEvent = "ClientDisConnectionEvent";
    public static readonly string ConnectStatusEvent = "ConnectStatusEvent";
}

//连接状态机管理器
public class ConnectionManager : Singleton<ConnectionManager>
{
    //当前的连接状态
    [SerializeField]
    public ConnectionState m_CurrentState;

    public NetworkManager NetworkManager;

    //重连次数
    public int ReconnectAttempts = 2;
    //最大连接数
    public int MaxConnectedPlayers = 8;

    //初始化几个状态
    internal readonly OfflineState m_Offline = new OfflineState();

    //连接
    public ConnectionMethodIP ConnectionMethod;
    private ProfileManager m_ProfileManager;

    //客户端：OfflineState——ClientConnectingState——ClientConnectedState——ClientReconnectingState(断线重连)/OfflineState(服务端被动断连)/DisconnectingWithReasonState(服务端主动断连)
    internal readonly ClientConnectingState m_ClientConnecting = new ClientConnectingState();
    internal readonly ClientConnectedState m_ClientConnected = new ClientConnectedState();
    internal readonly ClientReconnectingState m_ClientReconnecting = new ClientReconnectingState();
    internal readonly DisconnectingWithReasonState m_DisconnectingWithReason = new DisconnectingWithReasonState();

    //服务端：OfflineState——StartingHostState——OfflineState（启动失败）/HostingState（启动成功）
    internal readonly StartingHostState m_StartingHost = new StartingHostState();
    internal readonly HostingState m_Hosting = new HostingState();

    private List<ConnectionState> connectionStateList;

    public override void Init()
    {
        List<ConnectionState> connectionStateList = new List<ConnectionState>()
        {
            m_Offline,
            m_ClientConnecting,
            m_ClientConnected,
            m_ClientReconnecting,
            m_DisconnectingWithReason,
            m_StartingHost,
            m_Hosting
        };

        //初始化每个状态
        foreach(var state in connectionStateList)
        {
            state.m_ConnectionManager = this;
        }

        m_CurrentState = m_Offline;

        NetworkManager.OnClientConnectedCallback += OnClientConnectedCallback;
        NetworkManager.OnClientDisconnectCallback += OnClientDisconnectCallback;
        NetworkManager.OnServerStarted += OnServerStarted;
        NetworkManager.ConnectionApprovalCallback += ApprovalCheck;
        NetworkManager.OnTransportFailure += OnTransportFailure;

        ConnectionMethod = new ConnectionMethodIP(this, m_ProfileManager);
    }

    public void SetNetworkManager(NetworkManager manager)
    {
        NetworkManager = manager;
    }

    public override void OnDestroy()
    {
        base.OnDestroy();

        NetworkManager.OnClientConnectedCallback -= OnClientConnectedCallback;
        NetworkManager.OnClientDisconnectCallback -= OnClientDisconnectCallback;
        NetworkManager.OnServerStarted -= OnServerStarted;
        NetworkManager.ConnectionApprovalCallback -= ApprovalCheck;
        NetworkManager.OnTransportFailure -= OnTransportFailure;
    }

    internal void ChangeState(ConnectionState nextState)
    {
        Debug.Log($"ChangeState: Changed connection state from {m_CurrentState.GetType().Name} to {nextState.GetType().Name}.");

        if(m_CurrentState != null)
        {
            m_CurrentState.Exit();
        }

        m_CurrentState = nextState;
        m_CurrentState.Enter();
    }

    void OnClientDisconnectCallback(ulong clientId)
    {
        m_CurrentState.OnClientDisconnect(clientId);
    }

    void OnClientConnectedCallback(ulong clientId)
    {
        m_CurrentState.OnClientConnected(clientId);
    }

    void OnServerStarted()
    {
        m_CurrentState.OnServerStarted();
    }

    void ApprovalCheck(NetworkManager.ConnectionApprovalRequest request, NetworkManager.ConnectionApprovalResponse response)
    {
        m_CurrentState.ApprovalCheck(request, response);
    }

    void OnTransportFailure()
    {
        m_CurrentState.OnTransportFailure();
    }

    public void StartClientIp(string ipaddress, int port)
    {
        m_CurrentState.StartClientIP(ipaddress, port);
    }

    public void StartHostIp(string ipaddress, int port)
    {
        m_CurrentState.StartHostIP(ipaddress, port);
    }

    //外部调用，客户端主动发起断开
    public void RequestShutdown()
    {
        m_CurrentState.OnUserRequestedShutdown();
    }

    //-------------------------------自定义消息处理

    //自定义的消息，当调用NetworkManager.StartClient方法，就会初始化NetworkSceneManager和CustomMessagingManager，同时回调此处方法
    public void RegisterCustomMessages()
    {
        NetworkManager.CustomMessagingManager.RegisterNamedMessageHandler(nameof(ReceiveServerToClientSetDisconnectReason_CustomMessage), ReceiveServerToClientSetDisconnectReason_CustomMessage);
    }

    //客户端收到服务端的断开连接，并附加原因
    void ReceiveServerToClientSetDisconnectReason_CustomMessage(ulong clientID, FastBufferReader reader)
    {
        reader.ReadValueSafe(out ConnectStatus status);

        m_CurrentState.OnDisconnectReasonReceived(status);
    }

    //发送DisconnectReason信息给所有客户端
    public void SendServerToAllClientsSetDisconnectReason(ConnectStatus status)
    {
        var writer = new FastBufferWriter(sizeof(ConnectStatus), Allocator.Temp);
        writer.WriteValueSafe(status);
        NetworkManager.CustomMessagingManager.SendNamedMessageToAll(nameof(ReceiveServerToClientSetDisconnectReason_CustomMessage), writer);
    }

    //发送DisconnectReason信息给indicated客户端
    public void SendServerToClientSetDisconnectReason(ulong clientID, ConnectStatus status)
    {
        var writer = new FastBufferWriter(sizeof(ConnectStatus), Allocator.Temp);
        writer.WriteValueSafe(status);
        NetworkManager.CustomMessagingManager.SendNamedMessage(nameof(ReceiveServerToClientSetDisconnectReason_CustomMessage), clientID, writer);
    }
}
