﻿using System;
using BeardedManStudios.Network;
using Dissonance.Networking;
using BNetworking = BeardedManStudios.Network.Networking;

namespace Dissonance.Integrations.ForgeNetworking
{
    public class ForgeServer
        : BaseServer<ForgeServer, ForgeClient, ForgePeer>
    {
        private readonly ForgeCommsNetwork _network;

        private readonly BMSByte _sendBuffer = new BMSByte();

        private NetWorker _connected;

        public ForgeServer(ForgeCommsNetwork network)
        {
            _network = network;
        }

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

            _connected = BNetworking.PrimarySocket;
            _connected.AddCustomDataReadEvent(unchecked((uint)_network.SystemMessagesChannelToServer), ReceiveFromNetwork);
            _connected.AddCustomDataReadEvent(unchecked((uint)_network.VoiceDataChannelToServer), ReceiveFromNetwork);
            _connected.playerDisconnected += ForgeDisconnectEvent;
        }

        private void ForgeDisconnectEvent(NetworkingPlayer player)
        {
            ClientDisconnected(new ForgePeer(player));
        }

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

            if (_connected != null)
            {
                _connected.RemoveCustomDataReadEvent(unchecked((uint)_network.SystemMessagesChannelToServer));
                _connected.RemoveCustomDataReadEvent(unchecked((uint)_network.VoiceDataChannelToServer));
                _connected.playerDisconnected -= ForgeDisconnectEvent;
                _connected = null;
            }
        }

        protected override void ReadMessages()
        {
            //This is empty, so where do we read the forge messages!?
            //Forge delivers it's packets in event handlers, look above in `Connect()` to find the event handlers being setup
        }

        private void ReceiveFromNetwork(NetworkingPlayer source, NetworkingStream data)
        {
            var packet = new ArraySegment<byte>(data.Bytes.byteArr, data.Bytes.StartPointer, data.Bytes.Size);
            NetworkReceivedPacket(new ForgePeer(source), packet);
        }

        private void Send(ArraySegment<byte> packet, ForgePeer connection, uint channel, bool reliable)
        {
            if (_network.PreprocessPacketToClient(packet, connection, channel, reliable))
                return;

            _sendBuffer.Clear();
            _sendBuffer.BlockCopy(packet.Array, packet.Offset, packet.Count);

            BNetworking.WriteCustom(
                channel,
                BNetworking.PrimarySocket,
                _sendBuffer,
                connection.Peer,
                reliable
            );
        }

        protected override void SendReliable(ForgePeer connection, ArraySegment<byte> packet)
        {
            Send(packet, connection, unchecked((uint)_network.SystemMessagesChannelToClient), true);
        }

        protected override void SendUnreliable(ForgePeer connection, ArraySegment<byte> packet)
        {
            Send(packet, connection, unchecked((uint)_network.VoiceDataChannelToClient), false);
        }
    }
}
