using MainShell.Common;
using MainShell.Common.VisionTemple.ViewModel;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Motion;
using MainShell.PageCalib.OriginCalib.Model;
using MainShell.PageCalib.OriginCalib.Service;
using MainShell.Resources.CustomControl;
using MwFramework.Device;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace MainShell.PageCalib.OriginCalib.ViewModel
{
    public class OriginCalibModuleViewModel : PropertyChangedBase
    {
        private readonly HardwareManager _hardware;
        private readonly OriginCalibrationMotionService _motionService;
        private readonly OriginCalibModuleItem _item;
        private readonly VisionTempleWindowViewModel _visionTempleWindowViewModel;

        private int _index;
        private bool _isCalibrating;

        public OriginCalibModuleItem Item => _item;

        public int Index
        {
            get { return _index; }
            set { SetAndNotify(ref _index, value); }
        }

        public string ModuleName
        {
            get { return _item.ModuleName; }
            set { _item.ModuleName = value; NotifyOfPropertyChange(nameof(ModuleName)); }
        }

        public bool IsIndependent
        {
            get { return _item.IsIndependent; }
            set
            {
                _item.IsIndependent = value;
                NotifyOfPropertyChange(nameof(IsIndependent));
                NotifyOfPropertyChange(nameof(ShowIndependentBadge));
            }
        }

        public bool IsCalibrating
        {
            get { return _isCalibrating; }
            set
            {
                SetAndNotify(ref _isCalibrating, value);
                NotifyOfPropertyChange(nameof(CanStartCalib));

                // 标定进行中禁用避让示教和删除操作
                foreach (AvoidanceAxisViewModel axis in AvoidanceAxes)
                {
                    axis.CanTeach = !value;
                }
            }
        }

        public bool CanStartCalib
        {
            get { return !IsCalibrating; }
        }

        public bool HasAvoidanceAxes
        {
            get { return AvoidanceAxes.Count > 0; }
        }

        public System.Windows.Visibility ShowIndependentBadge
        {
            get { return IsIndependent ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; }
        }

        public System.Windows.Visibility ShowAvoidanceBadge
        {
            get { return HasAvoidanceAxes ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; }
        }

        public ObservableCollection<AvoidanceAxisViewModel> AvoidanceAxes { get; } = new ObservableCollection<AvoidanceAxisViewModel>();
        public ObservableCollection<CalibrationAxisViewModel> CalibrationAxes { get; } = new ObservableCollection<CalibrationAxisViewModel>();

        public OriginCalibModuleViewModel(
            OriginCalibModuleItem item,
            HardwareManager hardware,
            OriginCalibrationMotionService motionService,
            VisionTempleWindowViewModel visionTempleWindowViewModel)
        {
            _item = item ?? throw new ArgumentNullException(nameof(item));
            _hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
            _motionService = motionService ?? throw new ArgumentNullException(nameof(motionService));
            _visionTempleWindowViewModel = visionTempleWindowViewModel ?? throw new ArgumentNullException(nameof(visionTempleWindowViewModel));

            foreach (AvoidanceAxisItem axisItem in item.AvoidanceAxes)
            {
                AvoidanceAxes.Add(new AvoidanceAxisViewModel(axisItem, hardware, motionService, OnRemoveAvoidanceAxis));
            }

            foreach (CalibrationAxisItem calibItem in item.CalibrationAxes)
            {
                CalibrationAxes.Add(new CalibrationAxisViewModel(calibItem));
            }
        }

        public void AddAvoidanceAxis()
        {
            AvoidanceAxisItem newItem = new AvoidanceAxisItem { AxisName = "NewAxis", Position = 0.0 };
            _item.AvoidanceAxes.Add(newItem);
            AvoidanceAxisViewModel newVm = new AvoidanceAxisViewModel(newItem, _hardware, _motionService, OnRemoveAvoidanceAxis);
            newVm.CanTeach = !_isCalibrating;
            AvoidanceAxes.Add(newVm);
            NotifyOfPropertyChange(nameof(HasAvoidanceAxes));
            NotifyOfPropertyChange(nameof(ShowAvoidanceBadge));
        }

        private void OnRemoveAvoidanceAxis(AvoidanceAxisViewModel vm)
        {
            if (_isCalibrating)
            {
                "原点标定进行中，无法删除避让轴。".LogSysError();
                LocalizedMessageBox.Show(MessageKey.OriginCalibDeleteAxisDuringCalibration, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            try
            {
                string.Format("原点标定模块“{1}”准备删除避让轴“{0}”。", vm.AxisName, ModuleName).LogInfo();
                _item.AvoidanceAxes.Remove(vm.Item);
                AvoidanceAxes.Remove(vm);
                NotifyOfPropertyChange(nameof(HasAvoidanceAxes));
                NotifyOfPropertyChange(nameof(ShowAvoidanceBadge));
                string.Format("原点标定删除避让轴成功，当前剩余 {0} 个避让轴。", AvoidanceAxes.Count).LogInfo();
            }
            catch (Exception ex)
            {
                string.Format("原点标定删除避让轴“{0}”失败：{1}", vm.AxisName, ex.Message).LogSysError();
                LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibDeleteAxisFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
            }
        }

        public void TeachCalibPositions()
        {
            foreach (CalibrationAxisViewModel calibAxis in CalibrationAxes)
            {
                if (_hardware.AxesDic != null && _hardware.AxesDic.ContainsKey(calibAxis.AxisName))
                {
                    calibAxis.TargetPosition = Math.Round(_hardware.AxesDic[calibAxis.AxisName].State.ActualPos, 3);
                }
            }
        }

        public void MakeTemplate()
        {
            try
            {
                if(Item.ModuleName== OriginCalibModuleNames.GantryX2)
                {
                    _visionTempleWindowViewModel.SetCamera(CameraType.TopWideCamera);
                }
                else
                {
                    _visionTempleWindowViewModel.SetCamera(CameraType.TopPositionCamera);
                }
                _visionTempleWindowViewModel.TemplatePath = _item.TemplatePath;
                $"原点标定模块“{ModuleName}”打开视觉模板制作窗口。".LogInfo();
                _visionTempleWindowViewModel.ShowWindow();
            }
            catch (Exception ex)
            {
                $"原点标定模块“{ModuleName}”打开视觉模板制作窗口失败：{ex.Message}".LogSysError();
                LocalizedMessageBox.ShowFormat(
                    MessageKey.OriginCalibOpenVisionTemplateFailed,
                    MessageKey.TitleError,
                    MessageBoxButton.OK,
                    MessageBoxImage.Error,
                    ex.Message);
            }
        }

        public async Task StartCalib()
        {
            if (IsCalibrating)
            {
                return;
            }

            IsCalibrating = true;
            LoadingService.Instance.Show(string.Format("模块 {0} 正在标定...", ModuleName), true, true);
            try
            {
                string.Format(
                    "原点标定模块“{0}”开始标定，避让轴数量：{1}，标定轴数量：{2}。",
                    ModuleName,
                    AvoidanceAxes.Count,
                    CalibrationAxes.Count).LogInfo();

                var calibrationResult = await _motionService.ExecuteAsync(
                    AvoidanceAxes.Select(x => x.Item),
                    CalibrationAxes.Select(x => x.Item),
                    LoadingService.Instance.Token);

                // 执行模块特定的后处理逻辑，通常在运动完成后、标定写入前
                if (_item.PostProcessor != null)
                {
                    string.Format(
                        "原点标定模块“{0}”执行后处理“{1}”。",
                        ModuleName,
                        _item.PostProcessor.Name).LogInfo();

                    try
                    {
                        await _item.PostProcessor.ExecuteAsync(calibrationResult, LoadingService.Instance.Token, new CenterRecognitionParameters
                        {
                            TemplatePath = _item.TemplatePath,
                            Type = CenterRecognitionType.Template
                        });
                        string.Format(
                            "原点标定模块“{0}”后处理“{1}”完成。",
                            ModuleName,
                            _item.PostProcessor.Name).LogInfo();
                    }
                    catch (OperationCanceledException)
                    {
                        string.Format(
                            "原点标定模块“{0}”后处理“{1}”被取消。",
                            ModuleName,
                            _item.PostProcessor.Name).LogInfo();
                        throw;
                    }
                    catch (Exception ex)
                    {
                        string.Format(
                            "原点标定模块“{0}”后处理“{1}”失败：{2}",
                            ModuleName,
                            _item.PostProcessor.Name,
                            ex.Message).LogSysError();
                        throw;
                    }
                }

                foreach (CalibrationAxisViewModel calibAxis in CalibrationAxes)
                {
                    IAxis axis = ResolveCalibrationAxis(calibAxis.AxisName);
                    if (axis != null)
                    {
                        calibAxis.LastPosition = Math.Round(GetActualPosition(axis), 3);
                        calibAxis.IsCalibrated = true;
                        continue;
                    }

                    double lastPosition;
                    if (calibrationResult.ActualPositions.TryGetValue(calibAxis.AxisName, out lastPosition))
                    {
                        calibAxis.LastPosition = lastPosition;
                        calibAxis.IsCalibrated = true;
                    }
                }

                string.Format("原点标定模块“{0}”标定完成。", ModuleName).LogInfo();
            }
            catch (OperationCanceledException)
            {
                string.Format("原点标定模块“{0}”取消标定。", ModuleName).LogInfo();
                LocalizedMessageBox.Show(MessageKey.OriginCalibCalibrationCanceled, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            catch (Exception ex)
            {
                string.Format("原点标定模块“{0}”标定失败：{1}", ModuleName, ex.Message).LogSysError();
                LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibCalibrationFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
            }
            finally
            {
                LoadingService.Instance.Hide();
                IsCalibrating = false;
            }
        }

        public void ResetModule()
        {
            foreach (CalibrationAxisViewModel calibAxis in CalibrationAxes)
            {
                calibAxis.Reset();
            }

            string.Format("原点标定模块“{0}”已复位。", ModuleName).LogInfo();
        }

        private IAxis ResolveCalibrationAxis(string axisName)
        {
            if (string.IsNullOrWhiteSpace(axisName))
            {
                return null;
            }

            if (_hardware.AxesDic != null && _hardware.AxesDic.ContainsKey(axisName))
            {
                return _hardware.AxesDic[axisName];
            }

            if (string.Equals(axisName, "X1", StringComparison.OrdinalIgnoreCase))
            {
                return _hardware.Axis_X1;
            }

            if (string.Equals(axisName, "Y1", StringComparison.OrdinalIgnoreCase))
            {
                return _hardware.Axis_Y1;
            }

            if (string.Equals(axisName, "X2", StringComparison.OrdinalIgnoreCase))
            {
                return _hardware.Axis_X2;
            }

            if (string.Equals(axisName, "Y2", StringComparison.OrdinalIgnoreCase))
            {
                return _hardware.Axis_Y2;
            }

            if (string.Equals(axisName, "WSX3", StringComparison.OrdinalIgnoreCase))
            {
                return _hardware.Axis_WS_X3;
            }

            return null;
        }

        private static double GetActualPosition(IAxis axis)
        {
            if (axis == null)
            {
                return 0;
            }

            return axis.State != null ? axis.State.ActualPos : axis.GetPositionImmediate();
        }
    }
}
