<commit_before>﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using D2MPMaster.Browser;
using D2MPMaster.Client;
using D2MPMaster.LiveData;
using D2MPMaster.Model;
using D2MPMaster.Server;
using d2mpserver;
using MongoDB.Driver.Builders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using XSockets.Core.Common.Globals;
using XSockets.Core.Common.Socket;
using XSockets.Core.Common.Socket.Event.Arguments;
using XSockets.Core.XSocket;
using XSockets.Core.XSocket.Helpers;
using PluginRange = XSockets.Plugin.Framework.PluginRange;

namespace D2MPMaster.Lobbies
{
    /// <summary>
    /// Manages the lifecycle of lobbies and the list.
    /// </summary>
    [XSocketMetadata("LobbyManager", Constants.GenericTextBufferSize, PluginRange.Internal)]
    public class LobbyManager : XSocketController, IDisposable
    {
        private static readonly ClientController ClientsController = new ClientController();
        private static readonly BrowserController Browsers = new BrowserController();
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// Lobbies that should be visible in the list.
        /// </summary>
        public static ObservableCollection<Lobby> PublicLobbies = new ObservableCollection<Lobby>();

        /// <summary>
        /// Lobbies that should have full updates sent out JUST to users in them.
        /// </summary>
        public static ObservableCollection<Lobby> PlayingLobbies = new ObservableCollection<Lobby>();

        public static volatile bool Registered = false;

        public static List<Lobby> LobbyQueue = new List<Lobby>();

        public static Queue<JObject> PublicLobbyUpdateQueue = new Queue<JObject>();
        public static Thread LobbyUpdateThread;

        public static volatile bool shutdown = false;

        public LobbyManager()
        {
            if (!Registered)
            {
                LobbyUpdateThread = new Thread(LobbyUpdateProc);
                LobbyUpdateThread.Start();
                PublicLobbies.CollectionChanged += TransmitLobbiesChange;
                Registered = true;
            }
        }

        public void Dispose()
        {
            shutdown = true;
            Registered = false;
        }

        public void TransmitLobbiesChange(object s, NotifyCollectionChangedEventArgs e)
        {
            if (shutdown) return;
            if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                PublicLobbyUpdateQueue.Enqueue(DiffGenerator.RemoveAll("publicLobbies"));
            }
            else
            {
                if (e.NewItems != null)
                    foreach (var lobby in e.NewItems)
                    {
                        switch (e.Action)
                        {
                            case NotifyCollectionChangedAction.Add:
                                PublicLobbyUpdateQueue.Enqueue(lobby.Add("publicLobbies"));
                                break;
                        }
                    }
                if (e.OldItems != null)
                    foreach (var lobby in e.OldItems)
                    {
                        switch (e.Action)
                        {
                            case NotifyCollectionChangedAction.Remove:
                                PublicLobbyUpdateQueue.Enqueue(lobby.Remove("publicLobbies"));
                                break;
                        }
                    }
            }

        }

        public void LobbyUpdateProc()
        {
            while (!shutdown)
            {
                Thread.Sleep(500);
                var upd = new JObject();
                var updates = new JArray();
                while (PublicLobbyUpdateQueue.Count > 0)
                {
                    updates.Add(PublicLobbyUpdateQueue.Dequeue());
                }
                if (updates.Count == 0) continue;
                upd["msg"] = "colupd";
                upd["ops"] = updates;
                var msg = upd.ToString(Formatting.None);
                Browsers.AsyncSendTo(m=>m.user!=null&&m.lobby==null,new TextArgs(msg, "lobby"), ar => { });
            }
        }

        /// <summary>
        /// See if a user is already in a lobby.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static PlayerLocation FindPlayer(User user)
        {
            PlayerLocation plyr = null;
            string steamID = user.services.steam.steamid;
            foreach (var lobby in PlayingLobbies)
            {
                if (lobby.radiant.Any(player => player.steam == steamID))
                {
                    plyr = new PlayerLocation()
                           {
                               lobby = lobby,
                               goodguys = true
                           };
                }
                if (lobby.dire.Any(player => player.steam == steamID))
                {
                    plyr = new PlayerLocation()
                    {
                        lobby = lobby,
                        goodguys = false
                    };
                }
                if (plyr != null) break;
            }
            return plyr;
        }

        /// <summary>
        /// Exit the lobby queue.
        /// </summary>
        /// <param name="lobby"></param>
        public static void CancelQueue(Lobby lobby)
        {
            if (LobbyQueue.Contains(lobby))
            {
                LobbyQueue.Remove(lobby);
                lobby.status = LobbyStatus.Start;
                PublicLobbies.Add(lobby);
                TransmitLobbyUpdate(lobby, new []{"status"});
                CalculateQueue();
            }
        }

        public static void CalculateQueue()
        {

            foreach (var lobby in LobbyQueue.ToArray())
            {
                var server = ServerManager.FindForLobby(lobby);
                if (server == null) continue;
                GameInstance instance = server.StartInstance(lobby);
                lobby.status = LobbyStatus.Configure;
                TransmitLobbyUpdate(lobby, new []{"status"});
                SendLaunchDota(lobby);
                LobbyQueue.Remove(lobby);
            }
        }

        /// <summary>
        /// Enter the lobby queue.
        /// </summary>
        /// <param name="lobby"></param>
        public static void StartQueue(Lobby lobby)
        {
            if (!LobbyQueue.Contains(lobby))
            {
                LobbyQueue.Add(lobby);
                lobby.status = LobbyStatus.Queue;
                PublicLobbies.Remove(lobby);
                TransmitLobbyUpdate(lobby, new[]{"status"});
                CalculateQueue();
            }
        }

        public static void TransmitLobbyUpdate(Lobby lobby, string[] fields)
        {
            //Generate message<commit_msg>Launch dota when queue starts<commit_after>﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using D2MPMaster.Browser;
using D2MPMaster.Client;
using D2MPMaster.LiveData;
using D2MPMaster.Model;
using D2MPMaster.Server;
using d2mpserver;
using MongoDB.Driver.Builders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using XSockets.Core.Common.Globals;
using XSockets.Core.Common.Socket;
using XSockets.Core.Common.Socket.Event.Arguments;
using XSockets.Core.XSocket;
using XSockets.Core.XSocket.Helpers;
using PluginRange = XSockets.Plugin.Framework.PluginRange;

namespace D2MPMaster.Lobbies
{
    /// <summary>
    /// Manages the lifecycle of lobbies and the list.
    /// </summary>
    [XSocketMetadata("LobbyManager", Constants.GenericTextBufferSize, PluginRange.Internal)]
    public class LobbyManager : XSocketController, IDisposable
    {
        private static readonly ClientController ClientsController = new ClientController();
        private static readonly BrowserController Browsers = new BrowserController();
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// Lobbies that should be visible in the list.
        /// </summary>
        public static ObservableCollection<Lobby> PublicLobbies = new ObservableCollection<Lobby>();

        /// <summary>
        /// Lobbies that should have full updates sent out JUST to users in them.
        /// </summary>
        public static ObservableCollection<Lobby> PlayingLobbies = new ObservableCollection<Lobby>();

        public static volatile bool Registered = false;

        public static List<Lobby> LobbyQueue = new List<Lobby>();

        public static Queue<JObject> PublicLobbyUpdateQueue = new Queue<JObject>();
        public static Thread LobbyUpdateThread;

        public static volatile bool shutdown = false;

        public LobbyManager()
        {
            if (!Registered)
            {
                LobbyUpdateThread = new Thread(LobbyUpdateProc);
                LobbyUpdateThread.Start();
                PublicLobbies.CollectionChanged += TransmitLobbiesChange;
                Registered = true;
            }
        }

        public void Dispose()
        {
            shutdown = true;
            Registered = false;
        }

        public void TransmitLobbiesChange(object s, NotifyCollectionChangedEventArgs e)
        {
            if (shutdown) return;
            if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                PublicLobbyUpdateQueue.Enqueue(DiffGenerator.RemoveAll("publicLobbies"));
            }
            else
            {
                if (e.NewItems != null)
                    foreach (var lobby in e.NewItems)
                    {
                        switch (e.Action)
                        {
                            case NotifyCollectionChangedAction.Add:
                                PublicLobbyUpdateQueue.Enqueue(lobby.Add("publicLobbies"));
                                break;
                        }
                    }
                if (e.OldItems != null)
                    foreach (var lobby in e.OldItems)
                    {
                        switch (e.Action)
                        {
                            case NotifyCollectionChangedAction.Remove:
                                PublicLobbyUpdateQueue.Enqueue(lobby.Remove("publicLobbies"));
                                break;
                        }
                    }
            }

        }

        public void LobbyUpdateProc()
        {
            while (!shutdown)
            {
                Thread.Sleep(500);
                var upd = new JObject();
                var updates = new JArray();
                while (PublicLobbyUpdateQueue.Count > 0)
                {
                    updates.Add(PublicLobbyUpdateQueue.Dequeue());
                }
                if (updates.Count == 0) continue;
                upd["msg"] = "colupd";
                upd["ops"] = updates;
                var msg = upd.ToString(Formatting.None);
                Browsers.AsyncSendTo(m=>m.user!=null&&m.lobby==null,new TextArgs(msg, "lobby"), ar => { });
            }
        }

        /// <summary>
        /// See if a user is already in a lobby.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static PlayerLocation FindPlayer(User user)
        {
            PlayerLocation plyr = null;
            string steamID = user.services.steam.steamid;
            foreach (var lobby in PlayingLobbies)
            {
                if (lobby.radiant.Any(player => player.steam == steamID))
                {
                    plyr = new PlayerLocation()
                           {
                               lobby = lobby,
                               goodguys = true
                           };
                }
                if (lobby.dire.Any(player => player.steam == steamID))
                {
                    plyr = new PlayerLocation()
                    {
                        lobby = lobby,
                        goodguys = false
                    };
                }
                if (plyr != null) break;
            }
            return plyr;
        }

        /// <summary>
        /// Exit the lobby queue.
        /// </summary>
        /// <param name="lobby"></param>
        public static void CancelQueue(Lobby lobby)
        {
            if (LobbyQueue.Contains(lobby))
            {
                LobbyQueue.Remove(lobby);
                lobby.status = LobbyStatus.Start;
                PublicLobbies.Add(lobby);
                TransmitLobbyUpdate(lobby, new []{"status"});
                CalculateQueue();
            }
        }

        public static void CalculateQueue()
        {

            foreach (var lobby in LobbyQueue.ToArray())
            {
                var server = ServerManager.FindForLobby(lobby);
                if (server == null) continue;
                GameInstance instance = server.StartInstance(lobby);
                lobby.status = LobbyStatus.Configure;
                TransmitLobbyUpdate(lobby, new []{"status"});
                LobbyQueue.Remove(lobby);
            }
        }

        /// <summary>
        /// Enter the lobby queue.
        /// </summary>
        /// <param name="lobby"></param>
        public static void StartQueue(Lobby lobby)
        {
            if (!LobbyQueue.Contains(lobby))
            {
                LobbyQueue.Add(lobby);
                lobby.status = LobbyStatus.Queue;
                PublicLobbies.Remove(lobby);
                TransmitLobbyUpdate(lobby, new[]{"status"});
                SendLaunchDota(lobby);
                CalculateQueue();
            }
        }

        public static void TransmitLobbyUpdate(Lobby lobby, string[] fields)
        {
            //Generate message
{'fraction_non_alphanumeric': 0.0923464390653421, 'fraction_numerical': 0.0019588638589618022, 'mean_word_length': 1.4390035830063128, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 69, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3810872', 'n_tokens_mistral': 3668, 'n_tokens_neox': 3461, 'n_words': 767}