using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using FastArena.Network.Packets;
using UnityEngine;

namespace FastArena.Network
{
    public class ServerConfig
    {
        public int PortFirst = 26782;
        public int MaxPlayers = 20;
    }
    
    public static class Server
    {
        private static UdpClient _udp;
        private static TcpListener _tcp;

        public class ClientConnection
        {
            // public int id { get; private set; }
            private TcpClient _tcp;
            
            public ClientConnection(TcpClient tcp)
            {
                _tcp = tcp;
            }

            public IPEndPoint GetEndPoint()
            {
                return (IPEndPoint)_tcp.Client.RemoteEndPoint;
            }
        }

        public static readonly List<ClientConnection> Connections = new List<ClientConnection>();
        
        public static void Start(ServerConfig config)
        {
            _tcp = new TcpListener(IPAddress.Any, config.PortFirst);
            _udp = new UdpClient(config.PortFirst + 1);
            _tcp.Start();
            _tcp.BeginAcceptTcpClient(TcpAcceptCallback, null);
            _udp.BeginReceive(UdpReceiveCallback, null);
        }


        private static void TcpAcceptCallback(IAsyncResult ar)
        {
            var tcpClient = _tcp.EndAcceptTcpClient(ar);
            var connection = new ClientConnection(tcpClient);
            Connections.Add(connection);
        }
        
        private static void UdpReceiveCallback(IAsyncResult ar)
        {
            IPEndPoint clientEndPoint = new IPEndPoint(IPAddress.None, 0);
            byte[] data = _udp.EndReceive(ar, ref clientEndPoint);
            // begin receiving new data immediately
            _udp.BeginReceive(UdpReceiveCallback, null);

            PacketType t = PacketParser.ReadHeader(data);
            switch (t)
            {
                case PacketType.Invalid:
                    break;
                default:
                    Debug.Log($"received unexpected packet of type {t}");
                    break;
            }
        }

        private static void UdpSendTo(byte[] data, IPEndPoint cliendEnd)
        {
            _udp.BeginSend(data, data.Length, cliendEnd, null, null);
        }
        
        private static void UdpSendTo(byte[] data, ClientConnection client)
        {
            UdpSendTo(data, client.GetEndPoint());
        }

        public static void SendTransformUpdate(int id, Transform transform)
        {
            byte[] data = StructBinaryConverter.GetBytes(new TransformUpdatePacket(id, transform));
            foreach(var p in Connections)
            {
                UdpSendTo(data, p);
            }
        }
    }
}