using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel.Params;
using Sicentury.Core;

namespace MECF.Framework.UI.Client.RecipeEditorLib.RecipeModel
{
    /// <summary>
    /// 配方步骤集合。
    /// </summary>
    /// <remarks>
    /// 该集合继承自<see cref="ObservableCollection{T}"/>。
    /// </remarks>
    public sealed class RecipeStepCollection : ObservableCollection<RecipeStep>, IDisposable
    {
        #region Variables

        public event EventHandler<bool> OnSavedStateChanged;
        public event EventHandler HighlightStateChanged;
        public event EventHandler GasFlowCalculated;
        public event EventHandler StepTimeChanged;

        private bool _isLoadingRecipe;
        private bool? _isAllStepsSelected;
        private bool _isSaved;

        #endregion

        #region Constructors

        /// <summary>
        /// 创建配方步骤集合的实例。
        /// </summary>
        public RecipeStepCollection()
        {
            _isSaved = true;
        }

        /// <summary>
        /// 创建配方步骤集合的实例。
        /// </summary>
        /// <param name="parent">
        /// 父级配方对象，请参考<see cref="RecipeData"/>。
        /// </param>
        public RecipeStepCollection(RecipeData parent) : this()
        {
            Parent = parent;
        }

        /// <summary>
        /// 创建配方步骤集合的实例。
        /// </summary>
        /// <param name="steps">配方步骤数组。</param>
        /// <param name="parent">
        /// 父级配方对象，请参考<see cref="RecipeData"/>。
        /// </param>
        public RecipeStepCollection(IList<RecipeStep> steps, RecipeData parent) 
            : base(steps)
        {
            _isSaved = true;
            Parent = parent;

            foreach (var step in steps)
                step.Parent = parent;
        }

        #endregion

        #region Properties

        /// <summary>
        /// 返回父级配方对象的实例。
        /// </summary>
        public RecipeData Parent { get; }

        /// <summary>
        /// 返回当前配方是否已经被保存。
        /// </summary>
        public bool IsSaved
        {
            get => _isSaved;
            private set
            {
                _isSaved = value;
                OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsSaved)));
                OnSavedStateChanged?.Invoke(this, _isSaved);
            }
        }

        public bool? IsAllStepsSelected
        {
            get
            {
                var groupSelection = this.GroupBy(x => x.IsSelected).ToList();
                if (groupSelection.Count == 1)
                    _isAllStepsSelected = groupSelection[0].Key;
                else
                    _isAllStepsSelected = null;

                return _isAllStepsSelected;
            }
            set
            {
                if (!_isAllStepsSelected.HasValue || _isAllStepsSelected == true)
                    DeselectAll();
                else
                    SelectAll();

                OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAllStepsSelected)));
            }
        }

        /// <summary>
        /// 返回被选中的配方步骤。
        /// </summary>
        public IReadOnlyList<RecipeStep> SelectedSteps =>
            this.ToList().Where(x => x.StepNoParam != null && x.StepNoParam.IsChecked).ToList();

        #endregion

        #region Methods

        private void RegisterEvents(RecipeStep step)
        {
            step.SavedStateChanged += ItemOnSavedStateChanged;
            step.SelectionStateChanged += StepOnSelectionStateChanged;
            step.HighlightStateChanged += HighlightStateChanged;
            step.GasFlowCalculated += GasFlowCalculated;
            step.StepTimeChanged += StepTimeChanged;
        }

        public void UnRegisterEvents(RecipeStep step)
        {
            step.SavedStateChanged -= ItemOnSavedStateChanged;
            step.SelectionStateChanged -= StepOnSelectionStateChanged;
            step.HighlightStateChanged -= HighlightStateChanged;
            step.GasFlowCalculated -= GasFlowCalculated;
            step.StepTimeChanged -= StepTimeChanged;
        }

        /// <summary>
        /// 当配方中的某步骤的IsSaved状态发生变化时，触发此事件。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ItemOnSavedStateChanged(object sender, bool e)
        {
            if (e == false)
                IsSaved = false;
            else
                IsSaved = (this.ToList().FirstOrDefault(x => x.IsSaved == false) == null);
        }

        /// <summary>
        /// 当配方步骤的选中状态发生变化时，触发此事件。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void StepOnSelectionStateChanged(object sender, bool e)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsAllStepsSelected)));
        }
        
        /// <summary>
        /// 更新Step序列号。
        /// </summary>
        /// <param name="markAsSaved"></param>
        public void UpdateStepIndex(bool markAsSaved = false)
        {
            var step = this.FirstOrDefault();

            var stepNo = step?.StepNoParam;
            if (stepNo == null)
                return;

            var index = RecipeFormatBuilder.STEP_INDEX_BASE;
            while (true)
            {
                stepNo.Value = index;
                if (markAsSaved)
                    stepNo.Save();

                //! 最后一个步骤的Next应该为Null，此时跳出循环。
                if (stepNo.Next is StepParam nextParam)
                    stepNo = nextParam;
                else
                    break;

                index++;
            }
        }

        /// <summary>
        /// 开始加载Recipe。
        /// </summary>
        public IDisposable BeginLoading()
        {
            _isLoadingRecipe = true;
            IsSaved = true;
            return this;
        }

        /// <summary>
        /// 结束加载Recipe
        /// </summary>
        public void EndLoading()
        {
            _isLoadingRecipe = false;
            IsSaved = true;
        }

        /// <summary>
        /// 计算所有步骤的总时长。
        /// </summary>
        /// <returns></returns>
        public double GetTotalTime()
        {
            return this.Sum(x => x.StepTime);
        }
        
        
        /// <summary>
        /// 选择当前集合中的所有配方步骤。
        /// </summary>
        public void SelectAll()
        {
            foreach (var step in this)
            {
                step.IsSelected = true;
            }
        }

        /// <summary>
        /// 取消选择当前集合中的所有配方步骤。
        /// </summary>
        public void DeselectAll()
        {
            foreach (var step in this)
            {
                step.IsSelected = false;
            }
        }

        /// <summary>
        /// 反向选择当前集合中的所有配方步骤。
        /// </summary>
        public void ReverseSelection()
        {
            foreach (var step in this)
            {
                step.IsSelected = !step.IsSelected;
            }
        }

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

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

        /// <summary>
        /// 校验所有步骤。
        /// </summary>
        internal void Validate()
        {
            this.ToList().ForEach(step =>
            {
                step.Validate();
            });
        }

        public void HideValue()
        {
            this.ToList().ForEach(step =>
            {
                step.HideValue();
            });
        }

        public void ShowValue()
        {
            this.ToList().ForEach(step =>
            {
                step.ShowValue();
            });
        }

        public void SetHiddenValueMasking(string masking)
        {
            this.ToList().ForEach(step =>
            {
                step.SetHiddenValueMasking(masking);
            });
        }
        
        #endregion

        #region Overrided Methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="previous"></param>
        /// <param name="next"></param>
        private static void LinkSteps(RecipeStep previous, RecipeStep next)
        {
            if (previous != null && next != null)
            {
                previous.SetNextStep(next);
                next.SetPreviousStep(previous);
            }
            else if (previous == null && next != null)
            {
                next.SetPreviousStep(null);
            }
            else
            {
                previous?.SetNextStep(null);
            }
        }

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

        protected override void InsertItem(int index, RecipeStep step)
        {

            if (step != null)
            {
                // 如果Step没有分配Uid，先分配一个。
                if (string.IsNullOrEmpty(step.StepUid))
                {
                    // 尝试20次，如果均重号，则报错。
                    var retry = 0;
                    for (retry = 0; retry < 20; retry++)
                    {
                        var uid = GlobalDefs.GetShortUid();
                        if (this.FirstOrDefault(x => x.StepUid == uid) == null)
                        {
                            step.StepUid = uid;
                            break;
                        }
                    }

                    if (retry == 20)
                        throw new Exception("unable to add recipe step since the exclusive UID can not be assigned.");
                }
            }

            base.InsertItem(index, step);

            if (step != null)
            {
                step.Parent = Parent;
                RegisterEvents(step);


                if (index > 0 && index < Count - 1)
                {
                    LinkSteps(this[index - 1], this[index]);
                    LinkSteps(this[index], this[index + 1]);
                }
                else if (index == 0)
                {
                    if (Count > 1)
                    {
                        LinkSteps(null, this[index]);
                        LinkSteps(this[index], this[index + 1]);
                    }
                    else
                    {
                        LinkSteps(null, this[index]);
                        LinkSteps(this[index], null);
                    }
                }
                else if (index == Count - 1)
                {
                    LinkSteps(this[index - 1], this[index]);
                    LinkSteps(this[index], null);
                }
            }

            // 如果不是加载Recipe状态，则为新增步骤，需要校验并更新保存状态
            if (!_isLoadingRecipe)
            {
                UpdateStepIndex();
                this[index].Validate();

                // 强制标记为为保存状态
                IsSaved = false;
            }
        }

       

        protected override void RemoveItem(int index)
        {
            if (Count > 0)
            {
                if (index >= 0 && index < Count)
                {
                    UnRegisterEvents(this[index]);
                }
            }

            IsSaved = false;

            // 将前后两个Param链接起来
            var preStep = this[index].Previous;
            var nextStep = this[index].Next;
            LinkSteps(preStep, nextStep);
            preStep?.Validate();
            nextStep?.Validate();

            base.RemoveItem(index);

            UpdateStepIndex();

            // 强制标记为为保存状态
            IsSaved = false;
        }

        
        protected override void ClearItems()
        {
            if (Count > 0)
            {
                this.ToList().ForEach(UnRegisterEvents);
                IsSaved = false;
            }
            else
            {
                IsSaved = true;
            }

            base.ClearItems();
            
            // 强制标记为为保存状态
            IsSaved = false;
        }

        protected override void MoveItem(int oldIndex, int newIndex)
        {
            throw new NotSupportedException();
        }

        #endregion

        public void Dispose()
        {
            
        }
    }
}