using MainShell.Common;
using MainShell.Filewritable;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Models;
using MainShell.ParaSetting.Model;
using MwFramework.Device;
using MwFramework.Device.Drivers;
using MwFramework.Device.Motion;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace MainShell.ParaSetting.ViewModel
{
    public class AxisSoftLimitSettingViewModel : BaseScreen
    {
        private const string AxisSoftLimitSettingFileName = "AxisSoftLimitSetting.json";

        private readonly HardwareManager _hardwareManager;
        private readonly string _axisSoftLimitSettingFilePath;

        private AxisSoftLimitSetting _axisSoftLimitSetting;

        private ObservableCollection<AxisSoftLimitItemViewModel> _axisSoftLimitItems = new ObservableCollection<AxisSoftLimitItemViewModel>();
        public ObservableCollection<AxisSoftLimitItemViewModel> AxisSoftLimitItems
        {
            get
            {
                return _axisSoftLimitItems;
            }
            set
            {
                SetAndNotify(ref _axisSoftLimitItems, value);
            }
        }

        private AxisSoftLimitItemViewModel _selectedAxisSoftLimitItem;
        public AxisSoftLimitItemViewModel SelectedAxisSoftLimitItem
        {
            get
            {
                return _selectedAxisSoftLimitItem;
            }
            set
            {
                SetAndNotify(ref _selectedAxisSoftLimitItem, value);
            }
        }

        private string _parameterFilePath;
        public string ParameterFilePath
        {
            get
            {
                return _parameterFilePath;
            }
            set
            {
                SetAndNotify(ref _parameterFilePath, value);
            }
        }

        public AxisSoftLimitSettingViewModel(HardwareManager hardwareManager)
        {
            _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
            _axisSoftLimitSettingFilePath = System.IO.Path.Combine(Paths.CalibSettingPath, AxisSoftLimitSettingFileName);
            ParameterFilePath = _axisSoftLimitSettingFilePath;
        }

        protected override void OnViewLoaded()
        {
            base.OnViewLoaded();
            LoadAxisSoftLimitItems();
        }

        public void BtnSave()
        {
            if (!ValidateAxisSoftLimitItems())
            {
                return;
            }

            MessageBoxResult confirmResult = LocalizedMessageBox.Show(MessageKey.AxisSoftLimitSaveConfirm, MessageKey.TitleConfirm, MessageBoxButton.OKCancel, MessageBoxImage.Question);
            if (confirmResult != MessageBoxResult.OK)
            {
                return;
            }

            try
            {
                System.IO.Directory.CreateDirectory(Paths.CalibSettingPath);
                AxisSoftLimitSetting axisSoftLimitSetting = CreateAxisSoftLimitSetting();
                foreach (AxisSoftLimitItemViewModel axisSoftLimitItem in AxisSoftLimitItems)
                {
                    AxisSoftLimitItem axisSoftLimitItemModel = new AxisSoftLimitItem();
                    axisSoftLimitItemModel.AxisName = axisSoftLimitItem.AxisName;
                    axisSoftLimitItemModel.CardName = axisSoftLimitItem.CardName;
                    axisSoftLimitItemModel.CardNum = axisSoftLimitItem.CardNum;
                    axisSoftLimitItemModel.AxisNum = axisSoftLimitItem.AxisNum;
                    axisSoftLimitItemModel.NegativeSoftLimit = axisSoftLimitItem.NegativeSoftLimit;
                    axisSoftLimitItemModel.PositiveSoftLimit = axisSoftLimitItem.PositiveSoftLimit;
                    axisSoftLimitSetting.AxisSoftLimitItems.Add(axisSoftLimitItemModel);
                    UpdateAxisCheckLimit(axisSoftLimitItem);
                }

                axisSoftLimitSetting.Write(_axisSoftLimitSettingFilePath);
                _axisSoftLimitSetting = axisSoftLimitSetting;
                LocalizedMessageBox.Show(MessageKey.CommonSaveSucceeded, MessageKey.TitleInfo);
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                LocalizedMessageBox.ShowFormat(MessageKey.AxisSoftLimitSaveFailedWithReason, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
            }
        }

        public void BtnApply()
        {
            if (!ValidateAxisSoftLimitItems())
            {
                return;
            }

            AxisSoftLimitItemViewModel axisSoftLimitItem = SelectedAxisSoftLimitItem;
            if (axisSoftLimitItem == null)
            {
                LocalizedMessageBox.Show(MessageKey.AxisSoftLimitSelectedAxisRequired, MessageKey.TitleError);
                return;
            }

            try
            {
                ApplyAxisSoftLimitToController(axisSoftLimitItem);
                LocalizedMessageBox.ShowFormat(MessageKey.AxisSoftLimitApplySucceeded, MessageKey.TitleInfo, MessageBoxButton.OK, MessageBoxImage.Information, axisSoftLimitItem.AxisName);
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                LocalizedMessageBox.ShowFormat(MessageKey.AxisSoftLimitApplyFailedWithReason, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, axisSoftLimitItem.AxisName, ex.Message);
            }
        }

        public void BtnApplyAll()
        {
            if (!ValidateAxisSoftLimitItems())
            {
                return;
            }

            if (AxisSoftLimitItems == null || AxisSoftLimitItems.Count == 0)
            {
                LocalizedMessageBox.Show(MessageKey.AxisSoftLimitNoAxisData, MessageKey.TitleError);
                return;
            }

            try
            {
                int appliedAxisCount = 0;
                foreach (AxisSoftLimitItemViewModel axisSoftLimitItem in AxisSoftLimitItems)
                {
                    ApplyAxisSoftLimitToController(axisSoftLimitItem);
                    appliedAxisCount++;
                }

                LocalizedMessageBox.ShowFormat(MessageKey.AxisSoftLimitApplyAllSucceeded, MessageKey.TitleInfo, MessageBoxButton.OK, MessageBoxImage.Information, appliedAxisCount);
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                LocalizedMessageBox.ShowFormat(MessageKey.AxisSoftLimitApplyAllFailedWithReason, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
            }
        }

        private void LoadAxisSoftLimitItems()
        {
            try
            {
                AxisSoftLimitSetting axisSoftLimitSetting = CreateAxisSoftLimitSetting();
                if (System.IO.File.Exists(_axisSoftLimitSettingFilePath))
                {
                    axisSoftLimitSetting.Read(_axisSoftLimitSettingFilePath);
                }

                _axisSoftLimitSetting = axisSoftLimitSetting;
                AxisSoftLimitItems = BuildAxisSoftLimitItems(axisSoftLimitSetting);
                SelectedAxisSoftLimitItem = AxisSoftLimitItems.FirstOrDefault();
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                _axisSoftLimitSetting = CreateAxisSoftLimitSetting();
                AxisSoftLimitItems = BuildAxisSoftLimitItems(_axisSoftLimitSetting);
                SelectedAxisSoftLimitItem = AxisSoftLimitItems.FirstOrDefault();
                LocalizedMessageBox.ShowFormat(MessageKey.AxisSoftLimitLoadFailedWithReason, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
            }
        }

        private ObservableCollection<AxisSoftLimitItemViewModel> BuildAxisSoftLimitItems(AxisSoftLimitSetting axisSoftLimitSetting)
        {
            ObservableCollection<AxisSoftLimitItemViewModel> items = new ObservableCollection<AxisSoftLimitItemViewModel>();
            System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, IAxis>> axisPairs = Enumerable.Empty<System.Collections.Generic.KeyValuePair<string, IAxis>>();
            if (_hardwareManager.AxesDic != null)
            {
                axisPairs = _hardwareManager.AxesDic.OrderBy(item => item.Key);
            }

            foreach (System.Collections.Generic.KeyValuePair<string, IAxis> axisPair in axisPairs)
            {
                IAxis axis = axisPair.Value;
                if (axis == null)
                {
                    continue;
                }

                AxisCardParaPO axisCardParaPo = axis.AxisPO as AxisCardParaPO;
                int cardNum = axisCardParaPo != null ? axisCardParaPo.AxisCardNum : -1;
                string cardName = axisCardParaPo != null ? axisCardParaPo.AxisCardName : string.Empty;
                AxisSoftLimitItem axisSoftLimitItem = FindAxisSoftLimitItem(axisSoftLimitSetting, cardNum, axis.AxisIndex);
                double negativeSoftLimit = axisSoftLimitItem != null ? axisSoftLimitItem.NegativeSoftLimit : 0d;
                double positiveSoftLimit = axisSoftLimitItem != null
                    ? axisSoftLimitItem.PositiveSoftLimit
                    : axisCardParaPo != null ? axisCardParaPo.MaxCheckPos : 0d;

                AxisSoftLimitItemViewModel item = new AxisSoftLimitItemViewModel(axis, axisPair.Key, cardName, cardNum, axis.AxisIndex, negativeSoftLimit, positiveSoftLimit);
                items.Add(item);
            }

            return items;
        }

        private static AxisSoftLimitItem FindAxisSoftLimitItem(AxisSoftLimitSetting axisSoftLimitSetting, int cardNum, int axisNum)
        {
            if (axisSoftLimitSetting == null || axisSoftLimitSetting.AxisSoftLimitItems == null)
            {
                return null;
            }

            return axisSoftLimitSetting.AxisSoftLimitItems.FirstOrDefault(item => item != null && item.CardNum == cardNum && item.AxisNum == axisNum);
        }

        private static void UpdateAxisCheckLimit(AxisSoftLimitItemViewModel axisSoftLimitItem)
        {
            AxisCardParaPO axisCardParaPo = axisSoftLimitItem.Axis == null ? null : axisSoftLimitItem.Axis.AxisPO as AxisCardParaPO;
            if (axisCardParaPo != null)
            {
                axisCardParaPo.MaxCheckPos = axisSoftLimitItem.PositiveSoftLimit;
            }
        }

        private static void ApplyAxisSoftLimitToController(AxisSoftLimitItemViewModel axisSoftLimitItem)
        {
            if (axisSoftLimitItem == null)
            {
                throw new ArgumentNullException(nameof(axisSoftLimitItem));
            }

            IAxis axis = axisSoftLimitItem.Axis;
            if (axis == null)
            {
                throw new InvalidOperationException("当前轴对象无效，无法下发软限位设置。");
            }

            IAxisFunc axisFunc = axis as IAxisFunc;
            if (axisFunc == null)
            {
                throw new InvalidOperationException($"轴 {axisSoftLimitItem.AxisName} 不支持软限位设置接口。");
            }

            IMotionController controller = axis.Parent;
            if (controller == null || !controller.IsOpen)
            {
                throw new InvalidOperationException($"轴 {axisSoftLimitItem.AxisName} 所属控制器未连接或未打开。");
            }

            EnsureMotionCommandSucceeded(axisFunc.SetSoftMel(axisSoftLimitItem.NegativeSoftLimit), axisSoftLimitItem.AxisName, "负软限位");
            EnsureMotionCommandSucceeded(axisFunc.SetSoftPel(axisSoftLimitItem.PositiveSoftLimit), axisSoftLimitItem.AxisName, "正软限位");
            EnsureMotionCommandSucceeded(axisFunc.IssueParam(), axisSoftLimitItem.AxisName, "参数下发");
            UpdateAxisCheckLimit(axisSoftLimitItem);
        }

        private static void EnsureMotionCommandSucceeded(MotionErrorCode motionErrorCode, string axisName, string actionName)
        {
            if (motionErrorCode != MotionErrorCode.NoError)
            {
                throw new InvalidOperationException($"轴 {axisName} {actionName}失败，错误码：{motionErrorCode}");
            }
        }

        private bool ValidateAxisSoftLimitItems()
        {
            foreach (AxisSoftLimitItemViewModel axisSoftLimitItem in AxisSoftLimitItems)
            {
                if (axisSoftLimitItem.PositiveSoftLimit < axisSoftLimitItem.NegativeSoftLimit)
                {
                    LocalizedMessageBox.ShowFormat(MessageKey.AxisSoftLimitRangeInvalid, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, axisSoftLimitItem.AxisName);
                    SelectedAxisSoftLimitItem = axisSoftLimitItem;
                    return false;
                }
            }

            return true;
        }

        private static AxisSoftLimitSetting CreateAxisSoftLimitSetting()
        {
            return new AxisSoftLimitSetting();
        }
    }

    public class AxisSoftLimitItemViewModel : PropertyChangedBase
    {
        private double _negativeSoftLimit;
        private double _positiveSoftLimit;

        public AxisSoftLimitItemViewModel(IAxis axis, string axisName, string cardName, int cardNum, int axisNum, double negativeSoftLimit, double positiveSoftLimit)
        {
            Axis = axis;
            AxisName = axisName;
            CardName = cardName;
            CardNum = cardNum;
            AxisNum = axisNum;
            _negativeSoftLimit = negativeSoftLimit;
            _positiveSoftLimit = positiveSoftLimit;
        }

        public IAxis Axis { get; private set; }

        public string AxisName { get; private set; }

        public string CardName { get; private set; }

        public int CardNum { get; private set; }

        public int AxisNum { get; private set; }

        public double NegativeSoftLimit
        {
            get
            {
                return _negativeSoftLimit;
            }
            set
            {
                SetAndNotify(ref _negativeSoftLimit, value);
            }
        }

        public double PositiveSoftLimit
        {
            get
            {
                return _positiveSoftLimit;
            }
            set
            {
                SetAndNotify(ref _positiveSoftLimit, value);
            }
        }
    }
}
