using MainShell.Common;
using MainShell.Common.Display.ViewModel;
using MainShell.Common.VisionTemple.ViewModel;
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Models;
using MainShell.Motion;
using MainShell.PageCalib.OriginCalib.Model;
using MainShell.PageCalib.OriginCalib.Service;
using MaxwellFramework.Core.Interfaces;
using MwFramework.Device;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;

namespace MainShell.PageCalib.OriginCalib.ViewModel
{
    public class OriginCalibViewModel : BaseScreen, IPage
    {
        public string Name { get; set; } = "OriginCalib";

        private readonly OriginCalibSetting _setting;
        private readonly HardwareManager _hardware;
        private readonly OriginCalibrationMotionService _originCalibrationMotionService;
        private readonly ApproachAlignmentService _approachAlignmentService;
        private readonly VisionTempleWindowViewModel _visionTempleWindowViewModel;
        private readonly IEventAggregator _eventAggregator;
        private bool _isInitialized;

        private const string DialogCaption = "原点标定";

        public ObservableCollection<OriginCalibModuleViewModel> Modules { get; } = new ObservableCollection<OriginCalibModuleViewModel>();

        private CameraAxisViewModel _cameraAxisViewModel;
        public CameraAxisViewModel CameraAxisViewModel
        {
            get { return _cameraAxisViewModel; }
            private set { SetAndNotify(ref _cameraAxisViewModel, value); }
        }

        public OriginCalibViewModel(
            HardwareManager hardware,
            OriginCalibrationMotionService originCalibrationMotionService,
            ApproachAlignmentService approachAlignmentService,
            VisionTempleWindowViewModel visionTempleWindowViewModel,
            IEventAggregator eventAggregator)
        {
            _hardware = hardware ?? throw new ArgumentNullException(nameof(hardware));
            _originCalibrationMotionService = originCalibrationMotionService ?? throw new ArgumentNullException(nameof(originCalibrationMotionService));
            _approachAlignmentService = approachAlignmentService ?? throw new ArgumentNullException(nameof(approachAlignmentService));
            _visionTempleWindowViewModel = visionTempleWindowViewModel ?? throw new ArgumentNullException(nameof(visionTempleWindowViewModel));
            _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
            _setting = OriginCalibSetting.LoadOrCreate();
        }

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

            if (_isInitialized)
            {
                return;
            }

            CameraAxisViewModel = IoC.Get<CameraAxisViewModel>();
            CameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = _hardware.CameraAxisManager.TopPositionAndWideCameraAxisDevices;

            InitializePostProcessors();
            RebuildModuleViewModels();
            _isInitialized = true;
        }

        private void InitializePostProcessors()
        {
            foreach (OriginCalibModuleItem module in _setting.Modules)
            {
                if (module == null)
                {
                    continue;
                }

                module.PostProcessor = CreateDefaultPostProcessor(module);
            }
        }

        private ICalibrationPostProcessor CreateDefaultPostProcessor(OriginCalibModuleItem module)
        {
            if (module == null)
            {
                return null;
            }

            if (string.IsNullOrWhiteSpace(module.TemplatePath) || module.CalibrationAxes == null)
            {
                return null;
            }

            List<ApproachAlignmentAxis> alignmentAxes = module.CalibrationAxes
                .Where(item => item != null)
                .Select(item => item.AxisName)
                .Where(axisName => !string.IsNullOrWhiteSpace(axisName))
                .Select(axisName => new ApproachAlignmentAxis(axisName, 0.005))
                .ToList();

            if (alignmentAxes.Count == 0)
            {
                return null;
            }

            return new SingleAlignmentPostProcessor(
                _approachAlignmentService,
                alignmentAxes,
                CameraType.TopPositionCamera);
        }


        private void RebuildModuleViewModels()
        {
            Modules.Clear();
            for (int i = 0; i < _setting.Modules.Count; i++)
            {
                OriginCalibModuleViewModel moduleVm = new OriginCalibModuleViewModel(
                    _setting.Modules[i],
                    _hardware,
                    _originCalibrationMotionService,
                    _visionTempleWindowViewModel);
                moduleVm.Index = i + 1;
                Modules.Add(moduleVm);
            }
        }

        public void SaveConfig()
        {
            try
            {
                _setting.Write();
                _eventAggregator.Publish(CreateOriginCalibDataChangedEventArgs());
                LocalizedMessageBox.Show(MessageKey.OriginCalibConfigSaved, MessageKey.TitleInfo, MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                LocalizedMessageBox.ShowFormat(MessageKey.OriginCalibConfigSaveFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message);
            }
        }

        private OriginCalibDataChangedEventArgs CreateOriginCalibDataChangedEventArgs()
        {
            List<OriginCalibAxisOffsetChangedItem> axisOffsets = new List<OriginCalibAxisOffsetChangedItem>();

            foreach (OriginCalibModuleItem module in _setting.Modules)
            {
                if (module == null || module.CalibrationAxes == null)
                {
                    continue;
                }

                foreach (CalibrationAxisItem calibrationAxis in module.CalibrationAxes)
                {
                    if (calibrationAxis == null || string.IsNullOrWhiteSpace(calibrationAxis.AxisName))
                    {
                        continue;
                    }

                    axisOffsets.Add(new OriginCalibAxisOffsetChangedItem(calibrationAxis.AxisName, calibrationAxis.Offset));
                }
            }

            return new OriginCalibDataChangedEventArgs(axisOffsets);
        }

        public void ResetAll()
        {
            foreach (OriginCalibModuleViewModel module in Modules)
            {
                module.ResetModule();
            }
        }
    }
}