using MainShell.Common;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.ParaSetting.Model;
using MainShell.Vision;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Device;
using MwFramework.ManagerService;
using System;
using System.Globalization;
using System.Reflection;
using System.Windows;

namespace MainShell.Process
{
    public class DefaultWaferMachineAdapter : IWaferMachineAdapter
    {
        private readonly HardwareManager _hardwareManager;
        private readonly IParamList _parameterList;

        public DefaultWaferMachineAdapter(HardwareManager hardwareManager, IParameterManager parameterManager)
        {
            _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
            _parameterList = parameterManager as IParamList;
        }

        public CameraType GetCameraSource(WaferDiePositionContext context)
        {
            return CameraType.TopPositionCamera;
        }

        public MwCamera GetCamera(WaferDiePositionContext context)
        {
            return ResolveCamera(GetCameraSource(context));
        }

        public Point GetCameraFieldOfView(WaferDiePositionContext context)
        {
            DeviceFoundationSetting deviceFoundationSetting = GetDeviceFoundationSetting();
            CameraFovSettingItem cameraFovSettingItem = GetCameraFovSettingItem(deviceFoundationSetting, GetCameraSource(context));
            if (cameraFovSettingItem == null || cameraFovSettingItem.FovX <= 0d || cameraFovSettingItem.FovY <= 0d)
            {
                LogManager.LogProcessInfo("晶圆定位：DefaultWaferMachineAdapter 正在使用相机视野默认回退值。");
                return new Point(1.0d, 1.0d);
            }

            return new Point(cameraFovSettingItem.FovX, cameraFovSettingItem.FovY);
        }

        public double GetExposureTimeMilliseconds(WaferDiePositionContext context)
        {
            MwCamera camera = GetCamera(context);
            if (camera == null)
            {
                return 0d;
            }

            try
            {
                double exposureTime = 0d;
                DriverLibResult result = camera.GetExposureTime(ref exposureTime);
                if (result != DriverLibResult.DriverLibNoError || double.IsNaN(exposureTime) || double.IsInfinity(exposureTime) || exposureTime < 0d)
                {
                    return 0d;
                }

                return exposureTime;
            }
            catch (Exception)
            {
                return 0d;
            }
        }

        public CameraCaptureOptions CreateSoftTriggerCaptureOptions(WaferDiePositionContext context)
        {
            CameraCaptureOptions options = CameraCaptureOptions.CreateSoftTrigger(5000);
            options.TriggerDelay = 20d;
            return options;
        }

        public Point AdjustPointWithinSoftLimit(Point candidatePoint, WaferDiePositionContext context, double offsetMm, out bool adjusted, out string reason)
        {
            IAxis axisX = _hardwareManager.Axis_PHS_X1;
            IAxis axisY = _hardwareManager.Axis_Stage_Y3;
            double adjustedX = candidatePoint.X;
            double adjustedY = candidatePoint.Y;
            bool adjustedXFlag = TryClampByAxisSoftLimit(axisX, adjustedX, offsetMm, out adjustedX, out string adjustedXReason);
            bool adjustedYFlag = TryClampByAxisSoftLimit(axisY, adjustedY, offsetMm, out adjustedY, out string adjustedYReason);

            adjusted = adjustedXFlag || adjustedYFlag;
            if (!adjusted)
            {
                reason = string.Empty;
                return candidatePoint;
            }

            reason = BuildAdjustmentReason(adjustedXReason, adjustedYReason);
            return new Point(adjustedX, adjustedY);
        }

        private DeviceFoundationSetting GetDeviceFoundationSetting()
        {
            return _parameterList == null ? null : _parameterList.GetParameter<DeviceFoundationSetting>();
        }

        private MwCamera ResolveCamera(CameraType cameraType)
        {
            MwCameraExtend cameraExtend = GetCameraExtend(cameraType);
            if (cameraExtend == null)
            {
                return null;
            }

            return cameraExtend.Camera;
        }

        private MwCameraExtend GetCameraExtend(CameraType cameraType)
        {
            return _hardwareManager.GetCamera(cameraType);
        }

        private static CameraFovSettingItem GetCameraFovSettingItem(DeviceFoundationSetting deviceFoundationSetting, CameraType cameraType)
        {
            if (deviceFoundationSetting == null || deviceFoundationSetting.CameraSettingItem == null)
            {
                return null;
            }

            deviceFoundationSetting.CameraSettingItem.EnsureDefaultCameras();
            switch (cameraType)
            {
                case CameraType.TopWideCamera:
                    return deviceFoundationSetting.CameraSettingItem.DownCamera;

                case CameraType.MapCamera:
                    return deviceFoundationSetting.CameraSettingItem.MapCamera;

                case CameraType.TopPositionCamera:
                default:
                    return deviceFoundationSetting.CameraSettingItem.UpCamera;
            }
        }

        private static bool TryClampByAxisSoftLimit(IAxis axis, double candidateValue, double offsetMm, out double adjustedValue, out string reason)
        {
            adjustedValue = candidateValue;
            reason = string.Empty;

            if (axis == null)
            {
                return false;
            }

            bool hasNegativeLimit = TryGetAxisLimit(axis, true, out double negativeLimit);
            bool hasPositiveLimit = TryGetAxisLimit(axis, false, out double positiveLimit);
            if (!hasNegativeLimit && !hasPositiveLimit)
            {
                return false;
            }

            double minLimit = hasNegativeLimit && hasPositiveLimit ? Math.Min(negativeLimit, positiveLimit) : negativeLimit;
            double maxLimit = hasNegativeLimit && hasPositiveLimit ? Math.Max(negativeLimit, positiveLimit) : positiveLimit;
            double minAllowed = hasNegativeLimit ? minLimit + Math.Max(0d, offsetMm) : double.NegativeInfinity;
            double maxAllowed = hasPositiveLimit ? maxLimit - Math.Max(0d, offsetMm) : double.PositiveInfinity;

            if (candidateValue < minAllowed)
            {
                adjustedValue = minAllowed;
                reason = string.Format(
                    CultureInfo.InvariantCulture,
                    "{0}: clamp to negative soft limit {1:F3} with offset {2:F3}.",
                    axis.Name,
                    adjustedValue,
                    offsetMm);
                return true;
            }

            if (candidateValue > maxAllowed)
            {
                adjustedValue = maxAllowed;
                reason = string.Format(
                    CultureInfo.InvariantCulture,
                    "{0}: clamp to positive soft limit {1:F3} with offset {2:F3}.",
                    axis.Name,
                    adjustedValue,
                    offsetMm);
                return true;
            }

            return false;
        }

        private static bool TryGetAxisLimit(IAxis axis, bool isNegativeLimit, out double limitValue)
        {
            limitValue = 0d;
            if (axis == null)
            {
                return false;
            }

            try
            {
                double axisLimit = 0d;
                MotionErrorCode errorCode = isNegativeLimit ? axis.GetSoftMel(ref axisLimit) : axis.GetSoftPel(ref axisLimit);
                if (errorCode != MotionErrorCode.NoError)
                {
                    return false;
                }

                limitValue = axisLimit;
                if (double.IsNaN(limitValue) || double.IsInfinity(limitValue))
                {
                    return false;
                }

                return true;
            }
            catch (Exception)
            {
            }

            return false;
        }

        private static string BuildAdjustmentReason(string adjustedXReason, string adjustedYReason)
        {
            if (string.IsNullOrWhiteSpace(adjustedXReason))
            {
                return adjustedYReason ?? string.Empty;
            }

            if (string.IsNullOrWhiteSpace(adjustedYReason))
            {
                return adjustedXReason;
            }

            return adjustedXReason + " | " + adjustedYReason;
        }
    }
}