
// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    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 System.IO;
using System.Xml;
using System.Linq;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Runtime.Serialization;

using Hiperscan.Unix;
using Hiperscan.PdfLib;
using Hiperscan.Spectroscopy.Processing;
using Hiperscan.Extensions.ReferenceCheck.Control.Acquisition;

namespace Hiperscan.Extensions.ReferenceCheck.Control.Evaluation
{
    [Serializable]
    [DataContract]
    public partial class ResultData
    {
        [XmlIgnore]
        [NonSerialized]
        private const string FILE_NAME = "result.xml";
        [XmlIgnore]
        [NonSerialized]
        internal static readonly string PASS = Catalog.GetString("OK");
        [XmlIgnore]
        [NonSerialized]
        internal static readonly string FAIL = Catalog.GetString("X");

        [DataMember]
        public static string EvaluatorName { get; set; }
        [DataMember]
        public string ReportNumber { get; set; }
        [DataMember]
        public string DeviceName { get; set; }
        [DataMember]
        public string DeviceSerialNumber { get; set; }
        [DataMember]
        public string SGSVersion { get; set; }
        [DataMember]
        public string SGSSerialNumber { get; set; }
        [DataMember]
        public string DisplayName { get; set; }
        [DataMember]
        public string FirmLogo { get; set; }
        [DataMember]
        public DateTime CheckDate { get; set; }
        [DataMember]
        public string Creator { get; set; }
        [DataMember]
        public string ProcessorTypeName { get; set; }
        [DataMember]
        public bool ReferenceWheelDegraded { get; set; }
        [DataMember]
        public List<int> SessionIDs { get; set; }
        [DataMember]
        public Hiperscan.SGS.Benchtop.ReferenceWheelVersion ReferenceWheelVersion { get; set; }

        [DataMember]
        public bool EvaluationSuccess { get; set; }

        [DataMember]
        public List<BlackSessionResults> BlackSessionResults { get; set; }
        [DataMember]
        public List<WhiteSessionResults> WhiteSessionResults { get; set; }
        [DataMember]
        public List<WhitePESessionResults> WhitePESessionResults{ get; set; }
        [DataMember]
        public List<TransflectanceSessionResults> TransflectanceSessionResults { get; set; }

        [DataMember]
        internal List<CalibrationSession> CalibrationSessions { get; set; }

        [XmlIgnore]
        private List<AcquisitionSession> SessionsBlack { get; set; }
        [XmlIgnore]
        private List<AcquisitionSession> SessionsWhite { get; set; }
        [XmlIgnore]
        private List<AcquisitionSession> SessionsEmpty { get; set; }
        [XmlIgnore]
        private List<AcquisitionSession> SessionsWhiteInsert { get; set; }
        [XmlIgnore]
        private List<AcquisitionSession> SessionsTransflectance { get; set; }

        [XmlIgnore]
        public int BlackCount => BlackSessionResults.SelectMany(x => x.BlackResults).Count();
        [XmlIgnore]
        public int WhiteCount => WhiteSessionResults.SelectMany(x => x.WhiteResults).Count();
        [XmlIgnore]
        public int WhitePECount => WhitePESessionResults.SelectMany(x => x.WhitePEResults).Count();
        [XmlIgnore]
        public int TransflectanceCount => TransflectanceSessionResults.SelectMany(x => x.TransflectanceResults).Count();

        [XmlIgnore]
        public SpectrumProcessor Processor 
        {
            get
            {
                if (this.ProcessorTypeName == typeof(ReferenceBlockProcessor).Name)
                    return new ReferenceBlockProcessor(string.Empty);
                else if (this.ProcessorTypeName == typeof(ReferenceWheelProcessor).Name)
                    return new ReferenceWheelProcessor(string.Empty);
                else
                    return null;
            }
        }

        public ResultData()
        {
            this.EvaluationSuccess = false;
            this.FirmLogo = string.Empty;
        }

        public void SetWheelVersion(Hiperscan.SGS.Benchtop.ReferenceWheelVersion wheel_version)
        {
            this.ReferenceWheelVersion = wheel_version;

            switch (this.ReferenceWheelVersion)
            {
                case SGS.Benchtop.ReferenceWheelVersion.BariumSulfate:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxide:
                case SGS.Benchtop.ReferenceWheelVersion.ZenithApoIdent1:
                case SGS.Benchtop.ReferenceWheelVersion.ZenithApoIdent2:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideOEM:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithApoIdent:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithApoIdent2:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithApoIdentES20:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithOEM:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithOEMSD:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithOEMSD2:
                case SGS.Benchtop.ReferenceWheelVersion.Auto:
                case SGS.Benchtop.ReferenceWheelVersion.ZenithOEM1:
                case SGS.Benchtop.ReferenceWheelVersion.ZenithOEM2:
                    this.ProcessorTypeName = typeof(ReferenceWheelProcessor).Name;
                    break;

                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithFinder2:
                case SGS.Benchtop.ReferenceWheelVersion.TitaniumDioxideZenithFinderX:
                    this.ProcessorTypeName = typeof(ReferenceBlockProcessor).Name;
                    break;

                case SGS.Benchtop.ReferenceWheelVersion.Unknown:
                default:
                    throw new Exception(Catalog.GetString("Unknown ReferenceWheelVersion"));
            }
        }

        private ReferenceWheelProcessor NewReferenceWheelProcessorWithCalibrationSpectras(int session_id, ReferenceWheelVersion reference_wheel_version)
        {
            CalibrationSession calibration = this.CalibrationSessions.First(x => x.Id == session_id);

            if (calibration == null)
            {
                throw new Exception(string.Format(Catalog.GetString("No CalibrationSession found for session_id {0}"), session_id));
            }

            ReferenceWheelProcessor processor = new ReferenceWheelProcessor(this.Creator);
            processor.ReferenceWheelVersion = reference_wheel_version;

            processor.InternalDark = calibration.DarkSpectrum;
            processor.InternalWhite = calibration.WhiteSpectrum;
            processor.InternalOxid = calibration.OxidSpectrum;

            processor.InternalDarkReference = calibration.DarkSpectrum;
            processor.InternalWhiteReference = calibration.WhiteSpectrum;
            processor.InternalOxidReference = calibration.OxidSpectrum;

            return processor;
        }

        private ReferenceBlockProcessor NewReferenceBlockSetWithCalibrationSpectra(int session_id)
        {
            ReferenceBlockProcessor processor = new ReferenceBlockProcessor(this.Creator);
            CalibrationSession calibration = this.CalibrationSessions.First(x => x.Id == session_id);

            if (calibration == null)
            {
                throw new Exception(string.Format(Catalog.GetString("No CalibrationSession found for session_id {0}"), session_id));
            }

            processor.DarkReference = calibration.DarkSpectrum;
            //set.FixTwoGlassBlackReference = calibration.;
            processor.FixTwoGlassWhiteReference = calibration.WhiteSpectrum;
            processor.FixTwoGlassOxideReference = calibration.OxidSpectrum;

            return processor;
        }

        public void Evaluate(Dictionary<Enums.AcquisitionType, AcquisitionSessions> all_sessions,
                             CalibrationSessions calibration_sessions)
        {
            this.EvaluationSuccess = false;
            this.ReferenceWheelDegraded = false;

            this.SessionsBlack = all_sessions.Where(x => x.Key == Enums.AcquisitionType.Black)
                                             .Select(y => y.Value)
                                             .SelectMany(x => x.SessionQuue)
                                             .ToList();
            this.SessionsWhite = all_sessions.Where(x => x.Key == Enums.AcquisitionType.White)
                                             .Select(y => y.Value)
                                             .SelectMany(x => x.SessionQuue)
                                             .ToList();
            this.SessionsEmpty = all_sessions.Where(x => x.Key == Enums.AcquisitionType.Empty)
                                             .Select(y => y.Value)
                                             .SelectMany(x => x.SessionQuue)
                                             .ToList();
            this.SessionsWhiteInsert = all_sessions.Where(x => x.Key == Enums.AcquisitionType.WhiteInsert)
                                                   .Select(y => y.Value)
                                                   .SelectMany(x => x.SessionQuue)
                                                   .ToList();
            this.SessionsTransflectance = all_sessions.Where(x => x.Key == Enums.AcquisitionType.Transflectance)
                                                      .Select(y => y.Value)
                                                      .SelectMany(x => x.SessionQuue)
                                                      .ToList();
            this.CalibrationSessions = calibration_sessions.SessionQuue;
            this.SessionIDs = this.CalibrationSessions.Select(x => x.Id).ToList();

            if (this.ProcessorTypeName == typeof(ReferenceWheelProcessor).Name)
            {
                this.ReferenceWheelSetBlackEvaluation();
                this.ReferenceWheelSetWhiteEvaluation();
                this.ReferenceWheelSetWhitePEEvaluation();
                this.ReferenceWheelSetTransflectanceEvaluation();
                this.EvaluationSuccess = true;
            }
            else if (this.ProcessorTypeName == typeof(ReferenceBlockProcessor).Name)
            {
                throw new Exception(Catalog.GetString("Reference blocks are unsupported."));
                /*FIXME 37848 Placeholder for Referenceblock of FindeX
                this.ReferenceBlockSetBlackEvaluation();
                this.ReferenceBlockSetWhiteEvaluation();
                this.ReferenceBlockSetWhitePEEvaluation();
                this.ReferenceBlockSetTransflectanceEvaluation();
                this.EvaluationSuccess = true;                   
                */
            }
            else
            {
                throw new Exception(Catalog.GetString("Unsupported reference set."));
            }
        }

        private PdfTableStringCell[,] CreateRerferenceTableWithHead(int table_count, int table_length)
        {
            int header = 1;

            PdfTableStringCell[,] table_data = new PdfTableStringCell[table_count + header, table_length];

            for (int i = 0; i < table_data.GetLength(0); i++)
            {
                for (int j = 0; j < table_data.GetLength(1); j++)
                {
                    table_data[i, j] = new PdfTableStringCell();
                    if (i == 0 || j == 0)
                        table_data[i, j].FontType = PdfFontTypes.HELVETICA_BOLD;
                }
            }
            table_data[0, 0].Value = Catalog.GetString("Serial Number");
            table_data[0, 1].Value = Catalog.GetString("Check");
            table_data[0, 1].Align = PdfTextAlignments.Center;
            //table_data[0, 2].Value = Catalog.GetString("Limits");

            return table_data;
        }

        public void Save(string file_path)
        {
            string file = Path.Combine(file_path, ResultData.FILE_NAME);
            if (FileIO.ExistsFile(file))
                FileIO.DeleteFile(file);

            XmlWriterSettings settings = new XmlWriterSettings
            {
                Indent = true,
                IndentChars = "\t",
                NewLineOnAttributes = true,
                NewLineChars = "\n"
            };

            using (XmlWriter writer = XmlWriter.Create(file, settings))
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(ResultData));

                serializer.WriteStartObject(writer, this);
                writer.WriteAttributeString("xmlns", "p", null, "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
                serializer.WriteObjectContent(writer, this);
                serializer.WriteEndObject(writer);
            }
        }

        public static ResultData Load(string[] files)
        {
            string result_file = files.First(x => Path.GetFileName(x) == ResultData.FILE_NAME);

            if (string.IsNullOrEmpty(result_file))
                throw new Exception(Catalog.GetString("No result file found among given files."));

            return ResultData.Load(Path.GetFullPath(result_file));
        }

        public static ResultData Load(string file_path)
        {
            ResultData result = null;
            string file = file_path;

            if (Path.GetFileName(file_path) != ResultData.FILE_NAME)
                file = Path.Combine(file_path, ResultData.FILE_NAME);

            try
            {
                using (Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read))
                {
                    XmlDictionaryReaderQuotas quotas = new XmlDictionaryReaderQuotas();
                    quotas.MaxStringContentLength = Int32.MaxValue;

                    using (XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, quotas))
                    {
                        DataContractSerializer serializer = new DataContractSerializer(typeof(ResultData));
                        result = (ResultData)serializer.ReadObject(reader, true);
                    }
                }

            }
            catch (Exception ex)
            {
                throw new Exception(string.Format(Catalog.GetString("Cannot read Model Structure XML: {0}"), ex.Message), ex);
            }

            return result;
        }
    }
}
