using MainShell.Common;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Motion;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using MwFramework.ManagerService;
using Stylet;
using StyletIoC;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using MW.WorkFlow;

namespace MainShell.Manual.ViewModel
{
    public class DieRecheckDisplayItem : PropertyChangedBase
    {
        private int _stepIndex;
        public int StepIndex
        {
            get { return _stepIndex; }
            set { SetAndNotify(ref _stepIndex, value); }
        }

        private int _padRow;
        public int PadRow
        {
            get { return _padRow; }
            set { SetAndNotify(ref _padRow, value); }
        }

        private int _padColumn;
        public int PadColumn
        {
            get { return _padColumn; }
            set { SetAndNotify(ref _padColumn, value); }
        }

        private int _dieRow;
        public int DieRow
        {
            get { return _dieRow; }
            set { SetAndNotify(ref _dieRow, value); }
        }

        private int _dieColumn;
        public int DieColumn
        {
            get { return _dieColumn; }
            set { SetAndNotify(ref _dieColumn, value); }
        }

        private double _padX;
        public double PadX
        {
            get { return _padX; }
            set { SetAndNotify(ref _padX, value); }
        }

        private double _padY;
        public double PadY
        {
            get { return _padY; }
            set { SetAndNotify(ref _padY, value); }
        }

        private double _dieX;
        public double DieX
        {
            get { return _dieX; }
            set { SetAndNotify(ref _dieX, value); }
        }

        private double _dieY;
        public double DieY
        {
            get { return _dieY; }
            set { SetAndNotify(ref _dieY, value); }
        }

        private string _transPathType;
        public string TransPathType
        {
            get { return _transPathType; }
            set { SetAndNotify(ref _transPathType, value); }
        }

        private bool _isMissingBond;
        public bool IsMissingBond
        {
            get { return _isMissingBond; }
            set { SetAndNotify(ref _isMissingBond, value); }
        }

        private double _xError;
        public double XError
        {
            get { return _xError; }
            set { SetAndNotify(ref _xError, value); }
        }

        private double _yError;
        public double YError
        {
            get { return _yError; }
            set { SetAndNotify(ref _yError, value); }
        }

        private bool _isXThresholdExceeded;
        public bool IsXThresholdExceeded
        {
            get { return _isXThresholdExceeded; }
            set { SetAndNotify(ref _isXThresholdExceeded, value); }
        }

        private bool _isYThresholdExceeded;
        public bool IsYThresholdExceeded
        {
            get { return _isYThresholdExceeded; }
            set { SetAndNotify(ref _isYThresholdExceeded, value); }
        }

        private string _statusSummary;
        public string StatusSummary
        {
            get { return _statusSummary; }
            set { SetAndNotify(ref _statusSummary, value); }
        }
    }

    public class DieRecheckFilterState : PropertyChangedBase
    {
        private enum DieRecheckFilterRule
        {
            None = 0,
            MissingBondOnly,
            XThresholdExceededOnly,
            YThresholdExceededOnly,
            XYThresholdExceededOnly
        }

        private DieRecheckFilterRule _selectedRule;
        private bool _isUpdatingSelection;

        public bool IsMissingBondRuleSelected
        {
            get { return _selectedRule == DieRecheckFilterRule.MissingBondOnly; }
            set { SetSelectedRule(value, DieRecheckFilterRule.MissingBondOnly, nameof(IsMissingBondRuleSelected)); }
        }

        public bool IsXThresholdRuleSelected
        {
            get { return _selectedRule == DieRecheckFilterRule.XThresholdExceededOnly; }
            set { SetSelectedRule(value, DieRecheckFilterRule.XThresholdExceededOnly, nameof(IsXThresholdRuleSelected)); }
        }

        public bool IsYThresholdRuleSelected
        {
            get { return _selectedRule == DieRecheckFilterRule.YThresholdExceededOnly; }
            set { SetSelectedRule(value, DieRecheckFilterRule.YThresholdExceededOnly, nameof(IsYThresholdRuleSelected)); }
        }

        public bool IsXYThresholdRuleSelected
        {
            get { return _selectedRule == DieRecheckFilterRule.XYThresholdExceededOnly; }
            set { SetSelectedRule(value, DieRecheckFilterRule.XYThresholdExceededOnly, nameof(IsXYThresholdRuleSelected)); }
        }

        private double _xThreshold = 0.01d;
        public double XThreshold
        {
            get { return _xThreshold; }
            set { SetAndNotify(ref _xThreshold, value); }
        }

        private double _yThreshold = 0.01d;
        public double YThreshold
        {
            get { return _yThreshold; }
            set { SetAndNotify(ref _yThreshold, value); }
        }

        public void ClearSelectedRule()
        {
            UpdateSelectedRule(DieRecheckFilterRule.None, null);
        }

        private void SetSelectedRule(bool isSelected, DieRecheckFilterRule targetRule, string propertyName)
        {
            if (_isUpdatingSelection)
            {
                return;
            }

            DieRecheckFilterRule newRule = isSelected ? targetRule : DieRecheckFilterRule.None;
            UpdateSelectedRule(newRule, propertyName);
        }

        private void UpdateSelectedRule(DieRecheckFilterRule newRule, string propertyName)
        {
            if (_selectedRule == newRule)
            {
                if (!string.IsNullOrWhiteSpace(propertyName))
                {
                    OnPropertyChanged(propertyName);
                }

                return;
            }

            _selectedRule = newRule;
            _isUpdatingSelection = true;
            try
            {
                OnPropertyChanged(nameof(IsMissingBondRuleSelected));
                OnPropertyChanged(nameof(IsXThresholdRuleSelected));
                OnPropertyChanged(nameof(IsYThresholdRuleSelected));
                OnPropertyChanged(nameof(IsXYThresholdRuleSelected));
            }
            finally
            {
                _isUpdatingSelection = false;
            }
        }
    }

    public class DieRecheckViewModel : OperateViewModelBase
    {
        private readonly IEventAggregator _eventAggregator;
        private readonly RecipeManager _recipeManager;
        private readonly ProcessResultManager _processResultManager;
        private readonly DieRecheckService _dieRecheckService;
        private readonly SafeAxisMotion _safeAxisMotion;

        private ObservableCollection<DieRecheckDisplayItem> _allPointResults = new ObservableCollection<DieRecheckDisplayItem>();
        private List<DieRecheckDisplayItem> _filteredPointResults = new List<DieRecheckDisplayItem>();

        private ObservableCollection<DieRecheckDisplayItem> _pointResults = new ObservableCollection<DieRecheckDisplayItem>();
        public ObservableCollection<DieRecheckDisplayItem> PointResults
        {
            get { return _pointResults; }
            set { SetAndNotify(ref _pointResults, value); }
        }

        public PaginationViewModel Pagination { get; } = new PaginationViewModel();

        private RegionModel _recheckRegion = new RegionModel();
        public RegionModel RecheckRegion
        {
            get { return _recheckRegion; }
            set { SetAndNotify(ref _recheckRegion, value); }
        }

        private DieRecheckFilterState _filterState = new DieRecheckFilterState();
        public DieRecheckFilterState FilterState
        {
            get { return _filterState; }
            set { SetAndNotify(ref _filterState, value); }
        }

        private int _pointCount;
        public int PointCount
        {
            get { return _pointCount; }
            set { SetAndNotify(ref _pointCount, value); }
        }

        private int _filteredPointCount;
        public int FilteredPointCount
        {
            get { return _filteredPointCount; }
            set { SetAndNotify(ref _filteredPointCount, value); }
        }

        private DieRecheckDisplayItem _selectedPointResult;
        public DieRecheckDisplayItem SelectedPointResult
        {
            get { return _selectedPointResult; }
            set { SetAndNotify(ref _selectedPointResult, value); }
        }

        public DieRecheckViewModel(
            IEventAggregator eventAggregator,
            RecipeManager recipeManager,
            ProcessResultManager processResultManager,
            HardwareManager hardwareManager,
            DieRecheckService dieRecheckService,
            SafeAxisMotion safeAxisMotion)
        {
            _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
            _recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
            _processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
            if (hardwareManager == null)
            {
                throw new ArgumentNullException(nameof(hardwareManager));
            }
            _dieRecheckService = dieRecheckService ?? throw new ArgumentNullException(nameof(dieRecheckService));
            _safeAxisMotion = safeAxisMotion ?? throw new ArgumentNullException(nameof(safeAxisMotion));

            _cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
            _cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;

            FilterState.PropertyChanged += FilterState_PropertyChanged;
            RecheckRegion.PropertyChanged += RecheckRegion_PropertyChanged;
            Pagination.PageChanged += Pagination_PageChanged;

            LoadCurrentPoints();
        }

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

        public async Task StartProcess()
        {
            WorkflowContext context = new WorkflowContext();
            context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
            context[WorkflowContextKeys.RecipeManager] = _recipeManager;
            context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
            context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.DieRecheckFlow;

            WorkflowRunCompletedEventArgs result = await RunManualActivityAsync(
                new DieRecheckActivity(ProcessFlowName.DieRecheckFlow, _dieRecheckService),
                context,
                false);

            if (!IsWorkflowCompleted(result))
            {
                return;
            }

            LoadCurrentPoints();
        }

        public async Task MoveToSelectedPoint()
        {
            if (SelectedPointResult == null)
            {
                LocalizedMessageBox.Show(MessageKey.DieRecheckSelectedPointRequired, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            try
            {
                HardwareDevice selectedDevice = _cameraAxisViewModel.CameraAxisDevices != null
                    ? _cameraAxisViewModel.CameraAxisDevices.SelectedHardwareDevice
                    : null;
                if (selectedDevice == null || selectedDevice.AxisX == null || selectedDevice.AxisY == null)
                {
                    LocalizedMessageBox.Show(MessageKey.DieRecheckMoveDeviceUnavailable, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }

                string moveMessage = $"DieRecheck move to point. StepIndex={SelectedPointResult.StepIndex}, PadX={SelectedPointResult.PadX:F4}, PadY={SelectedPointResult.PadY:F4}";
                moveMessage.LogProcessInfo();

                await Task.Run(() =>
                    _safeAxisMotion.SafeMove(
                        MotionMoveRequest.ForAxis(selectedDevice.AxisX, SelectedPointResult.PadX, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisX" }),
                        MotionMoveRequest.ForAxis(selectedDevice.AxisY, SelectedPointResult.PadY, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisY" })));
            }
            catch (Exception exception)
            {
                LogManager.LogSysError(exception);
                MwMessageBox.Show(exception.Message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        public void ResetFilters()
        {
            FilterState.ClearSelectedRule();
            FilterState.XThreshold = 0.01d;
            FilterState.YThreshold = 0.01d;
            ApplyFilters();
        }

        private void FilterState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            UpdateThresholdFlags();
            Pagination.CurrentPage = 1;
            ApplyFilters();
        }

        private void RecheckRegion_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            Pagination.CurrentPage = 1;
            ApplyFilters();
        }

        private void Pagination_PageChanged(object sender, EventArgs e)
        {
            LoadPagedData();
        }

        private void LoadCurrentPoints()
        {
            _allPointResults.Clear();
            _filteredPointResults.Clear();
            PointResults.Clear();

            DieRecheckProcessResult processResult = _processResultManager.DieRecheckResult;
            if (processResult == null || processResult.PointResults == null)
            {
                PointCount = 0;
                FilteredPointCount = 0;
                Pagination.TotalItems = 0;
                return;
            }

            foreach (DieRecheckPointResult pointResult in processResult.PointResults)
            {
                _allPointResults.Add(new DieRecheckDisplayItem
                {
                    StepIndex = pointResult.StepIndex,
                    PadRow = pointResult.PadRow,
                    PadColumn = pointResult.PadColumn,
                    DieRow = pointResult.DieRow,
                    DieColumn = pointResult.DieColumn,
                    PadX = pointResult.PadX,
                    PadY = pointResult.PadY,
                    DieX = pointResult.DieX,
                    DieY = pointResult.DieY,
                    TransPathType = pointResult.TransPathType,
                    IsMissingBond = pointResult.IsMissingBond,
                    XError = pointResult.XError,
                    YError = pointResult.YError
                });
            }

            PointCount = _allPointResults.Count;
            InitializeRegion();
            UpdateThresholdFlags();
            Pagination.CurrentPage = 1;
            ApplyFilters();
        }

        private void InitializeRegion()
        {
            if (_allPointResults.Count == 0)
            {
                RecheckRegion.StartRow = 1;
                RecheckRegion.StartCol = 1;
                RecheckRegion.EndRow = 1;
                RecheckRegion.EndCol = 1;
                return;
            }

            RecheckRegion.StartRow = _allPointResults.Min(item => item.PadRow);
            RecheckRegion.StartCol = _allPointResults.Min(item => item.PadColumn);
            RecheckRegion.EndRow = Math.Max(RecheckRegion.StartRow, _allPointResults.Max(item => item.PadRow));
            RecheckRegion.EndCol = Math.Max(RecheckRegion.StartCol, _allPointResults.Max(item => item.PadColumn));
        }

        private void UpdateThresholdFlags()
        {
            foreach (DieRecheckDisplayItem item in _allPointResults)
            {
                item.IsXThresholdExceeded = Math.Abs(item.XError) >= FilterState.XThreshold;
                item.IsYThresholdExceeded = Math.Abs(item.YError) >= FilterState.YThreshold;
                item.StatusSummary = BuildStatusSummary(item);
            }
        }

        private string BuildStatusSummary(DieRecheckDisplayItem item)
        {
            Collection<string> tags = new Collection<string>();
            if (item.IsMissingBond)
            {
                tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusMissingBond));
            }

            if (item.IsXThresholdExceeded)
            {
                tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusXExceeded, item.XError));
            }

            if (item.IsYThresholdExceeded)
            {
                tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusYExceeded, item.YError));
            }

            if (tags.Count == 0)
            {
                tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusOk));
            }

            return string.Join(" / ", tags);
        }

        private void ApplyFilters()
        {
            _filteredPointResults = _allPointResults.Where(MatchesFilters).ToList();
            FilteredPointCount = _filteredPointResults.Count;
            Pagination.TotalItems = FilteredPointCount;

            if (Pagination.TotalItems == 0)
            {
                SelectedPointResult = null;
                PointResults = new ObservableCollection<DieRecheckDisplayItem>();
                return;
            }

            if (Pagination.CurrentPage > Pagination.TotalPages)
            {
                Pagination.CurrentPage = Pagination.TotalPages;
                return;
            }

            LoadPagedData();
        }

        private void LoadPagedData()
        {
            if (_filteredPointResults == null || _filteredPointResults.Count == 0)
            {
                PointResults = new ObservableCollection<DieRecheckDisplayItem>();
                SelectedPointResult = null;
                return;
            }

            int safePage = Math.Max(1, Math.Min(Pagination.CurrentPage, Pagination.TotalPages));
            int offset = (safePage - 1) * Pagination.PageSize;
            int limit = Pagination.PageSize;

            PointResults = new ObservableCollection<DieRecheckDisplayItem>(_filteredPointResults.Skip(offset).Take(limit));
            if (SelectedPointResult != null && !PointResults.Contains(SelectedPointResult))
            {
                SelectedPointResult = null;
            }
        }

        private bool MatchesFilters(DieRecheckDisplayItem item)
        {
            if (item == null)
            {
                return false;
            }

            bool isInRegion = item.PadRow >= RecheckRegion.StartRow
                && item.PadRow <= RecheckRegion.EndRow
                && item.PadColumn >= RecheckRegion.StartCol
                && item.PadColumn <= RecheckRegion.EndCol;
            if (!isInRegion)
            {
                return false;
            }

            if (FilterState.IsMissingBondRuleSelected && !item.IsMissingBond)
            {
                return false;
            }

            if (FilterState.IsXYThresholdRuleSelected && !(item.IsXThresholdExceeded && item.IsYThresholdExceeded))
            {
                return false;
            }

            if (FilterState.IsXThresholdRuleSelected && !item.IsXThresholdExceeded)
            {
                return false;
            }

            if (FilterState.IsYThresholdRuleSelected && !item.IsYThresholdExceeded)
            {
                return false;
            }

            return true;
        }
    }
}
