using SimpleCore.Library;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;


namespace StandardScene
{
    public  class LadderLogic
    {
	    /// below defines some useful functions...
        
        private static ConcurrentDictionary<string, (DateTime dt, int state)> pressedDT = new ConcurrentDictionary<string, (DateTime dt, int state)>();
        // if active for millis, trigger once.
        public static void TriggerOnce(bool active, int millis, Action trigger,int index=0,
            [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
        {
            var id = $"{sourceFilePath}:{sourceLineNumber+ index}";
            if (!active)
            {
                if (pressedDT.TryGetValue(id, out var pair1) && pair1.state != 1)
                    pressedDT.TryRemove(id, out _);
                return;
            }
            if (pressedDT.TryGetValue(id, out var pair))
            {
                if (pair.state != 0) return;
                if ((DateTime.Now - pair.dt).TotalMilliseconds > millis)
                {
                    pressedDT[id] = (pair.dt, 1);
                    Task.Run(() =>
                    {
                        try
                        {
                            trigger();
                        }
                        catch (Exception ex)
                        {
                            Diagnosis.Post($"Ex={ExceptionFormatter.FormatEx(ex)}", $"timed_trigger-{id}");
                            pressedDT.TryRemove(id, out _);
                        }

                        pressedDT[id] = (pair.dt, 2);
                    });
                }
            }
            else
                pressedDT[id] = (DateTime.Now, 0);
        }

        private static ConcurrentDictionary<string, bool> isochronous = new ConcurrentDictionary<string, bool>();
        public static object lockobj = new object();
        public static void IsochronousFork(Action action,
            [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
        {
            var id = $"{sourceFilePath}:{sourceLineNumber}";
            lock (lockobj)
            {
                if (isochronous.TryGetValue(id, out var calling) && calling) return;
                isochronous[id] = true;
            }

            Task.Run(() =>
            {
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    Diagnosis.Post($"Ex={ExceptionFormatter.FormatEx(ex)}", $"isochrounous_fork-{id}");
                }
                isochronous[id] = false;
            });
        }

        private static Dictionary<MethodInfo, object> keepTrack = new Dictionary<MethodInfo, object>();
        /// <summary>
        /// including first call.
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <param name="getter"></param>
        /// <param name="action">action(T1 old)</param>
        public static void TriggerIfChanged<T1>(Func<T1> getter, Action<T1> action)
        {
            var id = getter.Method;
            var val = getter.Invoke();
            if (keepTrack.TryGetValue(id, out var t) && t is T1 old)
            {
                if (val.Equals(old)) return;
                action(old);
            }
            else action(default);
            keepTrack[id] = val;
        }

        
        public static void FlipFlop<T1>(ref T1 target, int millis, params T1[] vs)
        {
            target = vs[(((long)DateTime.Now.TimeOfDay.TotalMilliseconds) / millis) % vs.Length];
        }
        
    }
}