using PlantBox.Shared.Communication;
using PlantBox.Shared.Communication.Commands;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace PlantBox.Broker
{
    abstract class TcpManager
    {
        public Broker Broker { get; }

        protected abstract string LogPrefix { get; }
        protected abstract int ListeningPort { get; }

        private TcpListener _listener;

        public TcpManager(Broker broker)
        {
            Log("Initializing...");

            Broker = broker;
            _listener = new TcpListener(IPAddress.Any, ListeningPort);
        }

        public void Start()
        {
            Log("Starting...");

            _listener.Start();

            Log("Started");

            TcpLoop();
        }

        private void TcpLoop()
        {
            while (Broker.IsRunning)
            {
                Log("Waiting client...");

                var client = _listener.AcceptTcpClient();

                Log($"Found client: {client.Client.RemoteEndPoint}");

                ClientLoop(client);
            }
        }

        private void ClientLoop(TcpClient client)
        {
            try
            {
                using (var commandStream = new CommandStream(client.GetStream()))
                {
                    while (client.Connected)
                    {
                        var packet = commandStream.Receive();
                        Log($"Received command from {client.Client.RemoteEndPoint}");
                        Log(packet.ToString());

                        switch (packet.Command)
                        {
                            case Command.Captors:
                                CaptorsCommand(commandStream, packet);
                                break;
                            case Command.Historic:
                                HistoricCommand(commandStream, packet);
                                break;
                            case Command.Info:
                                InfoCommand(commandStream, packet);
                                break;
                            case Command.Ping:
                                PingCommand(commandStream, packet);
                                break;
                        }
                    }
                }

                client.Close();
            }
            catch (Exception ex)
            {
                Log($"Client disconnected: {ex.Message}");
            }
        }

        protected void Log(string message)
        {
            Console.WriteLine($"[{LogPrefix}]({DateTime.Now:HH:mm:ss}) {message}");
        }

        protected abstract void CaptorsCommand(CommandStream commandStream, CommandPacket packet);
        protected abstract void HistoricCommand(CommandStream commandStream, CommandPacket packet);
        protected abstract void InfoCommand(CommandStream commandStream, CommandPacket packet);
        protected abstract void PingCommand(CommandStream commandStream, CommandPacket packet);
    }
}
