// Created with MonoDevelop
//
//    Copyright (C) 2024 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.Spectroscopy;
using Hiperscan.Spectroscopy.Processing.Transformations;

namespace Hiperscan.Spectroscopy.Processing
{
    public abstract partial class SpectrumProcessor 
        : ProcessingReferences
    {
        protected const double DEFAULT_SPECTRAL_RESOLUTION = 10.0;

        private bool recalc_black_reference = false;
        private bool recalc_white_reference = false;
        private bool recalc_sample_insert_white_reference = false;
        private bool recalc_transflectance_insert_reference = false;

        private CorrectionMask correction_mask = CorrectionMask.All & ~CorrectionMask.Adc;
        private ReferenceWheelVersion reference_wheel_version = ReferenceWheelVersion.Auto;
        private WhiteReferenceConversionMode white_reference_conversion_mode = WhiteReferenceConversionMode.None;
        private LimitBandwidthMode raw_spectrum_limit_bandwidth_mode = LimitBandwidthMode.ForcedSwitchOff;
        private LimitBandwidthMode processed_spectrum_limit_bandwith_mode = LimitBandwidthMode.ForcedSwitchOff;

        protected Spectrum black_reference = null;
        protected Spectrum white_reference = null;
        protected Spectrum sample_insert_white_reference = null;
        protected Spectrum transflectance_insert_reference = null;

        protected Spectrum reference_background = null;
        protected Spectrum sample_background = null;
        protected DarkIntensity reference_dark_intensity = null;
        protected DarkIntensity sample_dark_intensity = null;

        protected bool use_default_system_function = true;

        private bool dont_reapply_wavelength_correction = false;
        private int levenberg_marquardt_max_iterations;
        private double levenberg_marquardt_tolerance;
        private Hiperscan.Spectroscopy.Math.LevenbergMarquardt.OptimParams levenberg_marquardt_options = null;

        private Spectrum forced_master_oxid_reference = null;
        private static DataSet factor_tio2_to_zenith = null;
        protected byte reference_wavelength_correction_id = Spectrum.UNKNOWN_WLC_MASTER_SPECTRUM_ID;
        protected byte sample_wavelength_correction_id    = Spectrum.UNKNOWN_WLC_MASTER_SPECTRUM_ID;

        protected SpectrumProcessor(string creator)
            :base(creator)
        {
            this.InstrumentReferenceSystem = InstrumentReferenceSystem.Unknown;
            this.MaxIterationReachedBehavior = WlcMaxIterationReachedBehavior.ThrowException;

            this.use_default_system_function = true;
            this.dont_reapply_wavelength_correction = false;

            this.levenberg_marquardt_max_iterations = 400;
            this.levenberg_marquardt_tolerance = 1e-8;
            this.UpdateLevenbergMarquardtOptions();
        }

        public abstract Spectrum Process(Spectrum sample_spectrum);

        public ProcessingResult GetProcessingResult(Spectrum sample_spectrum)
        {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            Spectrum processed = this.Process(sample_spectrum);
            sw.Stop();
            return new ProcessingResult(this, processed, sw.Elapsed);
        }

        protected abstract void UpdateBlackReference();
        protected abstract void UpdateWhiteReference();
        protected abstract void UpdateSampleInsertWhiteReference();
        protected abstract void UpdateTransflactenceInsertReference();
        protected abstract void UpdateOxidOfReference();
        protected abstract void UpdateOxidOfSample();

        protected abstract double IntensityLimit { get; }
    }
}
