<commit_before>﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EndGate.Core.Utilities;

namespace EndGate.Core
{
    internal class GameRunner
    {
        private static Lazy<GameRunner> _instance = new Lazy<GameRunner>(() => new GameRunner());
        private ConcurrentDictionary<long, LooperCallback> _callbacks;
        private Looper _gameLoop;

        public GameRunner()
        {
            _callbacks = new ConcurrentDictionary<long, LooperCallback>();
        }

        private void TryLoopStart()
        {
            if (_callbacks.Count == 1)
            {
                _gameLoop = new Looper();
                _gameLoop.Start();
            }
        }

        private void TryLoopStop()
        {
            if (_callbacks.Count == 0)
            {
                _gameLoop.Dispose();
                _gameLoop = null;
            }
        }

        public Action<int> Register(Game game)
        {
            // We do not set the FPS initially (it's set to 0) because we want to allow the Game itself to set the UpdateRate(fps)
            var updateCallback = new LooperCallback
            {
                Callback = game.PrepareUpdate
            };

            _callbacks.TryAdd(game.ID, updateCallback);

            TryLoopStart();

            _gameLoop.AddCallback(updateCallback);

            return (updateRate) =>
            {
                updateCallback.Fps = updateRate;
            };
        }

        public void Unregister(Game game)
        {
            LooperCallback updateCallback;
            _callbacks.TryRemove(game.ID, out updateCallback);
            _gameLoop.RemoveCallback(updateCallback);

            TryLoopStop();
        }

        public static GameRunner Instance
        {
            get
            {
                return _instance.Value;
            }
        }<commit_msg>Re-factored and re-organized Game runner<commit_after>﻿using System;
using System.Collections.Concurrent;
using EndGate.Core.Utilities;

namespace EndGate.Core
{
    internal class GameRunner
    {
        private static Lazy<GameRunner> _instance = new Lazy<GameRunner>(() => new GameRunner());
        private ConcurrentDictionary<long, LooperCallback> _callbacks;
        private Looper _gameLoop;

        public GameRunner()
        {
            _callbacks = new ConcurrentDictionary<long, LooperCallback>();
        }

        public static GameRunner Instance
        {
            get
            {
                return _instance.Value;
            }
        }

        public Action<int> Register(Game game)
        {
            var updateCallback = CreateAndCacheCallback(game);

            // Try to start the loop prior to adding our games callback.  This callback may be the first, hence the "Try"
            TryLoopStart();

            // Add our callback to the game loop (which is now running), it will now be called on an interval dictated by updateCallback
            _gameLoop.AddCallback(updateCallback);

            // Updating the "updateRate" is an essential element to the game configuration.
            // If a game is running slowly we need to be able to slow down the update rate.
            return CreateUpdateRateSetter(updateCallback);
        }

        public void Unregister(Game game)
        {
            LooperCallback updateCallback;
            _callbacks.TryRemove(game.ID, out updateCallback);
            _gameLoop.RemoveCallback(updateCallback);

            TryLoopStop();
        }

        private void TryLoopStart()
        {
            if (_callbacks.Count == 1)
            {
                _gameLoop = new Looper();
                _gameLoop.Start();
            }
        }

        private void TryLoopStop()
        {
            if (_callbacks.Count == 0 && _gameLoop != null)
            {
                _gameLoop.Dispose();
                _gameLoop = null;
            }
        }

        private LooperCallback CreateAndCacheCallback(Game game)
        {
            var updateCallback = new LooperCallback
            {
                Callback = game.PrepareUpdate
            };

            // Add the callback to the callback cache
            _callbacks.TryAdd(game.ID, updateCallback);

            return updateCallback;
        }

        private Action<int> CreateUpdateRateSetter(LooperCallback callback)
        {
            return (updateRate) =>
            {
                callback.Fps = updateRate;
            };
        }
{'fraction_non_alphanumeric': 0.07412252016568563, 'fraction_numerical': 0.0010900370612600828, 'mean_word_length': 1.612756264236902, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 18, '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': '1988580', 'n_tokens_mistral': 1142, 'n_tokens_neox': 1095, 'n_words': 303}