using Microsoft.VisualBasic.CompilerServices;
using PCL.Core.App;
using PCL.Core.Utils;
using System.Collections;
using System.IO;
using System.Windows.Shell;
using PCL.Network;
using PCL.Network.Loaders;

using PCL.Core.App.Localization;
namespace PCL;

public static class ModLoader
{
    public enum LoaderFolderRunType
    {
        RunOnUpdated,
        ForceRun,
        UpdateOnly
    }

    // 任务栏进度条
    public static ModBase.SafeList<LoaderBase> loaderTaskbar = new();
    public static double loaderTaskbarProgress; // 平滑后的进度
    private static TaskbarItemProgressState loaderTaskbarProgressLast = TaskbarItemProgressState.None;

    // 文件夹刷新类委托
    private static readonly Dictionary<LoaderBase, LoaderFolderDictionaryEntry> loaderFolderDictionary = new();

    public static void LoaderTaskbarAdd<T>(LoaderCombo<T> loader)
    {
        if (ModMain.frmSpeedLeft is not null)
            ModMain.frmSpeedLeft.TaskRemove(loader);
        loaderTaskbar.Add(loader);
        ModBase.Log($"[Taskbar] {loader.name} 已加入任务列表");
    }

    public static void LoaderTaskbarProgressRefresh()
    {
        try
        {
            TaskbarItemProgressState newState;
            var newProgress = LoaderTaskbarProgressGet();
            // 若单个任务已中止，或全部任务已完成，则刷新并移除
            foreach (var Task in loaderTaskbar)
                if (loaderTaskbar.All(l => l.State != ModBase.LoadState.Loading) ||
                    Task.State == ModBase.LoadState.Waiting || Task.State == ModBase.LoadState.Aborted)
                {
                    ModMain.frmSpeedLeft?.TaskRefresh(Task);
                    loaderTaskbar.Remove(Task);
                    ModBase.Log($"[Taskbar] {Task.name} 已移出任务列表");
                }

            // 更新平滑后的进度
            if (newProgress <= 0d || newProgress >= 1d || loaderTaskbarProgress > newProgress)
                loaderTaskbarProgress = newProgress;
            else
                loaderTaskbarProgress = loaderTaskbarProgress * 0.9d + newProgress * 0.1d;
            ModBase.RunInUi(() => ModMain.frmMain.BtnExtraDownload.Progress = loaderTaskbarProgress);
            // 更新任务栏信息
            if (!loaderTaskbar.Any() || loaderTaskbarProgress == 1d)
            {
                newState = TaskbarItemProgressState.None;
            }
            else if (loaderTaskbarProgress < 0.015d)
            {
                newState = TaskbarItemProgressState.Indeterminate;
            }
            else
            {
                newState = TaskbarItemProgressState.Normal;
                ModMain.frmMain.TaskbarItemInfo.ProgressValue = loaderTaskbarProgress;
            }

            if (loaderTaskbarProgressLast != newState)
            {
                loaderTaskbarProgressLast = newState;
                ModMain.frmMain.TaskbarItemInfo.ProgressState = newState;
                ModMain.frmMain.BtnExtraDownload.ShowRefresh();
            }
        }
        catch (Exception ex)
        {
            ModBase.Log(
                ex,
                "刷新任务栏进度显示失败",
                ModBase.LogLevel.Feedback,
                userSummary: Lang.Text("Application.Loader.Error.OperationFailed"));
        }
    }

    public static double LoaderTaskbarProgressGet()
    {
        try
        {
            if (!loaderTaskbar.Any())
                return 1d;

            return ModBase.MathClamp(
                loaderTaskbar.Select(l => l.Progress).Average(),
                0,
                1
            );
        }
        catch (Exception ex)
        {
            ModBase.Log(
                ex,
                "获取任务栏进度出错",
                ModBase.LogLevel.Feedback,
                userSummary: Lang.Text("Application.Loader.Error.OperationFailed"));
            return 0.5d;
        }
    }

    /// <summary>
    ///     执行以文件夹检测作为输入的加载器。加载器需以文件夹路径为输入值。
    ///     返回是否执行了加载器。
    /// </summary>
    /// <param name="extraPath">用于检查文件夹修改的额外路径。该路径不会传入加载器。</param>
    /// <param name="loaderInput">如果不想要文件夹路径为输入值，则传入期望数据</param>
    public static bool LoaderFolderRun(LoaderBase loader, string folderPath, LoaderFolderRunType type, int maxDepth = 0,
        string extraPath = "", bool waitForExit = false, object loaderInput = null)
    {
        DirectoryInfo folderInfo;
        var value = new LoaderFolderDictionaryEntry { folderPath = folderPath + extraPath, lastCheckTime = default };
        try
        {
            // 获取数据
            folderInfo = new DirectoryInfo(folderPath + extraPath);
            value.lastCheckTime = folderInfo.Exists ? GetActualLastWriteTimeUtc(folderInfo, maxDepth) : null;
            // 如果已经检查过，则跳过
            if (type == LoaderFolderRunType.RunOnUpdated && loaderFolderDictionary.ContainsKey(loader))
            {
                if (folderInfo.Exists)
                {
                    if (loaderFolderDictionary[loader].lastCheckTime is not null &&
                        value.Equals(loaderFolderDictionary[loader]))
                        return false;
                }
                else if (loaderFolderDictionary[loader].lastCheckTime is null)
                {
                    return false;
                }
            }
        }
        catch (Exception ex)
        {
            ModBase.Log(ex, "文件夹加载器启动检测出错");
        }

        // 写入检查数据
        loaderFolderDictionary[loader] = value;
        // 开始检查
        if (type == LoaderFolderRunType.UpdateOnly)
            return false;
        if (waitForExit)
            loader.WaitForExit(loaderInput ?? folderPath, isForceRestart: true);
        else
            loader.Start(loaderInput ?? folderPath, true);
        return true;
    }

    private static DateTime GetActualLastWriteTimeUtc(DirectoryInfo folderInfo, int maxDepth)
    {
        var time = folderInfo.LastWriteTimeUtc;
        if (maxDepth > 0)
            foreach (var Folder in folderInfo.EnumerateDirectories())
            {
                var folderTime = GetActualLastWriteTimeUtc(Folder, maxDepth - 1);
                if (folderTime > time)
                    time = folderTime;
            }

        return time;
    }

    // 各类加载器
    /// <summary>
    ///     加载器的统一基类。
    /// </summary>
    public abstract class LoaderBase : ILoadingTrigger
    {
        public delegate void OnStateChangedThreadEventHandler(LoaderBase loader, ModBase.LoadState newState,
            ModBase.LoadState oldState);

        public delegate void OnStateChangedUiEventHandler(LoaderBase loader, ModBase.LoadState newState,
            ModBase.LoadState oldState);

        public delegate void PreviewFinishEventHandler(LoaderBase loader);

        // 等待结束
        public const string waitForExitTimeoutMessage = "等待加载器执行超时。";

        /// <summary>
        ///     用于状态改变检测的同步锁。
        /// </summary>
        public readonly object lockState = new();



        /// <summary>
        ///     使用 LoaderCombo 加载时，该任务是否会阻碍后续任务的进行。
        /// </summary>
        public bool block = true;

        public bool hasOnStateChangedThread;

        /// <summary>
        ///     当前加载器是否由 IsForceRestart 强制调起。
        ///     这个属性自身不会干任何事，而是提供给加载器执行的函数，使得加载器调用另一个加载器时，可以继承强制重启属性。
        /// </summary>
        public bool isForceRestarting;

        /// <summary>
        ///     加载器的名称。
        /// </summary>
        public string name;

        /// <summary>
        ///     父加载器。
        /// </summary>
        public LoaderBase parent;

        /// <summary>
        ///     该加载器是否显示在列表中。
        /// </summary>
        public bool show = true;

        // 基础属性
        /// <summary>
        ///     加载器的标识编号。
        /// </summary>
        public int Uuid = ModBase.GetUuid();

        public LoaderBase()
        {
            name = "未命名任务 " + Uuid + "#";
        }

        /// <summary>
        ///     最上级的加载器。
        /// </summary>
        public LoaderBase RealParent
        {
            get
            {
                LoaderBase realParentRet = default;
                try
                {
                    realParentRet = parent;
                    while (realParentRet is not null && realParentRet.parent is not null)
                        realParentRet = realParentRet.parent;
                }
                catch (Exception ex)
                {
                    ModBase.Log(
                        ex,
                        "获取父加载器失败（" + name + "）",
                        ModBase.LogLevel.Feedback,
                        userSummary: Lang.Text("Application.Loader.Error.OperationFailed"));
                    return null;
                }

                return realParentRet;
            }
        }

        /// <summary>
        ///     简易的在 UI 线程添加触发事件的方式。主要用于在新建 Loader 时直接使用 With 绑定事件，以及进行老代码兼容。
        /// </summary>
        public Action<LoaderBase> OnStateChanged
        {
            set { OnStateChangedUi += (loader, newState, oldState) => value(loader); }
        }

        // 状态监控
        /// <summary>
        ///     加载器的状态。
        /// </summary>
        public ModBase.LoadState State
        {
            get => field;
            set
            {
                if (field == value)
                    return;
                var oldState = field;
                if (value == ModBase.LoadState.Finished && Config.Debug.AddRandomDelay)
                    Thread.Sleep(RandomUtils.NextInt(100, 2000));
                field = value;
                ModBase.Log("[Loader] 加载器 " + name + " 状态改变：" + ModBase.GetStringFromEnum(value));
                // 实现 ILoadingTrigger 接口与 OnStateChanged 回调
                ModBase.RunInUi(() =>
                {
                    switch (value)
                    {
                        case ModBase.LoadState.Loading:
                        {
                            LoadingState = MyLoading.MyLoadingState.Run;
                            break;
                        }
                        case ModBase.LoadState.Failed:
                        {
                            LoadingState = MyLoading.MyLoadingState.Error;
                            break;
                        }

                        default:
                        {
                            LoadingState = MyLoading.MyLoadingState.Stop;
                            break;
                        }
                    }

                    OnStateChangedUi?.Invoke(this, value, oldState);
                });
                if (hasOnStateChangedThread)
                    ModBase.RunInThread(() => OnStateChangedThread?.Invoke(this, value, oldState));
            }
        } = ModBase.LoadState.Waiting;

        /// <summary>
        ///     若加载器出错，可提供给外部参考的异常。
        /// </summary>
        public Exception Error { get; set; }

        // 进度监控
        /// <summary>
        ///     加载器的执行进度，为 0 至 1 的小数。
        /// </summary>
        public virtual double Progress
        {
            get
            {
                switch (State)
                {
                    case ModBase.LoadState.Waiting:
                    {
                        return 0d;
                    }
                    case ModBase.LoadState.Loading:
                    {
                        return field == -1 ? 0.02d : field;
                    }

                    default:
                    {
                        return 1d;
                    }
                }
            }
            set
            {
                if (field == value)
                    return;
                var oldValue = field;
                field = value;
                ProgressChanged?.Invoke(value, oldValue);
            }
        } = -1;

        /// <summary>
        ///     计算总进度时的权重。它应该为预计时间（秒）。
        /// </summary>
        public double ProgressWeight { get; set; } = 1d;

        public bool IsLoader { get; } = true;

        public MyLoading.MyLoadingState LoadingState
        {
            get => field;
            set
            {
                if (field == value)
                    return;
                var oldState = field;
                field = value;
                LoadingStateChanged?.Invoke(value, oldState);
            }
        } = MyLoading.MyLoadingState.Stop;

        public event ILoadingTrigger.LoadingStateChangedEventHandler? LoadingStateChanged;
        public event ILoadingTrigger.ProgressChangedEventHandler? ProgressChanged;

        public virtual void InitParent(LoaderBase parent)
        {
            this.parent = parent;
        }

        // 事件

        /// <summary>
        ///     当状态改变时，在工作线程触发代码。在添加事件后，必须将 HasOnStateChangedThread 设为 True。
        /// </summary>
        public event OnStateChangedThreadEventHandler? OnStateChangedThread;

        /// <summary>
        ///     当状态改变时，在 UI 线程触发代码。
        /// </summary>
        public event OnStateChangedUiEventHandler? OnStateChangedUi;

        /// <summary>
        ///     在加载器目标事件执行完成，加载器状态即将变为 Finish 时调用。可以视为扩展加载器目标事件。
        /// </summary>
        public event PreviewFinishEventHandler? PreviewFinish;

        protected void RaisePreviewFinish()
        {
            PreviewFinish?.Invoke(this);
        }

        // 状态变化
        public abstract void Start(object? input = null, bool isForceRestart = false);
        public abstract void Abort();

        /// <summary>
        ///     无限期地等待加载器完成，直到结束或抛出异常。若加载器尚未开始，则会开始执行。
        /// </summary>
        public void WaitForExit(object input = null, LoaderBase loaderToSyncProgress = null,
            bool isForceRestart = false)
        {
            Start(input, isForceRestart);
            while (State == ModBase.LoadState.Loading)
            {
                if (loaderToSyncProgress is not null)
                    loaderToSyncProgress.Progress = Progress;
                Thread.Sleep(10);
            }

            if (State == ModBase.LoadState.Finished)
            {
            }
            else if (State == ModBase.LoadState.Aborted)
            {
                throw new ThreadInterruptedException("加载器执行已中断。");
            }
            else if (Error is null)
            {
                throw new Exception("未知错误！");
            }
            else
            {
                throw new Exception(Error.Message, Error);
            } // 保留调用堆栈，同时不影响信息输出与单元测试
        }

        /// <summary>
        ///     等待加载器完成，直到结束、抛出异常或超时。若加载器尚未开始，则会开始执行。
        /// </summary>
        /// <param name="timeout">等待的超时时间，以毫秒为单位。</param>
        /// <param name="timeoutMessage">若执行超时，将会抛出的异常信息。</param>
        public void WaitForExitTime(int timeout, object input = null, string timeoutMessage = waitForExitTimeoutMessage,
            object loaderToSyncProgress = null, bool isForceRestart = false)
        {
            Start(input, isForceRestart);
            while (State == ModBase.LoadState.Loading)
            {
                if (loaderToSyncProgress is not null)
                    ((dynamic)loaderToSyncProgress).Progress = Progress;
                Thread.Sleep(10);
                timeout -= 10;
                if (timeout < 0)
                    throw new TimeoutException(timeoutMessage);
            }

            if (State == ModBase.LoadState.Finished)
            {
            }
            else if (State == ModBase.LoadState.Aborted)
            {
                throw new ThreadInterruptedException("加载器执行已中断。");
            }
            else if (Error is null)
            {
                throw new Exception("未知错误！");
            }
            else
            {
                throw Error;
            }
        }

        // 相同重载
        public override bool Equals(object obj)
        {
            var @base = obj as LoaderBase;
            return @base is not null && Uuid == @base.Uuid;
        }
    }

    // 说实话，我真的觉得 C# 应该学学 VB 的那种近乎 Java 泛型擦除的兼容性，省掉一堆麻烦
    public abstract class LoaderTask : LoaderBase
    {
        /// <summary>
        ///     上次完成加载时的时间。
        /// </summary>
        public long lastFinishedTime;

        /// <summary>
        ///     最后一次运行加载器的线程。可能为 Nothing，或线程已结束。
        /// </summary>
        public Task? lastRunningTask;

        /// <summary>
        ///     在输入相同时使用原有结果的超时，单位为毫秒。
        /// </summary>
        public int reloadTimeout = -1;

        // 状态指示
        /// <summary>
        ///     当前执行线程是否应当中断。只应用在加载器的工作线程中判断，不可跨线程调用。
        /// </summary>
        public bool IsAborted => IsAbortedWithThread(Task.CurrentId ?? -1);

        /// <summary>
        ///     当前执行线程是否应当中断。需要手动提供加载器线程，用于需要跨线程检查的情况。
        /// </summary>
        public bool IsAbortedWithThread(int compareTaskId)
        {
            return lastRunningTask is null || compareTaskId != lastRunningTask.Id ||
                   State == ModBase.LoadState.Aborted;
        }

        public abstract bool ShouldStart(ref object? input, bool isForceRestart = false, bool ignoreReloadTimeout = false);

        // 装箱！装箱！装箱圣地！
        public abstract object? StartGetInputNoType(object? input = null, Func<object>? inputDelegate = null);

    }

    /// <summary>
    ///     用于异步执行并监控单一函数的加载器。
    /// </summary>
    public class LoaderTask<InputType, OutputType> : LoaderTask
    {
        // 输入输出
        public InputType input;
        protected internal Func<InputType?>? inputDelegate;

        // 执行事件
        protected internal Action<LoaderTask<InputType, OutputType>> loadDelegate;
        public OutputType output = default;

        private CancellationTokenSource? cancelToken;

        // 线程设定
        protected internal ThreadPriority threadPriority;

        public LoaderTask(string name, Action<LoaderTask<InputType, OutputType>> loadDelegate,
            Func<InputType?>? inputDelegate = null, ThreadPriority priority = ThreadPriority.Normal)
        {
            this.name = name;
            this.loadDelegate = loadDelegate;
            this.inputDelegate = inputDelegate;
        }

        // 获取输入
        public InputType? StartGetInput(InputType? input = default, Func<InputType?>? inputDelegate = null) // InputDelegate 参数存在匿名调用
        {
            inputDelegate ??= this.inputDelegate;
            // 按照龙猫的逻辑，此处将 input 与默认值直接进行等价比较，若相等则认为 input 未传入具体值，而调用 inputDelegate 获取
            // 这种逻辑未考虑值类型恰好传入 default 值 (如 double 传了 0.0) 的情况，这是一个陷阱，可能会产生 undefined behavior
            if (EqualityComparer<InputType>.Default.Equals(input, default) && inputDelegate is not null)
                ModBase.RunInUiWait(() => input = inputDelegate());
            return input;
        }

        public override object? StartGetInputNoType(object? input = null, Func<object?>? inputDelegate = null)
        {
            return StartGetInput(input is null ? default : (InputType?)input, inputDelegate is null ? null : () => (InputType?)inputDelegate());
        }

        // 代码执行
        public override bool ShouldStart(ref object? input, bool isForceRestart = false, bool ignoreReloadTimeout = false)
        {
            // 获取输入
            try
            {
                input = StartGetInput(Conversions.ToGenericParameter<InputType>(input));
            }
            catch (Exception ex)
            {
                ModBase.Log(
                    ex,
                    "加载输入获取失败（" + name + "）",
                    ModBase.LogLevel.Hint,
                    userSummary: Lang.Text("Application.Loader.Error.OperationFailed"));
                Error = ex;
                lock (lockState)
                {
                    State = ModBase.LoadState.Failed;
                }
            }

            // 检验输入以确定情况
            if (isForceRestart)
                return true; // 强制要求重启
            if (input is null != this.input is null || (input is not null && !input.Equals(this.input)))
                return true; // 输入不同
            if ((State == ModBase.LoadState.Loading || State == ModBase.LoadState.Finished) && (ignoreReloadTimeout ||
                    reloadTimeout == -1 || lastFinishedTime == 0L ||
                    TimeUtils.GetTimeTick() - lastFinishedTime < reloadTimeout)) // 正在加载或已结束
                // 没有超时
                return false; // 则不重试

            return true;
            // 需要开始
        }

        public override void Start(object input = null, bool isForceRestart = false)
        {
            // 确认是否开始加载
            if (ShouldStart(ref input, isForceRestart))
            {
                // 输入不同或失败，开始加载
                if (State == ModBase.LoadState.Loading)
                    TriggerThreadAbort();
                this.input = Conversions.ToGenericParameter<InputType>(input);
                lock (lockState)
                {
                    State = ModBase.LoadState.Loading;
                    Progress = -1;
                }
            }
            else return;

            // 如果线程是因为判断到 IsAborted 而提前中止，则代表已有新线程被重启，此时不应当改为 Aborted
            // 如果线程是在没有 IsAborted 时手动引发了 ThreadInterruptedException，则代表没有重启线程，这通常代表用户手动取消，应当改为 Aborted
            lastRunningTask = Task.Run(() =>
            {
                try
                {
                    isForceRestarting = isForceRestart;
                    if (ModBase.modeDebug)
                        ModBase.Log(
                            $"[Loader] 加载线程 {name} ({Task.CurrentId}) 已{(isForceRestarting ? "强制" : "")}启动");
                    loadDelegate(this);
                    if (IsAborted)
                    {
                        ModBase.Log(
                            $"[Loader] 加载线程 {name} ({Task.CurrentId}) 已中断但线程正常运行至结束，输出被弃用（最新线程：{(lastRunningTask is null ? -1 : lastRunningTask.Id)}）",
                            ModBase.LogLevel.Developer);
                        return;
                    }

                    if (ModBase.modeDebug)
                        ModBase.Log($"[Loader] 加载线程 {name} ({Task.CurrentId}) 已完成");
                    RaisePreviewFinish();
                    State = ModBase.LoadState.Finished;
                    lastFinishedTime = TimeUtils.GetTimeTick();
                }
                catch (ModBase.CancelledException ex)
                {
                    if (ModBase.modeDebug)
                        ModBase.Log(ex,
                            $"加载线程 {name} ({Task.CurrentId}) 已触发取消中断，已完成 {Math.Round(Progress * 100d)}%");
                    if (!IsAborted) State = ModBase.LoadState.Aborted;
                }
                catch (ThreadInterruptedException ex)
                {
                    if (ModBase.modeDebug)
                        ModBase.Log(ex,
                            $"加载线程 {name} ({Task.CurrentId}) 已触发线程中断，已完成 {Math.Round(Progress * 100d)}%");
                    if (!IsAborted) State = ModBase.LoadState.Aborted;
                }
                catch (Exception ex)
                {
                    if (IsAborted) return;
                    ModBase.Log(ex,
                        $"加载线程 {name} ({Task.CurrentId}) 出错，已完成 {Math.Round(Progress * 100d)}%",
                        ModBase.LogLevel.Developer);
                    Error = ex;
                    State = ModBase.LoadState.Failed;
                }
            }, (cancelToken ??= new CancellationTokenSource()).Token); // 未中断，本次输出有效
            // LastRunningTask.Start(); // 不能使用 RunInNewThread，否则在函数返回前线程就会运行完，导致误判 IsAborted
        }

        public override void Abort()
        {
            if (State != ModBase.LoadState.Loading)
                return;
            lock (lockState)
            {
                State = ModBase.LoadState.Aborted;
            }

            TriggerThreadAbort();
        }

        private void TriggerThreadAbort()
        {
            if (lastRunningTask is null) return;
            if (ModBase.modeDebug) ModBase.Log($"[Loader] 加载线程 {name} ({lastRunningTask.Id}) 已中断");
            if (!lastRunningTask.IsCompleted) cancelToken?.Cancel();
            lastRunningTask = null;
            cancelToken = null;
        }
    }

    /// <summary>
    ///     支持多个加载器连续运作的复合加载器。
    /// </summary>
    public class LoaderCombo : LoaderBase
    {
        public object? input;

        public List<LoaderBase> loaders = new();

        public LoaderCombo(string name, IEnumerable<LoaderBase> loaders)
        {
            this.loaders.Clear();
            foreach (var Loader in loaders)
                if (Loader is not null)
                {
                    this.loaders.Add(Loader);
                    Loader.OnStateChangedThread += SubTaskStateChanged;
                    Loader.hasOnStateChangedThread = true;
                }

            InitParent(null);
            this.name = name;
        }

        public override double Progress
        {
            get
            {
                switch (State)
                {
                    case ModBase.LoadState.Waiting:
                    {
                        return 0d;
                    }
                    case ModBase.LoadState.Loading:
                    {
                        var total = 0d;
                        var finished = 0d;
                        foreach (var Loader in loaders)
                        {
                            total += Loader.ProgressWeight;
                            finished += Loader.ProgressWeight * Loader.Progress;
                        }

                        if (total == 0d)
                            return 0d;
                        return finished / total;
                    }

                    default:
                    {
                        return 1d;
                    }
                }
            }
            set => throw new Exception("多重加载器不支持设置进度");
        }

        public override void InitParent(LoaderBase parent)
        {
            this.parent = parent;
            foreach (var Loader in loaders)
                Loader.InitParent(this);
        }

        public override void Start(object input = null, bool isForceRestart = false)
        {
            isForceRestarting = isForceRestart;
            lock (lockState)
            {
                if (State == ModBase.LoadState.Loading) return;

                State = ModBase.LoadState.Loading;
            }

            // 启动加载
            this.input = input;
            if (isForceRestart)
                foreach (var Loader in loaders)
                    Loader.State = ModBase.LoadState.Waiting;
            ModBase.RunInThread(Update);
        }

        public override void Abort()
        {
            lock (lockState)
            {
                if (State != ModBase.LoadState.Loading && State != ModBase.LoadState.Waiting)
                    return;
            }

            foreach (var Loader in loaders) Loader.Abort();

            lock (lockState)
            {
                if (State == ModBase.LoadState.Loading || State == ModBase.LoadState.Waiting)
                    State = ModBase.LoadState.Aborted;
            }
        }

        /// <summary>
        ///     子任务状态变更。
        /// </summary>
        private void SubTaskStateChanged(LoaderBase loader, ModBase.LoadState newState, ModBase.LoadState oldState)
        {
            switch (newState)
            {
                case ModBase.LoadState.Loading:
                {
                    break;
                }
                // 开始，啥都不干
                case ModBase.LoadState.Waiting:
                {
                    break;
                }
                // 子加载器可能由于外部输入改变而暂时变为 Waiting，之后会立即重新启动
                // 所以啥都不干就行
                case ModBase.LoadState.Finished:
                {
                    // 正常结束，触发刷新
                    Update();
                    break;
                }
                case ModBase.LoadState.Aborted:
                {
                    // 被中断，这个任务也中断
                    Abort();
                    break;
                }

                default:
                {
                    // 完蛋，出错了
                    lock (lockState)
                    {
                        if (State >= ModBase.LoadState.Finished)
                            return;
                        Error = new Exception(loader.name + "失败", loader.Error);
                        State = loader.State;
                    }

                    foreach (var currentLoader in loaders)
                    {
                        loader = currentLoader;
                        loader.Abort();
                    }

                    ModMain.frmMain.BtnExtraDownload.ShowRefresh();
                    return;
                }
            }
        }

        /// <summary>
        ///     触发一次更新，以启动新加载器或完成。
        /// </summary>
        private void Update()
        {
            if (State == ModBase.LoadState.Finished
                || State == ModBase.LoadState.Failed
                || State == ModBase.LoadState.Aborted)
                return;

            var isFinished = true;
            var blocked = false;
            object input = this.input;

            foreach (var loader in loaders)
                switch (loader.State)
                {
                    case ModBase.LoadState.Finished:
                    {
                        if (loader is LoaderTask task)
                        {
                            var genericArg = task.GetType().GenericTypeArguments.FirstOrDefault();
                            var shouldInput = input is not null && genericArg == input.GetType()
                                ? input
                                : null;

                            if (task.ShouldStart(ref shouldInput, false, true))
                            {
                                ModBase.Log("[Loader] 由于输入条件变更，重启已完成的加载器 " + loader.name);
                                goto Restart;
                            }

                            input = ((dynamic)loader).output; // 何意味啊，没法匹配 LoaderTask<,>
                        }

                        if (loader.block && !isFinished)
                            blocked = true;

                        break;
                    }

                    case ModBase.LoadState.Loading:
                    {
                        if (loader is LoaderTask task)
                        {
                            var genericArg = loader.GetType().GenericTypeArguments.FirstOrDefault();
                            var shouldInput = input is not null && genericArg == input.GetType()
                                ? input
                                : null;
                            if (task.ShouldStart(ref shouldInput, false, true))
                            {
                                ModBase.Log($"[Loader] 由于输入条件变更，重启进行中的加载器 {loader.name}",
                                    ModBase.LogLevel.Developer);
                                goto Restart;
                            }
                        }

                        isFinished = false;
                        blocked = true;
                        break;
                    }

                    default:

                        Restart:

                        isFinished = false;

                        if (blocked)
                            continue;

                        if (input is not null)
                        {
                            switch (loader)
                            {
                                case LoaderTask:
                                case LoaderCombo:
                                    var genericArg = loader.GetType().GenericTypeArguments.FirstOrDefault();

                                    loader.Start(
                                        genericArg == input.GetType() ? input : null,
                                        isForceRestarting);
                                    break;
                                case not null:
                                    loader.Start(
                                        input is List<DownloadFile> ? input : null,
                                        isForceRestarting);
                                    break;
                                default:
                                    throw new Exception($"未知的加载器类型（{loader?.GetType()}）");
                            }
                        }
                        else
                        {
                            loader.Start(isForceRestart: isForceRestarting);
                        }

                        if (loader.block)
                            blocked = true;

                        break;
                }

            if (isFinished)
            {
                RaisePreviewFinish();
                State = ModBase.LoadState.Finished;
                ModMain.frmMain.BtnExtraDownload.ShowRefresh();
            }
        }

        /// <summary>
        ///     获得最底层的，应被显示给用户的加载器列表，并追加于 List。
        /// </summary>
        public static void GetLoaderList(LoaderCombo loader, ref List<LoaderBase> list, bool requireShow = true)
        {
            foreach (var SubLoader in loader.loaders)
            {
                if (SubLoader.show || !requireShow)
                    list.Add(SubLoader);
                if (SubLoader is LoaderCombo combo)
                    GetLoaderList(combo, ref list);
            }
        }

        /// <summary>
        ///     获得最底层的，应被显示给用户的加载器列表，并追加于 List。
        /// </summary>
        public void GetLoaderList(ref List<LoaderBase> list, bool requireShow = true)
        {
            GetLoaderList(this, ref list, requireShow);
        }

        /// <summary>
        ///     获得最底层的，应被显示给用户的加载器列表。
        /// </summary>
        public List<LoaderBase> GetLoaderList(bool requireShow = true)
        {
            var list = new List<LoaderBase>();
            GetLoaderList(ref list, requireShow);
            return list;
        }
    }

    /// <summary>
    ///     支持多个加载器连续运作的复合加载器（泛型版本）。
    /// </summary>
    public class LoaderCombo<InputType> : LoaderCombo
    {
        public new InputType typedInput;

        public LoaderCombo(string name, IEnumerable<LoaderBase> loaders) : base(name, loaders) { }

        public override void Start(object input = null, bool isForceRestart = false)
        {
            this.typedInput = Conversions.ToGenericParameter<InputType>(input);
            base.Start(this.typedInput, isForceRestart);
        }
    }

    private struct LoaderFolderDictionaryEntry
    {
        public DateTime? lastCheckTime;
        public string folderPath;

        public override bool Equals(object obj)
        {
            if (obj is not LoaderFolderDictionaryEntry)
                return false;
            var entry = (LoaderFolderDictionaryEntry)obj;
            return EqualityComparer<DateTime?>.Default.Equals(lastCheckTime, entry.lastCheckTime) &&
                   (folderPath ?? "") == (entry.folderPath ?? "");
        }
    }
}
