// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2022, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.


using System;
using Hiperscan.SGS;
using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.Method;
using Hiperscan.Unix;
using Hiperscan.ApoIdentCore.IO;
using Hiperscan.ApoIdentCore.SectionManager.Substance.SubstanceSelection;
using Hiperscan.ApoIdentCore.Misc;
using Hiperscan.ApoIdentCore.ComplexFinder;
using Hiperscan.ApoIdentCore.Enums;
using Hiperscan.ApoIdentCommon.ApoConfig;
using Hiperscan.ApoIdentCommon.ApoConfig.IO;
using Hiperscan.AnalysisModule;
using Hiperscan.ApoIdentCore.SectionManager.Substance;

namespace Hiperscan.ApoIdentCore.SectionManager.Measuring
{
    public partial class MeasuringManager : ISectionManager
    {
        public event EventHandler FinderChanged = null;
        public event EventHandler RecalibrationTimerUpdated = null;
        public event EventHandler RecalibrationCycleFinished = null;
        public event EventHandler BeforeMeasuringStarted = null;

        public event EventHandler DeviceAdded = null;
        public event EventHandler DeviceRemoved = null;
        

        public MeasuringManager(DeviceList device_list, DeviceSettings settings, ApoIdentFileSystem file_system)
        {
            this.MeasuringSync = new ProcessControl();
            this.ReferenceControl = new ReferencingControl();
            this.InitCombinedFinder(device_list, settings, file_system);
            this.RecalibrationTimer.TimeUpdated += (sender, e) => this.RecalibrationTimerUpdated?.Invoke(sender, e);
            this.RecalibrationTimer.TimerCycleFinished += (sender, e) => this.RecalibrationCycleFinished?.Invoke(sender, e);
        }

        public void AddDeviceListEvents()
        {
            this.RemoveDeviceListEvents();
            this.Finder.DeviceList.DeviceAdded += this.OnDeviceAdded;
            this.Finder.DeviceList.DeviceRemoved += this.OnDeviceRemoved;
            this.Finder.DeviceRemoved += this.DeviceRemoved;
        }

        public void RemoveDeviceListEvents()
        {
            this.Finder.DeviceList.DeviceAdded -= this.OnDeviceAdded;
            this.Finder.DeviceList.DeviceRemoved -= this.OnDeviceRemoved;
            this.Finder.DeviceRemoved -= this.DeviceRemoved;
        }

        private void OnDeviceAdded(DeviceList list, string serial)
        {
            this.DeviceAdded?.Invoke(this, EventArgs.Empty);
        }

        private void OnDeviceRemoved(DeviceList list, string serial, string device_name)
        {
            this.DeviceRemoved?.Invoke(this, EventArgs.Empty);
        }

        private void OnDeviceRemoved(object sender, EventArgs e)
        {
            this.DeviceRemoved?.Invoke(this, EventArgs.Empty);
        }

        private void InitCombinedFinder(DeviceList device_list, DeviceSettings settings, ApoIdentFileSystem file_system)
        {
            this.Finder = new CombinedFinder(device_list,
                                             file_system,
                                             settings);

            this.Finder.FinderButton.MeasurementStarted += this.OnMeasurementStarted;
            this.Finder.WaitForExternalEmptyReference += this.OnWaitForExternalBlackReference;
            this.Finder.WaitForExternalWhiteReference += this.OnWaitForExternalWhiteReference;
            this.Finder.InternalCalibrationStarted += this.OnFinderInternalCalibrationStarted;
            this.Finder.InternalCalibrationFinished += this.OnFinderInternalCalibrationFinished;
            this.Finder.ExternalCalibrationFinished += this.OnExternalCalibrationFinished;
        }

        public void ChangeFinder(bool is_insert_finder)
        {
            this.Finder.IsInsertActive = is_insert_finder;
            this.FinderChanged?.Invoke(this, EventArgs.Empty);
        }

        public void UpdateAfterSubstanceSelection(SubstanceManager substance_manager)
        {
            this.SubstanceCatalog = substance_manager.SubstancesCatalog;
            this.NotMeasurableDate = DateTime.Now;

            if (this.SubstanceCatalog.ModelClass.SubstanceType == SubstanceType.Fluid)
                this.Finder.ResetRecalibrationTimestamp();

            this.Finder.Creator = substance_manager.GetModuleCreatorName(this.SubstanceCatalog.AnalysisType);
            this.Finder.AnalysisType = this.SubstanceCatalog.AnalysisType;

            this.DeactivateRecalibrationTimer();
        }

        public string GetMeasurmentId()
        {
            if (this.IsComplete == false)
                return String.Empty;

            DateTime stamp = this.NotMeasurableDate;
            if (this.IsNirMeasurable)
                stamp = this.ProcessedSpectrum.Timestamp;
           
            return (stamp.Year % 100).ToString() + stamp.ToString("MMdd-HHmmss");
        }

        public void Clear()
        {
            this.Reset();
            this.Finder.Reset();
        }

        public void Reset()
        {
            this.DiscardMeasurement();
            this.SubstanceCatalog = null;
            this.DeactivateRecalibrationTimer();
            this.Finder.IsInsertActive = false;
        }

        public bool IsComplete
        {
            get
            {
                return this.SubstanceCatalog != null &&
                           (this.ProcessedSpectrum != null || this.IsNirMeasurable == false);
            }
        }

        public bool IsReferencing
        {
            get
            {
                return this.ReferenceControl.ReferencingType != ProgramState.None;
            }
        }

        public bool IsNirMeasurable
        {
            get
            {
                return this.SubstanceCatalog != null &&
                           this.SubstanceCatalog.IsSelectedSubstanceNirMeasurable;
            }
        }

        public void DiscardMeasurement()
        {
            this.ProcessedSpectrum = null;
            this.ReferenceControl.Reset();
            if (this.Finder.IsStandardFinderRecalibrationPending)
                this.HasFirstTransflectance = false;
        }

        private Spectrum processed_spectrum = null;
        public Spectrum ProcessedSpectrum
        {
            get
            {
                return this.processed_spectrum;
            }

            private set
            {
                this.processed_spectrum = value;
                if (this.processed_spectrum != null)
                {
                    if (Math.Abs(this.processed_spectrum.Absorbance.YMax()) < 0.02 &&
                        Math.Abs(this.processed_spectrum.Absorbance.YMean()) < 0.01)
                        throw new Exception(Catalog.GetString("Invalid sample spectrum."));
                }
            }
        }

        public bool MeasurceCycleIsRunning { get; set; }
        private ProcessControl MeasuringSync { get; set; }

        private SubstancesCatalog SubstanceCatalog { get; set; }
        public DateTime NotMeasurableDate { get; private set; }

        public bool MeasurementProcessInterrupted { get; set; } = false;
        public CombinedFinder Finder { get; private set; }
    }
}
