using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using MECF.Framework.Common.Account;
using MECF.Framework.Common.Account.Extends;
using MECF.Framework.Common.Account.Permissions;
using MECF.Framework.UI.Client.CenterViews.Configs.Roles;
using MECF.Framework.UI.Client.CenterViews.Editors;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;

namespace MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel
{
    public class RecipeStep : ObservableCollection<Param>
    {
        #region Variables

        private MenuPermissionEnum _permission;
        private bool _isHideValue;
        private bool _isProcessed;
        private bool _isVisible;


        /// <summary>
        /// 当步骤的任意参数的<see cref="Param.IsSaved"/>属性发生变化时，引发此事件。
        /// </summary>
        public event EventHandler<bool> SavedStateChanged;

        /// <summary>
        /// 当步骤完成时，引发此事件。
        /// </summary>
        public event Action<bool> ProcessStatusChangedCallback;

        /// <summary>
        /// 当前步骤的选择状态发生变化时，引发此事件。
        /// </summary>
        public event EventHandler<bool> SelectionStateChanged;

        /// <summary>
        /// 当前步骤的任意参数高亮状态发生变化时，引发此事件。
        /// </summary>
        public event EventHandler HighlightStateChanged;

        /// <summary>
        /// 当前步骤的气体流量计算完成后，引发此事件。
        /// </summary>
        public event EventHandler GasFlowCalculated;

        /// <summary>
        /// 当步骤的时长发生变化时，引发此事件。
        /// </summary>
        public event EventHandler StepTimeChanged;

        #endregion

        #region Constructors

        public RecipeStep(RecipeStep previous)
        {
            Previous = previous;
            IsSaved = true;
            IsVisible = true;
        }

        #endregion

        #region Properties

        /// <summary>
        /// 设置或返回当前步骤的访问权限。
        /// </summary>
        public MenuPermissionEnum Permission
        {
            get => _permission;
            set
            {
                _permission = value;
                this.ToList().ForEach(x => x.StepPermission = _permission);
            }
        }
        
        /// <summary>
        /// 父级配方集合。
        /// </summary>
        public RecipeData Parent { get; internal set; }

        /// <summary>
        /// 返回配方步骤序号参数。
        /// </summary>
        public StepParam StepNoParam => this.FirstOrDefault(param => param.ControlName == RecipeFormatBuilder.SPEC_COL_STEPNO) as StepParam;

        private StringParam UidParam => this.FirstOrDefault(param => param.ControlName == RecipeFormatBuilder.SPEC_COL_STEPUID) as StringParam;
        
        private StringParam NameParam => this.FirstOrDefault(param => param.ControlName == RecipeFormatBuilder.SPEC_COL_NAME) as StringParam;

        /// <summary>
        /// 返回步骤名称。
        /// </summary>
        public string Name => NameParam?.Value;
        
        /// <summary>
        /// 返回当前步骤是否被选中。
        /// </summary>
        public bool IsSelected
        {
            get => StepNoParam?.IsChecked ?? false;
            set
            {
                if (StepNoParam != null)
                    StepNoParam.IsChecked = value;
            }
        }

        /// <summary>
        /// 当前配方步骤的唯一识别码。
        /// </summary>
        public string StepUid
        {
            get => UidParam?.Value ?? null;
            set
            {
                var paramUid = UidParam;
                if (paramUid != null)
                {
                    paramUid.Value = value;
                }
            }
        }

        /// <summary>
        /// 返回步骤序号。
        /// </summary>
        public int? StepNo => StepNoParam?.Value;


        /// <summary>
        /// 返回步骤执行时长。
        /// </summary>
        public double StepTime
        {
            get
            {
                var param = FindParamByControlName(RecipeControlNames.STEP_TIME);
                if(param is DoubleParam dblParam)
                    return dblParam.Value;
                
                return 0d;
            }
        }

        /// <summary>
        /// 返回当前配方步骤是否已保存。
        /// </summary>
        public bool IsSaved { get; private set; }

        /// <summary>
        /// 返回当前步骤是否已经跑完工艺。
        /// </summary>
        public bool IsProcessed
        {
            get => _isProcessed;
            set
            {
                if (_isProcessed != value)
                {
                    _isProcessed = value;
                    ProcessStatusChangedCallback?.Invoke(value);
                    OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsProcessed)));
                }
            }
        }

        /// <summary>
        /// 返回前序配方。
        /// </summary>
        public RecipeStep Previous { get; private set; }

        /// <summary>
        /// 返回后序配方。
        /// </summary>
        public RecipeStep Next { get; private set; }

        /// <summary>
        /// 设置或返回当前步骤是否在DataGrid中显示。
        /// </summary>
        public bool IsVisible
        {
            get => _isVisible;
            set
            {
                _isVisible = value;
                OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsVisible)));
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// 当配方中的参数的属性发生变化时，触发此事件。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnParamPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (sender is not IParam param)
                return;

            switch (e.PropertyName)
            {
                case nameof(Param.IsSaved):
                {
                    if (param.IsSaved == false)
                        IsSaved = false;
                    else
                        IsSaved = this.ToList().FirstOrDefault(x => x.IsSaved == false) == null;

                    SavedStateChanged?.Invoke(this, IsSaved);
                    break;
                }
                case nameof(StepParam.IsChecked):
                    OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsSelected)));
                    SelectionStateChanged?.Invoke(this, IsSelected);
                    break;

                case nameof(Param.IsHighlighted):
                    HighlightStateChanged?.Invoke(this, EventArgs.Empty);
                    break;
            }
        }

        /// <summary>
        /// 当配方中的参数的值发生变化时，触发此事件。
        /// </summary>
        /// <remarks>
        /// 该事件在DragToFill操作时会被触发。
        /// 单元格编辑完成后参数值发生变化不会引发此事件，而是直接在CellEndEditing事件中处理。
        /// </remarks>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnParamValueChanged(object sender, object e)
        {
            if (sender is Param para and IValueParam )
            {
                if (para.ControlName == RecipeControlNames.STEP_TIME)
                {
                    StepTimeChanged?.Invoke(this, EventArgs.Empty);
                }
                else
                {
                    //TODO 和气体不相关的参数修改后，不应该触发气体流量计算过程
                    CalculateGasFlow();
                }
            }

        }


        /// <summary>
        /// 将指定的Param链接到前序Param。
        /// </summary>
        /// <param name="param">当前Step中的参数。</param>
        private void LinkWithPreviousParam(IParam param)
        {
            var preParam = Previous?.FirstOrDefault(x => x.ControlName == param.ControlName);
            param.Previous = preParam;
            if (preParam != null)
                preParam.Next = param;
        }

        /// <summary>
        /// 将指定的Param链接到后序Param。
        /// </summary>
        /// <param name="param">当前Step中的参数。</param>
        private void LinkWithNextParam(IParam param)
        {
            var nextParam = Next?.FirstOrDefault(x => x.ControlName == param.ControlName);
            param.Next = nextParam;
            if (nextParam != null)
                nextParam.Previous = param;
        }

        /// <summary>
        /// 从角色配置中加载当前步骤的访问权限。
        /// </summary>
        /// <param name="step"></param>
        /// <param name="role">角色</param>
        internal static void SetPermission(RecipeStep step, Role role)
        {
            Debug.Assert(role != null, "Role can not be null.");
            Debug.Assert(step != null, "Recipe Step can not be null.");

            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
            if (step == null)
                return;

            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
            if (role == null)
                step.Permission = MenuPermissionEnum.MP_NONE;
            else
            {
                var mpKey = $"Step{step.StepNo}";

                step.Permission = role.PermissionControlItems.TryGetValue(mpKey, out var perm)
                    ? perm
                    : MenuPermissionEnum.MP_NONE;
            }
        }
        
        /// <summary>
        /// 在当前步骤中查找指定控制名的参数。
        /// </summary>
        /// <param name="controlName">
        /// 参数控制的名称。
        /// </param>
        /// <param name="isIgnoreCase">
        /// 是否区分大小写。
        /// </param>
        /// <returns></returns>
        public IParam FindParamByControlName(string controlName, bool isIgnoreCase = true)
        {
            return this.FirstOrDefault(x =>
                string.Equals(x.ControlName, controlName,
                    isIgnoreCase ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture));
        }

        /// <summary>
        /// 校验步骤中的所有参数。
        /// </summary>
        public void Validate()
        {
            this.ToList().ForEach(x => x.Validate());
        }

        /// <summary>
        /// 计算当前步骤的气体流量。
        /// </summary>
        public void CalculateGasFlow()
        {
            if (Parent.RecipeType != RecipeType.Process)
                return;
            
            Parent?.CalculateGasFlow(this);
            GasFlowCalculated?.Invoke(this, EventArgs.Empty);
        }

        /// <summary>
        /// 取消所有参数的Highlight状态。
        /// </summary>
        public void ResetHighlight()
        {
            this.ToList().ForEach(x => x.ResetHighlight());
        }

        /// <summary>
        /// 获取高亮显示的参数。
        /// </summary>
        /// <returns></returns>
        public List<Param> GetHighlightedParams()
        {
            return this.ToList().Where(p => p.IsHighlighted).ToList();
        }

        /// <summary>
        /// 隐藏参数值。
        /// </summary>
        public void HideValue()
        {
            this.ToList().ForEach(x => x.IsHideValue = true);
        }

        /// <summary>
        /// 显示参数值。
        /// </summary>
        public void ShowValue()
        {
            this.ToList().ForEach(x => x.IsHideValue = false);
        }

        /// <summary>
        /// 设置隐藏的参数值掩膜字符。
        /// </summary>
        /// <param name="masking"></param>
        public void SetHiddenValueMasking(string masking)
        {
            this.ToList().ForEach(x => x.HiddenValueMasking = masking);
        }

        /// <summary>
        /// 保存当前配方步骤。
        /// </summary>
        public void Save()
        {
            this.ToList().ForEach(x => x.Save());
        }

        /// <summary>
        /// 設置前序步驟。
        /// </summary>
        /// <param name="step"></param>
        public void SetPreviousStep(RecipeStep step)
        {
            Previous = step;

            // 调整Step中Param的链接关系
            this.ToList().ForEach(LinkWithPreviousParam);

            if (Previous == null)
                return;

            // 将前序步骤的后序步骤链接到我自己。
            Previous.Next = this;
        }

        public void SetNextStep(RecipeStep step)
        {
            Next = step;
            this.ToList().ForEach(LinkWithNextParam);
        }

        /// <summary>
        /// 检查StepNo是否为有效值。
        /// </summary>
        /// <param name="stepNo"></param>
        /// <param name="validatedStepNo"></param>
        /// <returns></returns>
        public static bool ValidateStepNo(int? stepNo, out int validatedStepNo)
        {
            validatedStepNo = int.MinValue;
            var isValid = stepNo >= RecipeFormatBuilder.STEP_INDEX_BASE;
            if (isValid)
                validatedStepNo = stepNo.Value;

            return isValid;
        }


        #endregion 

        #region Override Methods

        protected override void InsertItem(int index, Param item)
        {
            if (item != null)
            {
                item.Parent = this;
                LinkWithPreviousParam(item);
                LinkWithNextParam(item);

                item.PropertyChanged += OnParamPropertyChanged;
                if(item is IValueParam vp)
                    vp.OnValueChanged += OnParamValueChanged;
            }

            base.InsertItem(index, item);
        }

        protected override void SetItem(int index, Param item)
        {
            throw new NotSupportedException();
        }

        protected override void RemoveItem(int index)
        {
            if (Count > 0)
            {
                if (index >= 0 && index < Count)
                {
                    this[index].PropertyChanged -= OnParamPropertyChanged;
                    
                    if(this[index] is IValueParam vp)
                        vp.OnValueChanged -= OnParamValueChanged;
                }
            }

            // 将前后两个Param链接起来
            var preParam = this[index].Previous;
            var nextParam = this[index].Next;
            if (preParam != null && nextParam != null)
            {
                preParam.Next = nextParam;
                nextParam.Previous = preParam;
            }
            else if (preParam == null && nextParam != null)
            {
                nextParam.Previous = null;
            }
            else if (preParam != null)
            {
                preParam.Next = null;
            }


            IsSaved = false;
            SavedStateChanged?.Invoke(this, true);

            base.RemoveItem(index);
        }

        protected override void ClearItems()
        {
            this.ToList().ForEach(x =>
            {
                x.PropertyChanged -= OnParamPropertyChanged;
                
                if(x is IValueParam vp)
                    vp.OnValueChanged -= OnParamValueChanged;
            });

            IsSaved = Count <= 0;
            SavedStateChanged?.Invoke(this, true);

            base.ClearItems();
        }

        public override string ToString()
        {
            return Count > 0 ? (StepNoParam == null
                    ? "Recipe Step, Item={Count}" : StepNoParam.ToString())
                : "Empty Recipe Step";
        }

        #endregion
    }
}
