using Apewer;
using Apewer.Surface;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading;

namespace Apewer.Web
{

    /// <summary>Cron 调度器。</summary>
    public sealed class CronInvoker
    {

        #region Instance

        private List<Assembly> _assemblies = null;
        private List<CronInstance> _instances = null;
        private bool _break = false;
        private Action<string> _log = null;

        /// <summary>获取或设置 Log 处理程序。</summary>
        public Action<string> LogAction { get { return _log; } set { _log = value; } }

        internal void Log(params object[] content) => Logger?.Text(typeof(CronInvoker), content);

        /// <summary>加载程序集。</summary>
        public void Load(IEnumerable<Assembly> assemblies)
        {
            Log("开始加载程序集。");
            if (_assemblies == null) _assemblies = new List<Assembly>();
            if (_assemblies.Count > 0)
            {
                Log("程序集列表为空。");
                return;
            }

            if (assemblies == null)
            {
                _assemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies());
            }
            else
            {
                foreach (var assembly in assemblies)
                {
                    if (assembly == null) continue;
                    if (_assemblies.Contains(assembly)) continue;
                    _assemblies.Add(assembly);
                }
            }

            var count = _assemblies.Count.ToString();
            Log($"已加载 {count} 个程序集。");
        }

        /// <summary>通知打断循环,所有 Cron 执行结束后退出。</summary>
        public void Break()
        {
            _break = true;
        }

        /// <summary>开始 Cron 调用。</summary>
        public void Start()
        {
            if (_instances != null) return;
            _instances = GetInstances();

            var count = _assemblies.Count.ToString();
            Log($"检查到 {count} 个 Cron 类型。");

            foreach (var i in _instances) Log(i.Type.FullName);
            while (true)
            {
                // CronLog.Write("Tick");
                var alive = 0;
                foreach (var i in _instances)
                {
                    // 跳出。
                    if (i.Alive) alive++;
                    if (_break)
                    {
                        i.Break = true;
                        break;
                    }

                    // 当前线程正在活动。
                    if (i.Alive)
                    {
                        i.Latest = true;
                        continue;
                    }

                    // 记录 Cron 结束时间,根据结束时间判断再次启动 Cron。
                    if (i.Latest)
                    {
                        Log($"类型 {i.Type.FullName} 已结束。");
                        i.Ended = DateTime.Now;
                        i.Latest = false;
                    }

                    if (i.Ended == null)
                    {
                        Log($"准备开始类型 {i.Type.FullName}。");
                        i.Start();
                        i.Latest = true;
                    }
                    else
                    {
                        var span = DateTime.Now - i.Ended.Value;
                        if (span.TotalMilliseconds >= Convert.ToDouble(i.Interval))
                        {
                            Log($"准备开始类型 {i.Type.FullName}。");
                            i.Start();
                            i.Latest = true;
                        }
                    }
                }

                if (_break && alive < 1)
                {
                    break;
                }

                Thread.Sleep(1000);
                GC.Collect();
            }

            Log("循环结束,即将退出。");
        }

        private List<CronInstance> GetInstances()
        {
            var list = new List<CronInstance>();

            var types = GetTypes();
            foreach (var type in types)
            {
                var attribute = RuntimeUtility.GetAttribute<CronAttribute>(type, false);
                if (attribute == null) continue;

                var instance = new CronInstance();
                instance.Invoker = this;
                instance.Attribute = attribute;
                instance.Type = type;

                list.Add(instance);
            }

            return list;
        }

        private List<Type> GetTypes()
        {
            var list = new List<Type>();

            var assemblies = _assemblies;
            foreach (var assembly in assemblies)
            {
                var types = RuntimeUtility.GetTypes(assembly);
                foreach (var type in types)
                {
                    if (!type.IsPublic) continue;
                    if (type.IsAbstract) continue;
                    if (!RuntimeUtility.CanNew(type)) continue;

                    list.Add(type);
                }
            }

            return list;
        }

        #endregion

        #region Static

        /// <summary>获取或设置日志记录器。</summary>
        public static Logger Logger { get; set; }

        /// <summary>在当前线程开始 Cron 调用,可能会阻塞当前线程。可指定 Log 处理程序。</summary>
        public static CronInvoker Start(Assembly assembly, Action<string> log = null)
        {
            var assemblies = new Assembly[] { assembly };
            return Start(assemblies);
        }

        /// <summary>在当前线程开始 Cron 调用,可能会阻塞当前线程。可指定 Log 处理程序。</summary>
        public static CronInvoker Start(IEnumerable<Assembly> assemblies, Action<string> log = null)
        {
            var instance = new CronInvoker();
            instance.LogAction = log;
            instance.Load(assemblies);
            instance.Start();
            return instance;
        }

        #endregion

    }

}