using DevExpress.Data;
using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VIZ.Framework.Core;
using VIZ.Package.Domain;
using VIZ.Package.Plugin;
using VIZ.Package.Service;
using VIZ.Package.Storage;

namespace VIZ.Package.Module
{
    /// <summary>
    /// 控制视图模型
    /// </summary>
    public class ControlViewModel : ViewModelBase, IControlService
    {
        /// <summary>
        /// 日志
        /// </summary>
        private readonly static ILog log = LogManager.GetLogger(typeof(ControlViewModel));

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

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

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

        /// <summary>
        /// 初始化命令
        /// </summary>
        private void InitCommand()
        {
            this.TakeCommand = new VCommand(this.Take);
            this.ContinueCommand = new VCommand(this.Continue);
            this.TakeOutCommand = new VCommand(this.TakeOut);
            this.UpdateCommand = new VCommand(this.Update);
        }

        /// <summary>
        /// 初始化消息
        /// </summary>
        private void InitMessage()
        {
            ApplicationDomainEx.MessageManager.Register<HotkeyMessage>(this, this.OnHotkeyMessage);
            ApplicationDomainEx.MessageManager.Register<PageOpenOverMessage>(this, this.OnPageOpenOverMessage);
        }

        // ==============================================================
        // Field
        // ==============================================================

        /// <summary>
        /// 等待Take的页
        /// </summary>
        private PageModelBase waitTakePageBase;

        /// <summary>
        /// 等待Continue的页
        /// </summary>
        private PageModelBase waitTakeContinuePageBase;

        /// <summary>
        /// 等待TakeOut的页
        /// </summary>
        private PageModelBase waitTakeOutPageBase;

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

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

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

        /// <summary>
        /// 操作日志服务
        /// </summary>
        private RecordLogService recordLogService = new RecordLogService();

        /// <summary>
        /// 播出控制器
        /// </summary>
        private ControlController controlController = new ControlController();

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

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

        #region TakeCommand -- 上版子命令

        /// <summary>
        /// 上版命令
        /// </summary>
        public VCommand TakeCommand { get; set; }

        /// <summary>
        /// 上板
        /// </summary>
        public void Take()
        {
            this.Take(null);
        }

        #endregion

        #region ContinueCommand -- 继续命令

        /// <summary>
        /// 继续命令
        /// </summary>
        public VCommand ContinueCommand { get; set; }

        /// <summary>
        /// 继续
        /// </summary>
        public void Continue()
        {
            // 记录操作日志
            this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.OPERATE_TAKE_CONTINUE);

            this.Execute(
                action: (obj, view, conn) =>
                {
                    if (ApplicationDomainEx.CurrentPage.PageType == PageType.Scene)
                    {
                        if (obj.TransitionLogic)
                        {
                            this.controlController.ContinueTransitionLogic(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                        else
                        {
                            this.controlController.ContinueNormal(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                    }
                    this.controlController.ContinueCommnad(ApplicationDomainEx.CurrentPage, conn);
                },
                over: null);
        }

        #endregion

        #region TakeOutCommand -- 下版命令

        /// <summary>
        /// 下版命令
        /// </summary>
        public VCommand TakeOutCommand { get; set; }

        /// <summary>
        /// 下版
        /// </summary>
        public void TakeOut()
        {
            // 记录操作日志
            this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.OPERATE_TAKE_OUT);

            this.Execute(
                action: (obj, view, conn) =>
                {
                    if (ApplicationDomainEx.CurrentPage.PageType == PageType.Scene)
                    {
                        if (obj.TransitionLogic)
                        {
                            this.controlController.TakeOutTransitionLogic(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                        else
                        {
                            this.controlController.TakeOutNormal(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                    }
                    this.controlController.TakeOutCommnad(ApplicationDomainEx.CurrentPage, conn);
                },
                over: () =>
                {
                    // 清理Take状态
                    WPFHelper.BeginInvoke(() =>
                    {
                        if (ApplicationDomainEx.CurrentTake != null)
                        {
                            ApplicationDomainEx.CurrentTake.IsTake = false;
                            ApplicationDomainEx.CurrentTake = null;
                        }
                    });
                });
        }

        #endregion

        #region UpdateCommand -- 更新命令

        /// <summary>
        /// 更新命令
        /// </summary>
        public VCommand UpdateCommand { get; set; }

        /// <summary>
        /// 更新
        /// </summary>
        public void Update()
        {
            // 更新控制字段List的值
            IFieldTreeService service = ApplicationDomainEx.ServiceManager.GetService<IFieldTreeService>(ViewServiceKeys.FIELD_TREE_SERVICE);
            if (service == null)
                return;

            // 记录操作日志
            this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.OPERATE_TAKE_UPDATE);

            // 尝试更新控制字段List的值
            service.TryUpdateControlFieldListValue();

            // 执行更新流程
            this.Execute(
                action: (obj, view, conn) =>
                {
                    if (ApplicationDomainEx.CurrentPage.PageType == PageType.Scene)
                    {
                        if (obj.TransitionLogic)
                        {
                            this.controlController.UpdateTransitionLogic(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                        else
                        {
                            this.controlController.UpdateNormal(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                    }
                    this.controlController.UpdateCommnad(ApplicationDomainEx.CurrentPage, conn);
                },
                over: null);
        }

        #endregion

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

        /// <summary>
        /// 处理热键消息
        /// </summary>
        /// <param name="msg">消息</param>
        private void OnHotkeyMessage(HotkeyMessage msg)
        {
            try
            {
                // 上板
                if (string.Equals(msg.Key, ApplicationDomainEx.HotKeyConfig.Take))
                {
                    // 记录操作日志
                    this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.SYSTEM_HOT_KEY, msg.Key);

                    this.Take();
                    return;
                }

                // 继续
                if (string.Equals(msg.Key, ApplicationDomainEx.HotKeyConfig.TakeContinue))
                {
                    // 记录操作日志
                    this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.SYSTEM_HOT_KEY, msg.Key);

                    this.Continue();
                    return;
                }

                // 下版
                if (string.Equals(msg.Key, ApplicationDomainEx.HotKeyConfig.TakeOut))
                {
                    // 记录操作日志
                    this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.SYSTEM_HOT_KEY, msg.Key);

                    this.TakeOut();
                    return;
                }

                // 更新
                if (string.Equals(msg.Key, ApplicationDomainEx.HotKeyConfig.TakeUpdate))
                {
                    // 记录操作日志
                    this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.SYSTEM_HOT_KEY, msg.Key);

                    this.Update();
                    return;
                }
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
        }

        /// <summary>
        /// 页打开完成消息
        /// </summary>
        /// <param name="msg">消息</param>
        private void OnPageOpenOverMessage(PageOpenOverMessage msg)
        {
            // Wait Take
            if (this.waitTakePageBase != null && this.waitTakePageBase == msg.Page)
            {
                this.waitTakePageBase = null;
                this.waitTakeContinuePageBase = null;
                this.waitTakeOutPageBase = null;
                this.Take();
                return;
            }

            // Wait Continue
            if (this.waitTakeContinuePageBase != null && this.waitTakeContinuePageBase == msg.Page)
            {
                this.waitTakePageBase = null;
                this.waitTakeContinuePageBase = null;
                this.waitTakeOutPageBase = null;
                this.Continue();
                return;
            }

            // Wait TakeOut
            if (this.waitTakeOutPageBase != null && this.waitTakeOutPageBase == msg.Page)
            {
                this.waitTakePageBase = null;
                this.waitTakeContinuePageBase = null;
                this.waitTakeOutPageBase = null;
                this.TakeOut();
                return;
            }

            this.waitTakePageBase = null;
            this.waitTakeContinuePageBase = null;
            this.waitTakeOutPageBase = null;
        }

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

        /// <summary>
        /// 上版
        /// </summary>
        /// <param name="callback">上版完成后的回调</param>
        public void Take(Action callback)
        {
            // 更新控制字段List的值
            IFieldTreeService service = ApplicationDomainEx.ServiceManager.GetService<IFieldTreeService>(ViewServiceKeys.FIELD_TREE_SERVICE);
            if (service == null)
                return;

            // 记录操作日志
            this.recordLogService.AppendLog(ApplicationConstants.APPLICATION_GROUP_NAME, RecordLogOperate.Operate, RecordLogTrigger.Human, RecordLogConstants.OPERATE_TAKE_TAKE);

            // 尝试更新控制字段List的值
            service.TryUpdateControlFieldListValue();

            // 执行上版流程
            this.Execute(
                action: (obj, view, conn) =>
                {
                    if (ApplicationDomainEx.CurrentPage.PageType == PageType.Scene)
                    {
                        if (obj.TransitionLogic)
                        {
                            this.controlController.TakeTransitionLogic(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                        else
                        {
                            this.controlController.TakeNormal(ApplicationDomainEx.CurrentTake, ApplicationDomainEx.CurrentPage, obj, view, conn);
                        }
                    }
                    this.controlController.TakeCommnad(ApplicationDomainEx.CurrentPage, conn);
                },
                over: () =>
                {
                    // 设置当前Take的模板
                    WPFHelper.BeginInvoke(() =>
                    {
                        if (ApplicationDomainEx.CurrentTake != null)
                        {
                            ApplicationDomainEx.CurrentTake.IsTake = false;
                        }

                        ApplicationDomainEx.CurrentTake = ApplicationDomainEx.CurrentPage;

                        if (ApplicationDomainEx.CurrentTake != null)
                        {
                            ApplicationDomainEx.CurrentTake.IsTake = true;
                        }

                        callback?.Invoke();
                    });
                });
        }

        /// <summary>
        /// 当页打开后Take指定页
        /// </summary>
        /// <param name="pageBase">指定页</param>
        public void TakeWhenPageOpend(PageModelBase pageBase)
        {
            this.waitTakePageBase = pageBase;
            this.waitTakeContinuePageBase = null;
            this.waitTakeOutPageBase = null;
        }

        /// <summary>
        /// 当页打开后Continue指定页
        /// </summary>
        /// <param name="pageBase">指定页</param>
        public void TakeContinueWhenPageOpend(PageModelBase pageBase)
        {
            this.waitTakePageBase = null;
            this.waitTakeContinuePageBase = pageBase;
            this.waitTakeOutPageBase = null;
        }

        /// <summary>
        /// 当页打开后TakeOut指定页
        /// </summary>
        /// <param name="pageBase">指定页</param>
        public void TakeOutWhenPageOpend(PageModelBase pageBase)
        {
            this.waitTakePageBase = null;
            this.waitTakeContinuePageBase = null;
            this.waitTakeOutPageBase = pageBase;
        }

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

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="action">行为</param>
        /// <param name="over">执行完成时处理</param>
        private void Execute(Action<ControlObjectModel, IPluginView, ConnModel> action, Action over)
        {
            // 是否拥有打开的页或模板页
            if (ApplicationDomainEx.CurrentPage == null)
                return;

            // 插件服务
            IPluginService pluginService = ApplicationDomainEx.ServiceManager.GetService<IPluginService>(ViewServiceKeys.PLUGIN_SERVICE);
            if (pluginService == null)
                return;

            // 控制字段树服务
            IFieldTreeService fieldTreeService = ApplicationDomainEx.ServiceManager.GetService<IFieldTreeService>(ViewServiceKeys.FIELD_TREE_SERVICE);
            if (fieldTreeService == null)
                return;

            ControlObjectModel controlObject = fieldTreeService.GetControlObject();

            // 模板
            if (ApplicationDomainEx.CurrentPage is PageTemplateModel template)
            {
                // 模板上版等操作使用默认分组
                ConnGroupModel group = ApplicationDomainEx.ConnGroups.FirstOrDefault(p => p.IsDefault);
                // 如果没有默认分组，那么采用第一个可用分组
                if (group == null)
                {
                    group = ApplicationDomainEx.ConnGroups.FirstOrDefault(p => p.IsEnabled);
                }

                // 如果没有分组可用，那么不处理
                if (group == null)
                    return;

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

                    try
                    {
                        action(controlObject, null, item);
                    }
                    catch (Exception ex)
                    {
                        log.Error(ex);
                    }
                }
            }
            // 页
            else if (ApplicationDomainEx.CurrentPage is PageModel page)
            {
                // 模板上版等操作使用默认分组
                ConnGroupModel group = ApplicationDomainEx.ConnGroups.FirstOrDefault(p => p.GroupID == page.ConnGroupID);
                if (group == null)
                {
                    // 记录日志
                    log.Error($"ConnGroupID: {page.ConnGroupID} is not found.");

                    return;
                }

                IPluginView view = pluginService.GetCurrentPluginView() as IPluginView;

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

                    try
                    {
                        action(controlObject, view, item);
                    }
                    catch (Exception ex)
                    {
                        log.Error(ex);
                    }
                }
            }

            try
            {
                over?.Invoke();
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
        }
    }
}