﻿using BNEBotCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BNEBotLauncher
{
  public class LauncherServer : IDisposable
  {
    public LauncherServer(int port)
    {
      m_AcceptThread = new Thread(ServerProc);
      m_AcceptThread.Start(port);

      m_UpdateThread = new Thread(UpdateProc);
      m_UpdateThread.Start();
    }

    private void ServerProc(object port)
    {
      m_Server = new TcpListener(IPAddress.Any, (int)port);
      m_Server.Start();

      while (!m_Exit)
      {
        try
        {
          TcpClient client = m_Server.AcceptTcpClient();
          BotManagerConnection connection = new BotManagerConnection(client, () => { m_Exit = true; });

          lock (m_DataLock)
          {
            m_Connections.Add(connection);
          }
        }
        catch (SocketException)
        {
          // Just exit
          break;
        }
      }

      //m_Server.Stop();
    }

    private void UpdateProc()
    {
      while (!m_Exit)
      {
        if (m_Connections.Count > 0)
        {
          // TODO: for now, we are just sending the whole state every once in a while. We should rewrite
          // this to only send update notifications
          if ((DateTime.Now - m_LastInfoSend).TotalSeconds > 1)
          {
            m_LastInfoSend = DateTime.Now;

            List<BotInfo> bots = BotProcessManager.GetBotInfo();
            BotInfoCommand cmd = new BotInfoCommand()
            {
              UpdatedBots = bots
            };
            cmd.Finalize();

            lock (m_DataLock)
            {
              List<BotManagerConnection> droppedConnections = null;
              foreach (BotManagerConnection connection in m_Connections)
              {
                try
                {
                  connection.SendMessage(cmd);
                }
                catch (IOException)
                {
                  // An IO exception here means the client has disconnected
                  if (droppedConnections == null)
                    droppedConnections = new List<BotManagerConnection>();

                  droppedConnections.Add(connection);
                }
              }

              if (droppedConnections != null)
              {
                foreach (BotManagerConnection connection in droppedConnections)
                {
                  m_Connections.Remove(connection);
                }
              }
            }
          }

          // Update our connections
          lock (m_DataLock)
          {
            foreach (BotManagerConnection connection in m_Connections)
            {
              connection.Receive();
            }
          }
        }

        Thread.Sleep(100);
      }
    }

    public bool IsRunning
    {
      get { return !m_Exit; }
    }

    #region IDisposable
    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected void Dispose(bool disposing)
    {
      if (!m_Disposed)
      {
        if (disposing)
        {
          m_Exit = true;
          m_Server.Stop();
          //m_AcceptThread.Join();
          m_UpdateThread.Join();
          m_AcceptThread.Abort();
          //m_UpdateThread.Abort();
        }
      }

      m_Disposed = true;
    }

    private bool m_Disposed;
    #endregion

    private TcpListener m_Server;
    private Thread m_AcceptThread;
    private Thread m_UpdateThread;
    private bool m_Exit;

    private List<BotManagerConnection> m_Connections = new List<BotManagerConnection>();
    private object m_DataLock = new object();

    // FIXME: we are sending all data repeatedly for now
    private DateTime m_LastInfoSend = DateTime.Now;
  }
}
