// Created with MonoDevelop
//
//    Copyright (C) 2011 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.Unix;

namespace Hiperscan.SGS
{
    public static class TransflectanceInsert
    {
        private const double REFLECTANCE_MIN = 0.4;
        private const double REFLECTANCE_MAX = 1.0;
        private const double RMS_1_MAX = 0.005;
        private const double RMS_4_MAX = 0.00653;
        private const double SLOPE_MAX = 3.33E-5;

        public static bool Check(Spectrum transflectance_reference_spectrum, Spectrum ext_white_spectrum, DarkIntensity dark_intensity)
        {
            TransflectanceInsert.ExceptionMessage = String.Empty;

            ext_white_spectrum = ext_white_spectrum.Clone();
            transflectance_reference_spectrum = transflectance_reference_spectrum.Clone();

            ext_white_spectrum.DarkIntensity = dark_intensity;
            transflectance_reference_spectrum.DarkIntensity = dark_intensity;

            DataSet transflectance = new DataSet(transflectance_reference_spectrum.RawIntensity);
            for (int i = 0; i < transflectance.Count; i++)
            {
                transflectance.Y[i] -= transflectance_reference_spectrum.DarkIntensity.Intensity;
            }

            DataSet ext_white = new DataSet(ext_white_spectrum.RawIntensity);
            for (int i = 0; i < ext_white.Count; i++)
            {
                ext_white.Y[i] -= ext_white_spectrum.DarkIntensity.Intensity;
            }

            DataSet reflectance = transflectance / ext_white;

            if (TransflectanceInsert.CheckReflectance(reflectance) == false)
                return false;
            if (TransflectanceInsert.CheckSqrtAndSlope(reflectance) == false)
                return false;
            if (TransflectanceInsert.CheckRMS_4(reflectance) == false)
                return false;

            return true;
        }


        // Criterion 1 - Feature #26056
        private static bool CheckReflectance(DataSet transflectance_reflectance)
        {
            double mean = TransflectanceInsert.GetReflectanceRange(transflectance_reflectance, 1050, 1850).YMean();

            Console.WriteLine("Check transflectance criterion 1 -> mean reflectance: {0}", mean);


            if (mean <= TransflectanceInsert.REFLECTANCE_MIN)
            {
                TransflectanceInsert.ExceptionMessage = Catalog.GetString("The Black Reference and Transflectance Reference may have been mixed up.");
                return false;
            }

            if (mean >= TransflectanceInsert.REFLECTANCE_MAX)
            {
                TransflectanceInsert.ExceptionMessage = Catalog.GetString("The White Reference and Transflectance Reference may have been mixed up.");
                return false;
            }

            return true;
        }

        // Criterion 2 - Feature #26056
        private static bool CheckSqrtAndSlope(DataSet transflection_reflection)
        {
            DataSet range_1 = TransflectanceInsert.GetReflectanceRange(transflection_reflection, 1100, 1150);
            DataSet range_2 = TransflectanceInsert.GetReflectanceRange(transflection_reflection, 1251, 1800);

            range_1.AddRange(range_2);
            DataSet corr_reflections = range_1;

            DataSet.LinearRegressionResult result = corr_reflections.LinearRegression();
            DataSet regression = corr_reflections.LinearRegressionCurve(result);
            double abs_slope = Math.Abs(result.Slope);
            double rms = TransflectanceInsert.GetRMS(corr_reflections, regression);

            Console.WriteLine("Check transflectance criterion 2a -> rms: {0}", rms);
            Console.WriteLine("Check transflectance criterion 2b -> slope: {0}", abs_slope);

            if (rms < TransflectanceInsert.RMS_1_MAX && abs_slope <= TransflectanceInsert.SLOPE_MAX)
            {
                TransflectanceInsert.ExceptionMessage = Catalog.GetString("The White Reference may have been incorrectly applied during measurement.");
                return false;
            }

            return true;
        }

        // Criterion 3 - Feature #26056
        private static bool CheckRMS_4(DataSet transflection_reflection)
        {
            DataSet range_1 = TransflectanceInsert.GetReflectanceRange(transflection_reflection, 1100, 1150);
            DataSet range_2 = TransflectanceInsert.GetReflectanceRange(transflection_reflection, 1251, 1800);

            range_1.AddRange(range_2);
            DataSet corr_reflections = range_1;

            double[] p = MathNet.Numerics.Fit.Polynomial(corr_reflections.X.ToArray(), corr_reflections.Y.ToArray(), 4);
            DataSet regression = new DataSet(corr_reflections.X, 0);

            for (int i = 0; i < p.Length; i++)
            {
                for (int j = 0; j < regression.Count; j++)
                {
                    regression.Y[j] += Math.Pow(regression.X[j], i) * p[i];
                }
            }

            double rms = TransflectanceInsert.GetRMS(corr_reflections, regression);

            Console.WriteLine("Check transflectance criterion 3 -> rms: {0}", rms);

            if (rms >= TransflectanceInsert.RMS_4_MAX)
            {
                TransflectanceInsert.ExceptionMessage = Catalog.GetString("A Sample may have been incorrectly applied during measurement.");
                return false;
            }

            return true;
        }

        private static DataSet GetReflectanceRange(DataSet tranflection, double start_nm, double end_nm)
        {
            int start_index = 0;
            int end_index = 0;

            double tolerance = 0.00001;

            foreach (double x in tranflection.X)
            {
                if (x > start_nm - tolerance)
                    break;

                start_index++;
            }

            foreach (double x in tranflection.X)
            {
                if (x > end_nm - tolerance)
                    break;

                end_index++;
            }

            int range = end_index - start_index + 1;

            return tranflection.GetRange(start_index, range);
        }

        private static double GetRMS(DataSet data1, DataSet data2)
        {
            DataSet diff = data1 - data2;

            double sum = 0;
            foreach (double y in diff.Y)
                sum += y * y;

            return Math.Sqrt(sum / diff.Count);
        }

        public static string ExceptionMessage { get; private set; }
    }
}
