using System.Linq;
using System.Net;
using System.Net.Sockets;
using System;
namespace LobbyServer
{
    class LobbyServer
    {
        public static LobbyServer Instance { get; private set; }

        public ChatManager ChatManager { get; }
        public ScoresManager ScoresManager { get; }

        public LobbyServer()
        {
            Instance = this;

            Console.WriteLine("Starting LobbyServer...");

            ChatManager = new ChatManager();
            ScoresManager = new ScoresManager();

            Console.WriteLine("LobbyServer started");
        }

        public void Start()
        {
            var listener = new TcpListener(IPAddress.Any, 1409);
            listener.Start();

            while (true)
            {
                Console.WriteLine("Waiting client...");
                using (var client = listener.AcceptTcpClient())
                {
                    ClientLoop(client);
                }
            }
        }

        public void Stop()
        {
            Console.WriteLine("Stopping LobbyServer...");

            ScoresManager.Save();

            Console.WriteLine("LobbyServer stopped");
        }

        public void ClientLoop(TcpClient client)
        {
            Console.WriteLine($"Client connected: {client.Client.RemoteEndPoint}");

            var stream = new CommandStream(client.GetStream());

            while (true)
            {
                try
                {
                    var packet = stream.Receive();
                    Console.WriteLine(packet);

                    switch (packet.Command)
                    {
                        case Command.ChatAdd:
                            ChatAdd(packet);
                            break;
                        case Command.ChatRequest:
                            ChatRequest(stream, packet);
                            break;
                        case Command.ScoreAdd:
                            ScoreAdd(packet);
                            break;
                        case Command.ScoreRequest:
                            ScoreRequest(stream, packet);
                            break;
                        case Command.Invalid:
                            throw new InvalidOperationException("Received invalid command");
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Client disconnected: {e.Message}");
                    break;
                }
            }
        }

        private void ChatAdd(CommandPacket packet)
        {
            var message = new Message(packet.Content);

            ChatManager.Add(message);
        }

        private void ChatRequest(CommandStream stream, CommandPacket packet)
        {
            var messages = ChatManager.Messages.Reverse().Take(10).Reverse().Select(x => x.ToString());
            string data = string.Join('@', messages);

            stream.Send(new CommandPacket(Command.ChatRequest, data));
        }

        private void ScoreAdd(CommandPacket packet)
        {
            var score = new ScoreEntry(packet.Content);

            ScoresManager.Add(score);
        }

        private void ScoreRequest(CommandStream stream, CommandPacket packet)
        {
            var scores = ScoresManager.Scores.Take(10).Select(x => x.ToString());
            string data = string.Join('@', scores);

            stream.Send(new CommandPacket(Command.ScoreRequest, data));
        }
    }
}