// Created with MonoDevelop
//
//    Copyright (C) 2018 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 System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;

using Hiperscan.Spectroscopy.IO;
using Hiperscan.Spectroscopy;
using Hiperscan.Unix;


namespace Hiperscan.Spectroscopy.Processing.Transformations
{
    public static class FinderSDApoIdentTransformer
    {
        private static void LoadTransformationFunction()
        {
            if (FinderSDApoIdentTransformer.TransformationFunction != null)
                return;

            PlainCSV.Separator = ',';
            Dictionary<int, List<string>> plain_csv = null;

            using(Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Spectroscopy.Transformations.FinderSD.transform_finder_sd_apo_ident.csv"))
            {
                plain_csv = PlainCSV.Read(stream);
                CultureInfo culture = new CultureInfo("en-US");

                List<double> reflections_finder_sd = new List<double>();
                List<double> reflections_apo_ident = new List<double>();

                foreach (int line in plain_csv.Keys)
                {
                    if (line == 1)
                        continue;
                        
                    double reflection_finder_sd = double.Parse(plain_csv[line][0], culture);
                    reflections_finder_sd.Add(reflection_finder_sd);

                    double reflection_apo_ident = double.Parse(plain_csv[line][1], culture); 
                    reflections_apo_ident.Add(reflection_apo_ident);
                }

                FinderSDApoIdentTransformer.TransformationFunction = new DataSet(reflections_finder_sd, reflections_apo_ident);
            }
        }

        private static double GetAverageReflectances(Spectrum spechtrum, int index, int range)
        {
            List<double> values = new List<double>();

            for (int i = index - 1; i >= 0 && i >= index - range; i--)
            {
                values.Add(spechtrum.Reflectance.Y[i]);
            }

            for (int i = index + 1; i < spechtrum.Reflectance.Count && i <= index + range; i++)
            {
                values.Add(spechtrum.Reflectance.Y[i]);
            }

            values.Add(spechtrum.Reflectance.Y[index]);

            double sum = 0;
            foreach (double value in values)
            {
                sum += value;
            }

            return sum / values.Count;
        }

        private static double GetApoCorrectionFactor(double finder_reflection)
        {
            int index = 0;
            foreach (double x in TransformationFunction.X)
            {
                if (finder_reflection <= x)
                    break;

                if (index < TransformationFunction.X.Count - 1)
                    index++;
            }

            if (index == 0)
                index = 1;

            double x2 = FinderSDApoIdentTransformer.TransformationFunction.X[index];
            double x1 = FinderSDApoIdentTransformer.TransformationFunction.X[index - 1];
            double y2 = FinderSDApoIdentTransformer.TransformationFunction.Y[index];
            double y1 = FinderSDApoIdentTransformer.TransformationFunction.Y[index - 1];

            double m  = (y2 - y1) / (x2 - x1);
            double y0 = y2 - m * x2;

            return (m * finder_reflection + y0) / finder_reflection;
        }

        public static void ApplyTransformation(Spectrum spectrum)
        {
            if (spectrum.SpectrumType != SpectrumType.ProcessedFluidSpecimen && spectrum.SpectrumType != SpectrumType.ProcessedSolidSpecimen)
                throw new ArgumentException(Catalog.GetString("Spectrum must be a processed spectrum"));

            if (spectrum.ProbeType != ProbeType.FinderSDInsert && spectrum.ProbeType != ProbeType.FinderSDStandard)
                throw new ArgumentException(Catalog.GetString("Spectrum must be a Finder SD spectrum"));

            if (spectrum.ProbeType == ProbeType.FinderSDInsert)
                return;

            if (spectrum.SpectrumType == SpectrumType.ProcessedFluidSpecimen)
                return;

            FinderSDApoIdentTransformer.LoadTransformationFunction();

            DataSet corrected_intensities_0 = new DataSet(spectrum.Reflectance.X, 0);

            for (int i = 0; i < spectrum.Reflectance.X.Count; i++)
            {
                double average_reflectance_finder_sd = FinderSDApoIdentTransformer.GetAverageReflectances(spectrum, i, 4);
                double correction_factor = FinderSDApoIdentTransformer.GetApoCorrectionFactor(average_reflectance_finder_sd);
                double intensity_0 = spectrum.Intensity.Y[i] / average_reflectance_finder_sd / correction_factor;
                corrected_intensities_0.Y[i] = intensity_0;
            }

            spectrum.Reference = new Spectrum(corrected_intensities_0, spectrum.Reference.WavelengthLimits);
        }

        private static DataSet TransformationFunction { get; set; }
    }
}