using MainShell.Common;
using MainShell.Common.Display.Models;
using MainShell.Log;
using MainShell.Motion;
using Maxwell.SemiFramework.DefaultConfig.Vision;
using MwFramework.Controls.Components;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.Controls.UIControl;
using MwFramework.Device;
using MwFramework.ManagerService;
using Stylet;
using StyletIoC;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace MainShell.Common.Display.ViewModel
{
    public class CameraAxisViewModel : Screen
    {
        private const double DefaultFovX = 14.1 * 10;
        private const double DefaultFovY = 10.3 * 10;
        private const string SafeAxisMotionNotInjectedMessage = "SafeAxisMotion is not injected.";

        private readonly object _moveLock = new object();

        private CameraAxisDevices _cameraAxisDevices = new CameraAxisDevices();
        private double _xPixelPos;
        private double _yPixelPos;
        private double _cameraXIndex;
        private double _cameraYIndex;
        private GetConvertValue _convertFunXDelegate;
        private GetConvertValue _convertFunYDelegate;
        private ObservableCollection<PrimShape> _regions = new ObservableCollection<PrimShape>();
        private bool _isCameraEnable = true;
        private Visibility _controlVisibility = Visibility.Visible;
        private bool _isOpen;

        [Inject]
        public SafeAxisMotion SafeAxisMotion { get; set; }

        public CameraAxisViewModel()
        {
            ConvertFunXDelegate = new GetConvertValue(ConvertFunX);
            ConvertFunYDelegate = new GetConvertValue(ConvertFunY);
            OnLeftMouseDownCommand = new DelegateCommand<object>(OnLeftMouseDown);
        }

        public CameraAxisDevices CameraAxisDevices
        {
            get { return _cameraAxisDevices; }
            set { SetAndNotify(ref _cameraAxisDevices, value); }
        }

        public double XPixelPos
        {
            get { return _xPixelPos; }
            set { SetAndNotify(ref _xPixelPos, value); }
        }

        public double YPixelPos
        {
            get { return _yPixelPos; }
            set { SetAndNotify(ref _yPixelPos, value); }
        }

        public double CameraXIndex
        {
            get { return _cameraXIndex; }
            set { SetAndNotify(ref _cameraXIndex, value); }
        }

        public double CameraYIndex
        {
            get { return _cameraYIndex; }
            set { SetAndNotify(ref _cameraYIndex, value); }
        }

        public GetConvertValue ConvertFunXDelegate
        {
            get { return _convertFunXDelegate; }
            set { SetAndNotify(ref _convertFunXDelegate, value); }
        }

        public GetConvertValue ConvertFunYDelegate
        {
            get { return _convertFunYDelegate; }
            set { SetAndNotify(ref _convertFunYDelegate, value); }
        }

        public ObservableCollection<PrimShape> Regions
        {
            get { return _regions; }
            set { SetAndNotify(ref _regions, value); }
        }

        public bool IsCameraEnable
        {
            get { return _isCameraEnable; }
            set { SetAndNotify(ref _isCameraEnable, value); }
        }

        public Visibility ControlVisibility
        {
            get { return _controlVisibility; }
            set { SetAndNotify(ref _controlVisibility, value); }
        }

        public ICommand OnLeftMouseDownCommand { get; private set; }

        public bool IsOpen
        {
            get { return _isOpen; }
            set { SetAndNotify(ref _isOpen, value); }
        }

        public void OnLeftMouseDown(object sender)
        {
            try
            {
                HardwareDevice selectedDevice;
                if (!TryGetSelectedDevice(out selectedDevice))
                {
                    return;
                }

                CameraAxisControlA controlA = sender as CameraAxisControlA;
                if (controlA == null)
                {
                    return;
                }

                Point pixelPos = new Point(controlA.YPixPos, controlA.XPixPos);
                Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, false));
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                ShowOperationFailed(ex.Message);
            }
        }

        public void MovePosAction()
        {
            try
            {
                HardwareDevice selectedDevice;
                if (!TryGetSelectedDevice(out selectedDevice))
                {
                    return;
                }

                Point pixelPos = new Point(YPixelPos, XPixelPos);
                Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, true));
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                ShowOperationFailed(ex.Message);
            }
        }

        public double ConvertFunX(double interval)
        {
            try
            {
                HardwareDevice selectedDevice;
                if (!TryGetSelectedDevice(out selectedDevice))
                {
                    return 0;
                }

                string name = selectedDevice.Camera.Id;
                double dx;
                double dy;
                VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
                double pixelX = interval / Math.Abs(dx);
                return pixelX;
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                ShowOperationFailed(ex.Message);
                return 0;
            }
        }

        public double ConvertFunY(double interval)
        {
            try
            {
                HardwareDevice selectedDevice;
                if (!TryGetSelectedDevice(out selectedDevice))
                {
                    return 0;
                }

                string name = selectedDevice.Camera.Id;
                double dx;
                double dy;
                VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
                double pixelY = interval / Math.Abs(dy);
                return pixelY;
            }
            catch (Exception ex)
            {
                LogManager.LogSysError(ex);
                ShowOperationFailed(ex.Message);
                return 0;
            }
        }

        private bool TryGetSelectedDevice(out HardwareDevice selectedDevice)
        {
            selectedDevice = CameraAxisDevices != null ? CameraAxisDevices.SelectedHardwareDevice : null;
            if (!IsOpen || selectedDevice == null)
            {
                return false;
            }

            if (selectedDevice.Camera == null || selectedDevice.Camera.Camera == null)
            {
                ShowDeviceNotInitialized();
                return false;
            }

            if (!selectedDevice.Camera.Camera.IsGrabbing)
            {
                LocalizedMessageBox.Show(MessageKey.VisionCameraNotGrabbing, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }

            if (selectedDevice.AxisX == null || selectedDevice.AxisY == null)
            {
                ShowDeviceNotInitialized();
                return false;
            }

            return true;
        }

        private void MoveToPixelPoint(HardwareDevice selectedDevice, Point pixelPos, bool requireConfirmation)
        {
            lock (_moveLock)
            {
                try
                {
                    IAxis axisX = selectedDevice.AxisX;
                    IAxis axisY = selectedDevice.AxisY;
                    Point rulerPos = new Point(axisX.State.ActualPos, axisY.State.ActualPos);
                    Point newRulerPoint = Algorithm.Instance.MoveToPixelPoint(selectedDevice.Camera.Id, rulerPos, pixelPos);

                    if (requireConfirmation)
                    {
                        double dx = newRulerPoint.X - rulerPos.X;
                        double dy = newRulerPoint.Y - rulerPos.Y;
                        if (!ValidateMoveRange(dx, dy))
                        {
                            return;
                        }

                        if (!ConfirmMove(newRulerPoint))
                        {
                            return;
                        }
                    }

                    if (SafeAxisMotion == null)
                    {
                        InvalidOperationException exception = new InvalidOperationException(SafeAxisMotionNotInjectedMessage);
                        LogManager.LogSysError(exception);
                        ShowOperationFailed(SafeAxisMotionNotInjectedMessage);
                        return;
                    }

                    SafeAxisMotion.SafeMove(
                        MotionMoveRequest.ForAxis(axisX, newRulerPoint.X),
                        MotionMoveRequest.ForAxis(axisY, newRulerPoint.Y));
                }
                catch (Exception ex)
                {
                    LogManager.LogSysError(ex);
                    ShowOperationFailed(ex.Message);
                }
            }
        }

        private static bool ValidateMoveRange(double dx, double dy)
        {
            if (Math.Abs(dx) > DefaultFovX || Math.Abs(dy) > DefaultFovY)
            {
                LocalizedMessageBox.Show(MessageKey.ParamOutOfRange, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }

            return true;
        }

        private static bool ConfirmMove(Point newRulerPoint)
        {
            string message = string.Format("新点的坐标({0},{1})，是否移动？", newRulerPoint.X, newRulerPoint.Y);
            return MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleConfirm), MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel) == MessageBoxResult.OK;
        }

        private static void ShowDeviceNotInitialized()
        {
            LocalizedMessageBox.Show(MessageKey.DeviceNotInitialized, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
        }

        private static void ShowOperationFailed(string message)
        {
            if (string.IsNullOrWhiteSpace(message))
            {
                LocalizedMessageBox.Show(MessageKey.CommonOperationFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }
}
