using MainShell.Common;
using MainShell.Hardware;
using MainShell.DeviceMaintance.Model;
using MainShell.Log;
using MainShell.Motion;
using MaxwellFramework.Core.Interfaces;
using Stylet;
using StyletIoC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MwFramework.Device;

namespace MainShell.DeviceMaintance.ViewModel
{
    public class LaserCompensationViewModel : DeviceMaintanceBaseViewModel, IPage
    {
        private const string ProjectPageName = "激光补偿";
        private const int MinimumMoveTimeoutMilliseconds = 15000;
        private const double MoveTimeoutScaleFactor = 3d;
        private const int MoveTimeoutExtraMilliseconds = 2000;

        private readonly SafeAxisMotion _safeAxisMotion;
        private readonly AxisSpeedManager _axisSpeedManager;
        private readonly HardwareManager _hardwareManager;
        private CancellationTokenSource _cancellationTokenSource;
        private bool _uiEnable = true;
        private bool _isRunning;
        private string _statusMessage;
        private int _currentLoopIndex;
        private int _currentPointIndex;
        private double _currentTargetPosition;
        private string _selectedAxisName;

        public LaserCompensationViewModel(SafeAxisMotion safeAxisMotion, AxisSpeedManager axisSpeedManager, HardwareManager hardwareManager)
        {
            _safeAxisMotion = safeAxisMotion;
            _axisSpeedManager = axisSpeedManager;
            _hardwareManager = hardwareManager;
            Setting = new LaserCompensationSetting();
            Setting.LoadFromFile();
            AxisNames = new System.Collections.ObjectModel.ObservableCollection<string>(BuildAxisNames());
            if (!string.IsNullOrWhiteSpace(Setting.AxisName) && AxisNames.Contains(Setting.AxisName))
            {
                SelectedAxisName = Setting.AxisName;
            }
            else
            {
                SelectedAxisName = AxisNames.FirstOrDefault();
            }
            StatusMessage = "待机";
        }

        public string Name => "LaserCompensationMaint";

        public LaserCompensationSetting Setting { get; }

        public System.Collections.ObjectModel.ObservableCollection<string> AxisNames { get; }

        public string SelectedAxisName
        {
            get { return _selectedAxisName; }
            set
            {
                if (SetAndNotify(ref _selectedAxisName, value))
                {
                    Setting.AxisName = value;
                }
            }
        }

        public bool UiEnable
        {
            get { return _uiEnable; }
            set
            {
                if (SetAndNotify(ref _uiEnable, value))
                {
                    OnUiStateChanged(value);
                }
            }
        }

        public bool IsRunning
        {
            get { return _isRunning; }
            set { SetAndNotify(ref _isRunning, value); }
        }

        public string StatusMessage
        {
            get { return _statusMessage; }
            set { SetAndNotify(ref _statusMessage, value); }
        }

        public int CurrentLoopIndex
        {
            get { return _currentLoopIndex; }
            set { SetAndNotify(ref _currentLoopIndex, value); }
        }

        public int CurrentPointIndex
        {
            get { return _currentPointIndex; }
            set { SetAndNotify(ref _currentPointIndex, value); }
        }

        public double CurrentTargetPosition
        {
            get { return _currentTargetPosition; }
            set { SetAndNotify(ref _currentTargetPosition, value); }
        }

        public async Task StartAsync()
        {
            if (IsRunning)
            {
                return;
            }

            string validationMessage = ValidateSetting();
            if (!string.IsNullOrWhiteSpace(validationMessage))
            {
                StatusMessage = validationMessage;
                LocalizedMessageBox.Show(MessageKey.ParamInvalid, MessageKey.TitleError, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
                return;
            }

            _cancellationTokenSource?.Dispose();
            _cancellationTokenSource = new CancellationTokenSource();

            UiEnable = false;
            IsRunning = true;
            CurrentLoopIndex = 0;
            CurrentPointIndex = 0;
            CurrentTargetPosition = 0d;
            Setting.AxisName = SelectedAxisName;
            StatusMessage = "激光补偿运行中";

            IoC.Get<IProjectManager>().EnablePageAndDisableOther(ProjectPageName);
            BuildStartLogMessage().LogInfo();

            try
            {
                CancellationToken cancellationToken = _cancellationTokenSource.Token;
                await ExecuteCompensationAsync(cancellationToken).ConfigureAwait(true);

                StatusMessage = "激光补偿执行完成";
                BuildFinishLogMessage("完成").LogInfo();
            }
            catch (OperationCanceledException)
            {
                StatusMessage = "激光补偿已停止";
                BuildFinishLogMessage("已停止").LogInfo();
            }
            catch (Exception ex)
            {
                StatusMessage = $"激光补偿异常：{ex.Message}";
                LogManager.LogSysError(ex, true);
            }
            finally
            {
                IsRunning = false;
                UiEnable = true;
                _cancellationTokenSource?.Dispose();
                _cancellationTokenSource = null;
                IoC.Get<IProjectManager>().SwitchState();
            }
        }

        public void Stop()
        {
            if (!IsRunning)
            {
                return;
            }

            StatusMessage = "正在停止激光补偿";
            "激光补偿收到停止请求".LogInfo();
            _cancellationTokenSource?.Cancel();
        }

        public void Save()
        {
            try
            {
                Setting.SaveToFile();
                StatusMessage = "参数已保存";
                "激光补偿参数已保存".LogInfo();
                LocalizedMessageBox.Show(MessageKey.CommonSaveSucceeded, MessageKey.TitleInfo, System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                StatusMessage = $"参数保存失败：{ex.Message}";
                LogManager.LogSysError(ex, true);
            }
        }

        private async Task ExecuteCompensationAsync(CancellationToken cancellationToken)
        {
            IReadOnlyList<double> trajectory = BuildTrajectory();

            ApplyAxisSpeedIfPossible();

            for (int workIndex = 0; workIndex < Setting.WorkCount; workIndex++)
            {
                cancellationToken.ThrowIfCancellationRequested();
                CurrentLoopIndex = workIndex + 1;
                $"激光补偿开始第{CurrentLoopIndex}次循环，总点数={trajectory.Count}".LogInfo();

                for (int pointIndex = 0; pointIndex < trajectory.Count; pointIndex++)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    double targetPosition = trajectory[pointIndex];
                    CurrentPointIndex = pointIndex + 1;
                    CurrentTargetPosition = targetPosition;
                    StatusMessage = $"循环 {CurrentLoopIndex}/{Setting.WorkCount}，点位 {CurrentPointIndex}/{trajectory.Count}";

                    int timeoutMilliseconds = CalculateMoveTimeoutMilliseconds(targetPosition, trajectory, pointIndex);
                    $"激光补偿移动开始：轴={Setting.AxisName}，循环={CurrentLoopIndex}，点位={CurrentPointIndex}，目标={targetPosition:F4}，超时={timeoutMilliseconds}ms".LogInfo();

                    MotionResult motionResult = await _safeAxisMotion.MoveAbsAsync(
                        Setting.AxisName,
                        targetPosition,
                        timeoutMilliseconds,
                        cancellationToken,
                        alarmId: null).ConfigureAwait(false);

                    motionResult.EnsureSuccess();

                    $"激光补偿移动完成：轴={Setting.AxisName}，循环={CurrentLoopIndex}，点位={CurrentPointIndex}，目标={targetPosition:F4}，到位={motionResult.EndPosition:F4}".LogInfo();

                    if (Setting.DwellTime > 0d)
                    {
                        int dwellMilliseconds = (int)Math.Ceiling(Setting.DwellTime * 1000d);
                        $"激光补偿延时采样：{dwellMilliseconds}ms".LogInfo();
                        await Task.Delay(dwellMilliseconds, cancellationToken).ConfigureAwait(false);
                    }
                }
            }
        }

        private void ApplyAxisSpeedIfPossible()
        {
            try
            {
                if (!string.IsNullOrWhiteSpace(Setting.AxisName) && Setting.Speed > 0d)
                {
                    _axisSpeedManager.SetAxisSpeed(Setting.AxisName, Setting.Speed);
                    $"激光补偿轴速度已设置：轴={Setting.AxisName}，Speed={Setting.Speed:F4}".LogInfo();
                }
            }
            catch (Exception ex)
            {
                $"激光补偿轴速度设置失败：{ex}".LogSysError();
                throw;
            }
        }

        private IReadOnlyList<double> BuildTrajectory()
        {
            List<double> measurePoints = Enumerable.Range(0, Setting.StepsCount)
                .Select(index => Setting.StartPos + index * Setting.Steps)
                .ToList();

            double readyPosition = Setting.StartPos - Setting.JumpPos;
            double overshootPosition = measurePoints[measurePoints.Count - 1] + Setting.JumpPos;

            List<double> trajectory = new List<double>(measurePoints.Count * 2 + 3)
            {
                readyPosition
            };

            trajectory.AddRange(measurePoints);
            trajectory.Add(overshootPosition);
            trajectory.AddRange(measurePoints.AsEnumerable().Reverse());
            trajectory.Add(readyPosition);

            return trajectory;
        }

        private int CalculateMoveTimeoutMilliseconds(double targetPosition, IReadOnlyList<double> trajectory, int pointIndex)
        {
            IAxis axis = _hardwareManager.GetAxisByName(Setting.AxisName);
            double startPosition = GetMoveStartPosition(axis, trajectory, pointIndex, targetPosition);
            double distance = Math.Abs(targetPosition - startPosition);
            double speed = ResolvePositiveMotionParameter(axis != null ? axis.Param.Velocity : 0d, Math.Abs(Setting.Speed));
            if (speed <= 0d)
            {
                return MinimumMoveTimeoutMilliseconds;
            }

            double acceleration = ResolvePositiveMotionParameter(axis != null ? axis.Param.Accelerate : 0d, 0d);
            double deceleration = ResolvePositiveMotionParameter(axis != null ? axis.Param.Decelerate : 0d, acceleration);
            double expectedMilliseconds = CalculateExpectedMoveMilliseconds(distance, speed, acceleration, deceleration);

            return Math.Max(MinimumMoveTimeoutMilliseconds, (int)Math.Ceiling(expectedMilliseconds * MoveTimeoutScaleFactor + MoveTimeoutExtraMilliseconds));
        }

        private static double GetMoveStartPosition(IAxis axis, IReadOnlyList<double> trajectory, int pointIndex, double targetPosition)
        {
            if (pointIndex > 0)
            {
                return trajectory[pointIndex - 1];
            }

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

            return targetPosition;
        }

        private static double ResolvePositiveMotionParameter(double primaryValue, double fallbackValue)
        {
            if (!double.IsNaN(primaryValue) && !double.IsInfinity(primaryValue) && primaryValue > 0d)
            {
                return primaryValue;
            }

            if (!double.IsNaN(fallbackValue) && !double.IsInfinity(fallbackValue) && fallbackValue > 0d)
            {
                return fallbackValue;
            }

            return 0d;
        }

        private static double CalculateExpectedMoveMilliseconds(double distance, double speed, double acceleration, double deceleration)
        {
            if (distance <= 0d)
            {
                return 0d;
            }

            if (speed <= 0d)
            {
                return 0d;
            }

            if (acceleration <= 0d || deceleration <= 0d)
            {
                return distance / speed * 1000d;
            }

            double accelerateDistance = speed * speed / (2d * acceleration);
            double decelerateDistance = speed * speed / (2d * deceleration);
            double criticalDistance = accelerateDistance + decelerateDistance;
            double expectedSeconds;

            if (distance >= criticalDistance)
            {
                expectedSeconds = speed / acceleration + speed / deceleration + (distance - criticalDistance) / speed;
            }
            else
            {
                double peakSpeed = Math.Sqrt(2d * distance * acceleration * deceleration / (acceleration + deceleration));
                expectedSeconds = peakSpeed / acceleration + peakSpeed / deceleration;
            }

            return expectedSeconds * 1000d;
        }

        private string ValidateSetting()
        {
            if (string.IsNullOrWhiteSpace(Setting.AxisName))
            {
                return "轴名称不能为空";
            }

            if (!AxisNames.Contains(Setting.AxisName))
            {
                return "请选择有效的轴名称";
            }

            if (Setting.StepsCount <= 0)
            {
                return "步距次数必须大于0";
            }

            if (Setting.WorkCount <= 0)
            {
                return "循环次数必须大于0";
            }

            if (Math.Abs(Setting.Steps) <= 0d)
            {
                return "步距必须非0";
            }

            if (Setting.JumpPos < 0d)
            {
                return "跃程不能小于0";
            }

            if (Setting.Speed <= 0d)
            {
                return "速度必须大于0";
            }

            if (Setting.DwellTime < 0d)
            {
                return "延时时间不能小于0";
            }

            return null;
        }

        private string BuildStartLogMessage()
        {
            return $"激光补偿启动：轴={Setting.AxisName}，StartPos={Setting.StartPos:F4}，Steps={Setting.Steps:F4}，StepsCount={Setting.StepsCount}，JumpPos={Setting.JumpPos:F4}，WorkCount={Setting.WorkCount}，Speed={Setting.Speed:F4}，DwellTime={Setting.DwellTime:F3}s";
        }

        private string BuildFinishLogMessage(string state)
        {
            return $"激光补偿结束：状态={state}，轴={Setting.AxisName}，已执行循环={CurrentLoopIndex}，最后点位={CurrentPointIndex}，最后目标={CurrentTargetPosition:F4}";
        }

        private IEnumerable<string> BuildAxisNames()
        {
            return new[]
            {
                GetAxisName(_hardwareManager?.Axis_PHS_X1, MainShell.Hardware.AxisName.Axis_PHS_X1),
                GetAxisName(_hardwareManager?.Axis_PHS_X2, MainShell.Hardware.AxisName.Axis_PHS_X2),
                GetAxisName(_hardwareManager?.Axis_PHS_Y1, MainShell.Hardware.AxisName.Axis_PHS_Y1),
                GetAxisName(_hardwareManager?.Axis_WS_X3, MainShell.Hardware.AxisName.Axis_WS_X3),
                GetAxisName(_hardwareManager?.Axis_Stage_Y3, MainShell.Hardware.AxisName.Axis_Stage_Y3),
                GetAxisName(_hardwareManager?.Axis_WS_R, MainShell.Hardware.AxisName.Axis_WS_R),
            }.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToArray();
        }

        private static string GetAxisName(IAxis axis, string fallbackName)
        {
            if (axis == null || string.IsNullOrWhiteSpace(axis.Name))
            {
                return fallbackName;
            }

            return axis.Name;
        }
    }
}
