<commit_before>﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

using Oxide.Core.Plugins;

namespace Oxide.Core.Libraries
{
    /// <summary>
    /// The timer library
    /// </summary>
    public class Timer : Library
    {
        private static Stopwatch stopwatch;

        private static float CurrentTime => (float)stopwatch.Elapsed.TotalSeconds;

        static Timer()
        {
            stopwatch = new Stopwatch();
            stopwatch.Start();
        }

        /// <summary>
        /// Represents a single timer instance
        /// </summary>
        public class TimerInstance
        {
            /// <summary>
            /// Gets the number of repetitions left on this timer
            /// </summary>
            public int Repetitions { get; private set; }

            /// <summary>
            /// Gets the delay between each repetition
            /// </summary>
            public float Delay { get; private set; }

            /// <summary>
            /// Gets the callback delegate
            /// </summary>
            public Action Callback { get; private set; }

            /// <summary>
            /// Gets if this timer has been destroyed
            /// </summary>
            public bool Destroyed { get; private set; }

            /// <summary>
            /// Gets the plugin to which this timer belongs, if any
            /// </summary>
            public Plugin Owner { get; private set; }

            // The next rep time
            internal float nextrep;

            /// <summary>
            /// Initialises a new instance of the TimerInstance class
            /// </summary>
            /// <param name="repetitions"></param>
            /// <param name="delay"></param>
            /// <param name="callback"></param>
            public TimerInstance(int repetitions, float delay, Action callback, Plugin owner)
            {
                Repetitions = repetitions;
                Delay = delay;
                Callback = callback;
                nextrep = CurrentTime + delay;
                Owner = owner;
                if (owner != null) owner.OnRemovedFromManager += owner_OnRemovedFromManager;
            }

            /// <summary>
            /// Called when the owner plugin was unloaded
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="manager"></param>
            private void owner_OnRemovedFromManager(Plugin sender, PluginManager manager)
            {
                Destroy();
            }

            /// <summary>
            /// Destroys this timer
            /// </summary>
            public void Destroy()
            {
                Destroyed = true;
            }

            /// <summary>
            /// Updates this timer
            /// </summary>
            public void Update()
            {
                if (Destroyed) return;

                nextrep += Delay;

                try
                {
                    Callback();
                }
                catch (Exception ex)
                {
                    Destroy();
                    var error_message = $"Failed to run a {Delay:0.00} timer";
                    if (Owner) error_message += " in " + Owner.Name;
                    Interface.GetMod().RootLogger.WriteException(error_message, ex);
                }

                if (Repetitions > 0)
                {
                    Repetitions--;
                    if (Repetitions == 0) Destroy();
                }
            }
        }
        
        public override bool IsGlobal => false;
        
        private const float updateInterval = .025f;
        private float lastUpdateAt;

        private readonly List<TimerInstance> timers = new List<TimerInstance>();

        /// <summary>
        /// Updates all timers - called every server frame
        /// </summary>
        public void Update()
        {
            if (timers.Count < 1) return;

            var now = CurrentTime;

            if (now < lastUpdateAt)
            {
                var difference = lastUpdateAt - now;
                var msg = $"Time travelling detected! Timers were updated {difference:0.00} seconds in the future? We will attempt to recover but this should really never happen!";
                Interface.GetMod().RootLogger.Write(Logging.LogType.Warning, msg);
                foreach (var timer in timers) timer.nextrep -= difference;
            }
            
            if (now < lastUpdateAt + updateInterval) return;

            var expired = timers.TakeWhile(t => t.nextrep <= now).ToArray();
            if (expired.Length > 0)
            {
                timers.RemoveRange(0, expired.Length);
                foreach (var timer in expired)
                {
                    timer.Update();
                    // Add the timer back to the queue if it needs to fire again
                    if (!timer.Destroyed) InsertTimer(timer);
                }                    
            }
        }

        private TimerInstance AddTimer(int repetitions, float delay, Action callback, Plugin owner = null)
        {
            var timer = new TimerInstance(repetitions, delay, callback, owner);<commit_msg>Even more refactoring Removed C# 6 string interpolation due to mono compiler bugs<commit_after>﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

using Oxide.Core.Plugins;

namespace Oxide.Core.Libraries
{
    /// <summary>
    /// The timer library
    /// </summary>
    public class Timer : Library
    {
        private static Stopwatch stopwatch = Stopwatch.StartNew();

        private static float CurrentTime => (float)stopwatch.Elapsed.TotalSeconds;

        /// <summary>
        /// Represents a single timer instance
        /// </summary>
        public class TimerInstance
        {
            /// <summary>
            /// Gets the number of repetitions left on this timer
            /// </summary>
            public int Repetitions { get; private set; }

            /// <summary>
            /// Gets the delay between each repetition
            /// </summary>
            public float Delay { get; private set; }

            /// <summary>
            /// Gets the callback delegate
            /// </summary>
            public Action Callback { get; private set; }

            /// <summary>
            /// Gets if this timer has been destroyed
            /// </summary>
            public bool Destroyed { get; private set; }

            /// <summary>
            /// Gets the plugin to which this timer belongs, if any
            /// </summary>
            public Plugin Owner { get; private set; }

            // The next rep time
            internal float nextrep;

            /// <summary>
            /// Initialises a new instance of the TimerInstance class
            /// </summary>
            /// <param name="repetitions"></param>
            /// <param name="delay"></param>
            /// <param name="callback"></param>
            public TimerInstance(int repetitions, float delay, Action callback, Plugin owner)
            {
                Repetitions = repetitions;
                Delay = delay;
                Callback = callback;
                nextrep = CurrentTime + delay;
                Owner = owner;
                if (owner != null) owner.OnRemovedFromManager += owner_OnRemovedFromManager;
            }

            /// <summary>
            /// Called when the owner plugin was unloaded
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="manager"></param>
            private void owner_OnRemovedFromManager(Plugin sender, PluginManager manager)
            {
                Destroy();
            }

            /// <summary>
            /// Destroys this timer
            /// </summary>
            public void Destroy()
            {
                Destroyed = true;
                if (Owner != null) Owner.OnRemovedFromManager -= owner_OnRemovedFromManager;
            }

            /// <summary>
            /// Updates this timer
            /// </summary>
            public void Update()
            {
                if (Destroyed) return;

                nextrep += Delay;

                try
                {
                    Callback();
                }
                catch (Exception ex)
                {
                    Destroy();
                    var error_message = string.Format("Failed to run a {0:0.00} timer", Delay);
                    if (Owner) error_message += " in " + Owner.Name;
                    Interface.GetMod().RootLogger.WriteException(error_message, ex);
                }

                if (Repetitions > 0)
                {
                    Repetitions--;
                    if (Repetitions == 0) Destroy();
                }
            }
        }
        
        public override bool IsGlobal => false;
        
        private const float updateInterval = .025f;
        private float lastUpdateAt;

        private readonly List<TimerInstance> timers = new List<TimerInstance>();

        /// <summary>
        /// Updates all timers - called every server frame
        /// </summary>
        public void Update()
        {
            if (timers.Count < 1) return;

            var now = CurrentTime;

            if (now < lastUpdateAt)
            {
                var difference = lastUpdateAt - now;
                var msg = string.Format("Time travelling detected! Timers were updated {0:0.00} seconds in the future? We will attempt to recover but this should really never happen!", difference);
                Interface.GetMod().RootLogger.Write(Logging.LogType.Warning, msg);
                foreach (var timer in timers) timer.nextrep -= difference;
            }
            
            if (now < lastUpdateAt + updateInterval) return;

            var expired = timers.TakeWhile(t => t.nextrep <= now).ToArray();
            if (expired.Length > 0)
            {
                timers.RemoveRange(0, expired.Length);
                foreach (var timer in expired)
                {
                    timer.Update();
                    // Add the timer back to the queue if it needs to fire again
                    if (!timer.Destroyed) InsertTimer(timer);
                }                    
            }
        }

        private TimerInstance AddTimer(int repetitions, float delay, Action callback, Plugin owner = null)
        {
            var timer = new TimerInstance(repetitions, delay, callback, owner);
{'fraction_non_alphanumeric': 0.0979526671675432, 'fraction_numerical': 0.0029113448534936136, 'mean_word_length': 1.2667092379736058, 'pattern_counts': {'":': 0, '<': 83, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5259734', 'n_tokens_mistral': 2583, 'n_tokens_neox': 2386, 'n_words': 763}