﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Net;
using System.Net.Sockets;

using System.IO;

namespace SNet.Net
{
    public class SNetTcpServer : IDisposable
    {

        public delegate void OnConnectedDelegate(SNetTcpClient Client);
        public event OnConnectedDelegate OnClientConnected;

        public delegate void OnDisconnectedDelegate(SNetTcpClient Client);
        public event OnDisconnectedDelegate OnClientDisconnected;

        public delegate void OnPacketCompletionDelegate(SNetTcpClient Client, ref byte[] Data, UInt16 PacketHeader);
        public event OnPacketCompletionDelegate OnClientPacketComplete;

        public delegate void OnPartialPacketRecievedDelegate(SNetTcpClient Client, UInt16 PacketHeader, int RecievedBytes, UInt32 PacketLenght);
        public event OnPartialPacketRecievedDelegate OnClientPartialPacketCompleted;

        public delegate void OnPartialPacketSendtDelegate(SNetTcpClient Client, byte[] Data, UInt16 PacketHeader, int SendtBytes, int PacketLenght);
        public event OnPartialPacketSendtDelegate OnClientPartialPacketSendt;

        public TcpListener Server {get; private set;}
        public List<SNetTcpClient> Clients;

        public SNetTcpServer(UInt16 Port)
        {
            Server = new TcpListener(IPAddress.Any, Port);
            Clients = new List<SNetTcpClient>();
        }

        public void StartServer()
        {
            Server.Start();
            Server.BeginAcceptTcpClient(new AsyncCallback(OnTcpClientAccepted), null);
        }

        public void StopServer()
        {
            Server.Stop();
        }

        private void OnTcpClientAccepted(IAsyncResult ar)
        {
            try
            {
                SNetTcpClient Client = new SNetTcpClient(Server.EndAcceptTcpClient(ar));

                Client.OnDisconnected += InternalOnClientDisconnected;
                Client.OnPacketCompleted += InternalOnClientPacketComplete;
                Client.OnPartialPacketCompleted += InternalOnClientPartialPacketComplete;
                Client.OnPartialPacketSendt += InternalOnPartialPacketSendt;

                Clients.Add(Client);
                if (OnClientConnected == null)
                { return; }
                OnConnectedDelegate EventDelegate = OnClientConnected;
                EventDelegate(Client);

                Server.BeginAcceptTcpClient(new AsyncCallback(OnTcpClientAccepted), null);
            }
            catch
            {

            }
        }

        private void InternalOnPartialPacketSendt(SNetTcpClient Client, byte[] Data, ushort PacketHeader, int SendtBytes, int PacketLenght)
        {
            if (OnClientPartialPacketSendt == null)
            { return; }
            OnPartialPacketSendtDelegate EventDelegate = OnClientPartialPacketSendt;
            EventDelegate(Client, Data, PacketHeader, SendtBytes, PacketLenght);
        }

        private void InternalOnClientPartialPacketComplete(SNetTcpClient Client, UInt16 PacketHeader, int RecievedBytes, uint PacketLenght)
        {
            if (OnClientPartialPacketCompleted == null)
            { return; }
            OnPartialPacketRecievedDelegate EventDelegate = OnClientPartialPacketCompleted;
            EventDelegate(Client, PacketHeader, RecievedBytes, PacketLenght);
        }

        private void InternalOnClientPacketComplete(SNetTcpClient Client, ref byte[] Data, UInt16 PacketHeader)
        {
            if (OnClientPacketComplete == null)
            { return; }
            OnPacketCompletionDelegate EventDelegate = OnClientPacketComplete;
            EventDelegate(Client, ref Data, PacketHeader);

        }

        private void InternalOnClientDisconnected(SNetTcpClient Client)
        {
            if (Clients != null)
            { Clients.Remove(Client); }
            if (OnClientDisconnected != null)
            {
                OnDisconnectedDelegate EventDelegate = OnClientDisconnected;
                EventDelegate(Client);
            }

            Client.Dispose();
        }


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool Disposing)
        {
            if (Disposing)
            {
                if (Server.IsValid())
                {
                    Server.Stop();
                    if (Server.Server != null)
                    {
                        Server.Server.Close();
                        Server.Server.Dispose();
                    }
                    Server = null;
                }

                foreach (SNetTcpClient Client in Clients)
                {
                    if (Client != null)
                    {
                        Client.Dispose();
                    }
                }
                Clients = null;

            }
        }
    }
}
