using DevExpress.Data;
using DevExpress.Mvvm.POCO;
using log4net;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Hosting;
using VIZ.Framework.Core;
using VIZ.Package.Domain;
using VIZ.Package.Plugin;
using VIZ.Package.Service;

namespace VIZ.Package.Module
{
    /// <summary>
    /// 包装任务视图模型
    /// </summary>
    public class PackageTaskViewModel : ViewModelBase, IPackageTaskService
    {
        /// <summary>
        /// 日志
        /// </summary>
        private readonly static ILog log = LogManager.GetLogger(typeof(PackageTaskViewModel));

        public PackageTaskViewModel()
        {
            // 初始化命令
            this.InitCommand();

            // 初始化消息
            this.InitMessage();

            // 注册服务
            ApplicationDomainEx.ServiceManager.AddService(ViewServiceKeys.PACKAGE_TASK_SERVICE, this);
        }

        /// <summary>
        /// 初始化命令
        /// </summary>
        private void InitCommand()
        {
            this.StartCommand = new VCommand<PackageTaskModel>(this.Start);
            this.StopCommand = new VCommand<PackageTaskModel>(this.Stop);
        }

        /// <summary>
        /// 初始化消息
        /// </summary>
        private void InitMessage()
        {
            ApplicationDomainEx.MessageManager.Register<PageDeleteMessage>(this, this.OnPageDeleteMessage);
            ApplicationDomainEx.MessageManager.Register<PageGroupDeleteMessage>(this, this.OnPageGroupDeleteMessage);
        }

        // ==============================================================
        // Service & Controller
        // ==============================================================

        /// <summary>
        /// Viz引擎命令服务
        /// </summary>
        private VizCommandService vizCommandService = new VizCommandService();

        /// <summary>
        /// Viz引擎控制对象服务
        /// </summary>
        private VizCommandControlObjectService vizCommandControlObjectService = new VizCommandControlObjectService();

        // ==============================================================
        // Property
        // ==============================================================

        #region ItemsSource -- 任务源

        private ObservableCollection<PackageTaskModel> itemsSource = new ObservableCollection<PackageTaskModel>();
        /// <summary>
        /// 任务源
        /// </summary>
        public ObservableCollection<PackageTaskModel> ItemsSource
        {
            get { return itemsSource; }
            set { itemsSource = value; this.RaisePropertyChanged(nameof(ItemsSource)); }
        }

        #endregion

        // ==============================================================
        // Command
        // ==============================================================

        #region StartCommand -- 开始命令

        /// <summary>
        /// 开始命令
        /// </summary>
        public VCommand<PackageTaskModel> StartCommand { get; set; }

        /// <summary>
        /// 开始
        /// </summary>
        /// <param name="task">任务</param>
        private void Start(PackageTaskModel task)
        {
            task.IsRunning = true;
        }

        #endregion

        #region StopCommand -- 停止命令

        /// <summary>
        /// 停止命令
        /// </summary>
        public VCommand<PackageTaskModel> StopCommand { get; set; }

        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="task">任务</param>
        private void Stop(PackageTaskModel task)
        {
            task.IsRunning = false;
        }

        #endregion

        // ==============================================================
        // Message
        // ==============================================================

        /// <summary>
        /// 页删除消息
        /// </summary>
        /// <param name="msg">消息</param>
        private void OnPageDeleteMessage(PageDeleteMessage msg)
        {
            if (msg.Pages == null || msg.Pages.Count == 0)
                return;

            var query = this.ItemsSource.Where(p => msg.Pages.Contains(p.PageModel)).ToList();
            foreach (PackageTaskModel task in query)
            {
                this.Cancel(task);
            }
        }

        /// <summary>
        /// 页分组删除消息
        /// </summary>
        /// <param name="msg">消息</param>
        private void OnPageGroupDeleteMessage(PageGroupDeleteMessage msg)
        {
            if (msg.PageGroup == null || msg.PageGroup.Pages == null)
                return;

            foreach (PageModel page in msg.PageGroup.Pages)
            {
                var query = this.ItemsSource.Where(p => p.PageModel == page).ToList();
                foreach (PackageTaskModel task in query)
                {
                    this.Cancel(task);
                }
            }
        }

        // ==============================================================
        // Public Function
        // ==============================================================

        /// <summary>
        /// 注册任务
        /// </summary>
        /// <param name="task">任务</param>
        public void Register(PackageTaskModel task)
        {
            lock (this.ItemsSource)
            {
                if (this.ItemsSource.Contains(task))
                    return;

                IPluginService pluginService = ApplicationDomainEx.ServiceManager.GetService<IPluginService>(ViewServiceKeys.PLUGIN_SERVICE);
                task.PageModel = pluginService.GetPageModelFromView(task.View);

                this.ItemsSource.Add(task);
            }

            string key = $"PackageTask_{task.ID}";
            task.TimerInfo = ApplicationDomainEx.TimerManager.Register(key, task.Interval, () => this.ExecuteTask(task));
        }

        /// <summary>
        /// 取消任务
        /// </summary>
        /// <param name="task">任务</param>
        public void Cancel(PackageTaskModel task)
        {
            lock (this.ItemsSource)
            {
                if (this.ItemsSource.Contains(task))
                {
                    string key = $"PackageTask_{task.ID}";
                    ApplicationDomainEx.TimerManager.UnRegister(key);

                    this.ItemsSource.Remove(task);

                    task.TimerInfo?.Dispose();
                }
            }
        }

        // ==============================================================
        // Private Function
        // ==============================================================

        /// <summary>
        /// 执行任务
        /// </summary>
        /// <param name="task">任务</param>
        private void ExecuteTask(PackageTaskModel task)
        {
            // 任务没有在运行状态 || 任务不可以执行 || 正在加载场景
            if (!task.IsRunning || !task.CanExecute || ApplicationDomainEx.IsSceneLoading)
                return;

            List<string> updateMessage = new List<string>();

            // 更新预览
            if (task.IsPreviewEnabled && ApplicationDomainEx.IsVizPreviewReadly && ApplicationDomainEx.PreviewConn != null && ApplicationDomainEx.PreviewConn.IsConnected)
            {
                this.ExecuteTaskPreviewUpdate(task, updateMessage);
            }

            // 更新版子
            if (task.IsTakeEnabled)
            {
                this.ExecuteTaskTakeUpdate(task, updateMessage);
            }
        }

        /// <summary>
        /// 执行任务 -- 更新预览
        /// </summary>
        /// <param name="task">任务</param>
        /// <param name="updateMessage">更新消息</param>
        private void ExecuteTaskPreviewUpdate(PackageTaskModel task, List<string> updateMessage)
        {
            try
            {
                if (task.IsCheckConnScene)
                {
                    string sceneName = this.vizCommandService.GetSceneName(ApplicationDomainEx.PreviewConn, task.PageModel.Layer);
                    if (sceneName != task.PageModel.Scene)
                    {
                        updateMessage.Add($"[预览] Viz引擎当前场景获取失败或与页场景{task.PageModel.Scene}不匹配");

                        return;
                    }
                }

                task.PreviewUpdateAction?.Invoke(ApplicationDomainEx.PreviewConn);
                WPFHelper.Invoke(() =>
                {
                    task.PreviewUpdateTime = DateTime.Now;
                });
            }
            catch (Exception ex)
            {
                log.Error(ex);
                WPFHelper.Invoke(() =>
                {
                    task.ErrorMessage = ex.Message;
                });
            }
        }

        /// <summary>
        /// 执行任务 -- 更新版子
        /// </summary>
        /// <param name="task">任务</param>
        /// <param name="updateMessage">更新消息</param>
        private void ExecuteTaskTakeUpdate(PackageTaskModel task, List<string> updateMessage)
        {
            ConnGroupModel group = ApplicationDomainEx.ConnGroups.FirstOrDefault(p => p.GroupID == task.PageModel.ConnGroupID);
            if (group == null)
                return;

            foreach (var item in group.Items)
            {
                if (!item.IsEnabled || !item.IsConnected)
                    continue;

                try
                {
                    if (task.IsCheckConnScene)
                    {
                        string sceneName = this.vizCommandService.GetSceneName(ApplicationDomainEx.PreviewConn, task.PageModel.Layer);
                        if (sceneName != task.PageModel.Scene)
                        {
                            updateMessage.Add($"[{item.IP}] Viz引擎当前场景获取失败或与页场景{task.PageModel.Scene}不匹配");
                            continue;
                        }
                    }

                    task.TakeUpdateAction(item);

                    WPFHelper.Invoke(() =>
                    {
                        task.TakeUpdateTime = DateTime.Now;
                    });
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                    WPFHelper.Invoke(() =>
                    {
                        task.ErrorMessage = ex.Message;
                    });
                }
            }

            WPFHelper.Invoke(() =>
            {
                task.UpdateMessage = string.Join("\r\n", updateMessage);
            });
        }
    }
}