// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2017 Thomas Klose & Torsten Schache, 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.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Xml;
using System.ComponentModel;

using Hiperscan.Spectroscopy;
using Hiperscan.Unix;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Hiperscan.I18n;
using Hiperscan.SGS;
using Hiperscan.Spectroscopy.IdentModule;


namespace Hiperscan.Extensions.ApoIdent
{
    [DataContract]
    public class ReportInfo : IExtensibleDataObject
	{
		private const string QUERY_TAG 	        = "QueryMetaData";
        public const string USE_ZENITH          = "Zenith";
        public const string USE_TI_O2           = "TiO2";
        public const string VERSION             = "2.0";

        public ReportInfo() 
		{

		}

        public ReportInfo(FinderWindow finder_window)
        {
            this.Company        = finder_window.Config.Profiles.Current.Company;
            this.Town           = finder_window.Config.Profiles.Current.Town;
            this.Street         = finder_window.Config.Profiles.Current.Street;
            this.ReportCountry  = finder_window.Config.Profiles.Current.ReportCountry;
            this.Culture        = this.GetCulture(this.ReportCountry);
            this.ProtocolType   = finder_window.Config.Profiles.Current.ExchangeData.ProtocolType;
            this.ShowDifferencePlot = finder_window.Config.Profiles.Current.ShowDifference;

            this.DefaultName    = finder_window.SubstanceManagement.CurrentSubstance.SelectedDefaultName;
            this.LatinName      = finder_window.SubstanceManagement.CurrentSubstance.SelectedLatinName;
            this.Batch          = StringHelper.ConvertEuroSign(finder_window.GtkBatchValue);
            this.TestId         = finder_window.GetTestId();
            this.User           = finder_window.GtkUsernameValue;
            this.Supplier       = finder_window.GtkSupplierValue;
            this.Comment        = StringHelper.ConvertEuroSign(finder_window.GtkCommentValue);
            this.Pzn            = finder_window.GtkPznValue;
            this.CorrectionFactor = finder_window.GtkCorrectionFactorValue;
			this.AdditionalTest = finder_window.Config.Profiles.Current.AdditionalCompletionEntries.ProtocolText;
			this.CustomerResult = finder_window.Config.Profiles.Current.AdditionalCompletionEntries.CustomerEvaluation(finder_window);
            this.DeadLine       = BatchDeadLine.CurrentDeadLineType;
            this.IsMeasurable   = finder_window.SubstanceManagement.CurrentClassification.IsNirMeasurable;

            int year    = finder_window.DeadLineYear;
            int month   = finder_window.DeadLineMonth;
            int day     = DateTime.DaysInMonth(year, month);
            this.ExpireDate     = new DateTime(year, month, day);

            this.SubstanceClass             = finder_window.SubstanceManagement.CurrentModelClass.ModelClassName;
            this.AccessDatabaseType         = finder_window .SubstanceManagement.CurrentModelClass.DatabaseInfo.Type;
            this.SelectedSubstanceId        = finder_window.SubstanceManagement.CurrentSubstance.Id;
            this.ClassificationName         = finder_window.SubstanceManagement.CurrentClassification.ClassificationName;
            this.ClassificationType         = finder_window.SubstanceManagement.CurrentClassification.ClassificationType;;
            this.HasValidationDocument      = finder_window.SubstanceManagement.CurrentModelClass.ValidationDocument;
            this.BatchNumbers               = finder_window.SubstanceManagement.CurrentSubstance.BatchNumbers;

            this.SubstanceNamesOfClassification = new List<string>(finder_window.SubstanceManagement.CurrentClassification.GetSubstanceNames(null).Keys);
            this.IdentServerLocation = finder_window.SubstanceManagement.Config.AcquisitionSettings.Locale;

            string copyright = String.Format("Copyright \u00A9 Hiperscan GmbH 2010 - {0}", Plugin.copyright_year);

            if (this.IsMeasurable == true)
            {
                this.Spectrum           = finder_window.CurrentProcessedSpectrum;
                this.ProbeType          = finder_window.CurrentProcessedSpectrum.ProbeType;
                this.SpectrumType       = finder_window.CurrentProcessedSpectrum.SpectrumType;
                this.MatchBackProjection    = finder_window.IdentServerClient.Result.MatchBackProjection;
                this.Backprojection         = finder_window.IdentServerClient.Result.SpectrumBackProjection;

                this.ScanEngineVersion  = finder_window.Finder.ScanEngineVersion;
               
                this.Serial             = finder_window.CurrentProcessedSpectrum.Serial;
                this.DeviceName         = finder_window.Finder.DeviceName;
                this.OemSerialNumber    = finder_window.Finder.OemSerialNumber;
                if (String.IsNullOrEmpty(this.OemSerialNumber) == true)
                    this.OemSerialNumber = this.Serial;

                this.DimmerValue    = finder_window.Finder.DimmerValue;
                this.WhiteReference = ReportInfo.USE_ZENITH;

                this.Version        = finder_window.IdentServerClient.Result.Identifier + " (" + finder_window.IdentServerClient.Result.Date.ToString("yyyy-MM-dd hh:mm") + ")";
                this.Rating         = finder_window.IdentServerClient.Result.Rating;
                this.MinRating      = finder_window.IdentServerClient.Result.MinRating;
                this.MatchId        = finder_window.IdentServerClient.Result.MatchId;
                this.MatchRating    = finder_window.IdentServerClient.Result.MatchRating;
                this.IsMatch        = finder_window.IdentServerClient.IsTruePositive;
                this.SignificanceLevel  = finder_window.IdentServerClient.Result.SignificanceLevel;

                this.PCAVersion          = IdentServerClient.Info.PcaVersion;

                this.Date       = finder_window.CurrentProcessedSpectrum.Timestamp;
                this.Footer     = String.Format(Catalog.GetString("Created by QuickStep {0} {1}, Ident-Module {2}, SGS: {3}, White reference: {4}, PCA: {5}, {6}, http://www.hiperscan.com"),
                                                       Plugin.name, 
                                                       Plugin.version, 
                                                       IdentServerClient.Info.ShortIdentModuleVersion,
                                                       finder_window.Finder.Serial,
                                                       "Zenith",
                                                       IdentServerClient.Info.PcaVersion,
                                                       copyright);

                this.Creator = this.Spectrum.Creator;
                this.FinderSDTransformation = finder_window.Finder.Current.TransformationType;

            }
            else
            {
                this.IsMatch    = false;
                this.Date       = finder_window.NotMeasurableCreationDate;
                this.Footer     = String.Format(Catalog.GetString("Created by QuickStep {0} {1}, {2}, http://www.hiperscan.com"),
                                                       Plugin.name, 
                                                       Plugin.version, 
                                                       copyright);                
            }

            this.Label = new Label(this);
            this.PdfFileFullName = FilesAndDirs.ProtocolPdfFileName(this);
            this.XmlFileFulleName = FilesAndDirs.ProtocolXmlFileName(this);
            if (this.IsMeasurable == true)
                this.CsvFilFullName = FilesAndDirs.ProtocolCsvFileName(this);
            this.SetReportInfo();
        }
      
        private void SetReportInfo()
		{
            if (this.Spectrum == null)
                return;

			StringBuilder sb = new StringBuilder();

			sb.AppendFormat("[Apo-Ident::SubstanceClass]{0}\n",     this.SubstanceClass);
			sb.AppendFormat("[Apo-Ident::Specimen]{0}\n",        	this.DefaultName);
			sb.AppendFormat("[Apo-Ident::Latin]{0}\n",          	this.LatinName);
			sb.AppendFormat("[Apo-Ident::Batch]{0}\n",           	this.Batch);
			sb.AppendFormat("[Apo-Ident::Pzn]{0}\n",          	 	this.Pzn);	
			sb.AppendFormat("[Apo-Ident::Supplier]{0}\n",        	this.Supplier);
			sb.AppendFormat("[Apo-Ident::ExpireDate]{0}\n",      	this.ExpireDate.ToString("yyyy-MM-dd"));

			sb.AppendFormat("[Apo-Ident::DeviceName]{0}\n",      	this.DeviceName);
			sb.AppendFormat("[Apo-Ident::DimmerValue]{0}\n",     	this.DimmerValue.ToString());
			sb.AppendFormat("[Apo-Ident::ScanEngineVersion]{0}\n",  this.ScanEngineVersion);

			sb.AppendFormat("[Apo-Ident::TestId]{0}\n",          	this.TestId);
			sb.AppendFormat("[Apo-Ident::User]{0}\n",            	this.User);
			sb.AppendFormat("[Apo-Ident::Comment]{0}\n",         	this.Comment);

			sb.AppendFormat("[Apo-Ident::SelectedSubstanceId]{0}\n",this.SelectedSubstanceId);
			sb.AppendFormat("[Apo-Ident::IsMatch]{0}\n",         	this.IsMatch.ToString());
			sb.AppendFormat("[Apo-Ident::Confidence]{0}\n",         this.Rating.ToString(Spectrum.CultureInfo));
			sb.AppendFormat("[Apo-Ident::MinRating]{0}\n",      	this.MinRating.ToString(Spectrum.CultureInfo));

			sb.AppendFormat("[Apo-Ident::MatchName]{0}\n",       	this.MatchId);
			sb.AppendFormat("[Apo-Ident::MatchRating]{0}\n",     	this.MatchRating.ToString(Spectrum.CultureInfo));
			sb.AppendFormat("[Apo-Ident::CustomerEval]{0}\n",     	this.CustomerResult.ToString());

			sb.AppendFormat("[Apo-Ident::Version]{0}\n",         	    this.Version);
			sb.AppendFormat("[Apo-Ident::PCAVersion]{0}\n",      	    this.PCAVersion);
			sb.AppendFormat("[Apo-Ident::WhiteReference]{0}\n",  	    this.WhiteReference);
            sb.AppendFormat("[Apo-Ident::FinderSDTransformation]{0}",   this.FinderSDTransformation.ToString());

            this.Spectrum.Comment = sb.ToString();           
		}
      
        [DataMember] public string Company 				{get; set;} = String.Empty;
        [DataMember] public string Street 				{get; set;} = String.Empty;
        [DataMember] public string Town 			    {get; set;} = String.Empty;
        [DataMember] public DateTime Date 				{get; set;} = DateTime.Now;

        [DataMember] public string SubstanceClass		{get; set;} = String.Empty;
        [DataMember] public string DefaultName 			{get; set;} = String.Empty;
        [DataMember] public string LatinName 			{get; set;} = String.Empty;
        [DataMember] public string Batch 				{get; set;} = String.Empty;
        [DataMember] public string Pzn 					{get; set;} = String.Empty;
        [DataMember] public string CorrectionFactor     {get; set;} = String.Empty;
        [DataMember] public string Supplier 			{get; set;} = String.Empty;
        [DataMember] public DateTime ExpireDate 		{get; set;} = DateTime.Now;

        [DataMember] public string Serial 				{get; set;} = String.Empty;
        [DataMember] public string DeviceName			{get; set;} = String.Empty;
        [DataMember] public string OemSerialNumber              { get; set; } = String.Empty;
        [DataMember] public byte DimmerValue 			{get; set;} = 0;
        [DataMember] public string ScanEngineVersion 	{get; set;} = Catalog.GetString("n/a");

        [DataMember] public string TestId 				{get; set;} = String.Empty;
        [DataMember] public string User 				{get; set;} = String.Empty;
        [DataMember] public string Comment 				{get; set;} = String.Empty;

        [DataMember] public string SelectedSubstanceId 	{get; set;} = String.Empty;
        [DataMember] public bool   IsMatch 				{get; set;} = false;
        [DataMember] public double Rating 				{get; set;} = 0;
        [DataMember] public double MinRating 			{get; set;} = 0;
        [DataMember] public string MatchId   			{get; set;} = String.Empty;
        [DataMember] public double MatchRating 			{get; set;} = 0;

        [DataMember] public string AdditionalTest 		{get; set;} = String.Empty;
        [DataMember] public bool CustomerResult			{get; set;} = false;

        [DataMember] public string Footer 				{get; set;} = String.Empty;
        [DataMember] public string Version 				{get; set;} = String.Empty;
        [DataMember] public string PCAVersion 			{get; set;} = String.Empty;
        [DataMember] public string WhiteReference 		{get; set;} = String.Empty;

        [DataMember] public ReportCountryType ReportCountry         {get; set;} = ReportCountryType.Germany;
        [DataMember] public BatchDeadLine.DeadLineType DeadLine		{get; set;} = BatchDeadLine.DeadLineType.UseByDate;
        [DataMember] public bool IsMeasurable			            {get; set;} = true;
        [DataMember] public Label  Label				            {get; set;} = null;

        // here are the memebers, which are valid with ReportInfo-Version >= 2.0 (Apo-Ident-Version >= 2.0)
        [DataMember] public string SerializedCulture                     {get; set;} = CultureInfo.CurrentCulture.ToString();
        public CultureInfo Culture                                       {get; set;} = CultureInfo.CurrentCulture;
        [DataMember] public string Creator                               {get; set;} = String.Empty;
       
        [DataMember] public ProbeType ProbeType                          {get; set;} = ProbeType.Unknown;
        [DataMember] public ProtocolType ProtocolType                    {get; set;} = ProtocolType.ApoIdent;
        [DataMember] public SpectrumType SpectrumType                    {get; set;} = SpectrumType.Unknown;
        [DataMember] public AccessDatabaseType AccessDatabaseType        {get; set;} = AccessDatabaseType.None;
        [DataMember] public ClassificationType ClassificationType        {get; set;} = ClassificationType.None;
        [DataMember] public string ClassificationName                    {get; set;} = String.Empty;
        [DataMember] public bool HasValidationDocument                   {get; set;} = false;
        [DataMember] public string SignificanceLevel                     {get; set;} = String.Empty;

        [DataMember] public List<string> SubstanceNamesOfClassification  {get; set;} = new List<string>();    
        [DataMember] public List<string> BatchNumbers                    {get; set;} = new List<string>();
        [DataMember] public bool ShowDifferencePlot                      {get; set;} = false;
        [DataMember] public string ReportInfoVersion                     {get; set;} = ReportInfo.VERSION;
        [DataMember] public Locale.LocaleType IdentServerLocation        {get; set;} = Locale.LocaleType.Unknown;

        [DataMember] public TransformationTypes FinderSDTransformation {get; set;} = TransformationTypes.None;

        // this member don't have to be restored
        public Spectrum Spectrum            {get; set;} = null;
        public DataSet MatchBackProjection  {get; set;} = null;
        public DataSet Backprojection       {get; set;} = null;
        public string PdfFileFullName       {get; set;} = String.Empty;
        public string CsvFilFullName        {get; set;} = String.Empty;
        public string XmlFileFulleName      {get; set;} = String.Empty;

        private ExtensionDataObject extensionDataObject_value;
        public ExtensionDataObject ExtensionData
        {
            get
            {
                return extensionDataObject_value;
            }
            set
            {
                extensionDataObject_value = value;
            }
        }


		public CultureInfo GetCulture(ReportCountryType country_type)
        {
            CultureInfo info = null;

            switch(country_type)
            {
                case ReportCountryType.Austria:
                case ReportCountryType.Germany:
                case ReportCountryType.Switzerland:
                    info = new CultureInfo("de-DE");
                    break;

                case ReportCountryType.GreatBritain:
                    info = new CultureInfo("en-GB");
                    break;

                default:
                    throw new Exception(String.Format(Catalog.GetString("Not supported ReportCountry '{0}'."), country_type));

            }

            return info;
        }

        public static void Write(ReportInfo info, string xml_filename)
        {
            try
            {
                string directory = Path.GetDirectoryName(xml_filename);
                if (Directory.Exists(directory) == false)
                {
                    Directory.CreateDirectory(directory);
                    new DirectoryInfo(directory).Attributes = FileAttributes.Hidden | FileAttributes.Directory;
                }

                info.SerializedCulture = info.Culture.ToString();
                using(MemoryStream ms = new MemoryStream())
                {
                    using(XmlTextWriter xml_tw = new XmlTextWriter(ms, Encoding.UTF8))
                    {
                        DataContractSerializer deserializer = new DataContractSerializer(typeof(ReportInfo));
                        deserializer.WriteObject(xml_tw, info);
                    }

                    File.WriteAllBytes(xml_filename, ms.ToArray());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error while writing QueryMetadata; {0}", ex.Message);
            }
        }

        public static ReportInfo Read(string xml_filename)
        {
            ReportInfo info = null;
            try
            {
                using (XmlTextReader tr = new XmlTextReader(xml_filename))
                {
                    DataContractSerializer serializer = new DataContractSerializer(typeof(ReportInfo));
                    info = (ReportInfo)serializer.ReadObject(tr);
                    string file_name = Path.GetFileNameWithoutExtension(xml_filename);
                    string parent_dir = Path.GetDirectoryName(Path.GetDirectoryName(xml_filename));
                    info.XmlFileFulleName = xml_filename;
                    info.PdfFileFullName = Path.Combine(parent_dir, file_name +".pdf");
                    if (info.IsMeasurable == true)
                        info.CsvFilFullName = Path.Combine(parent_dir, file_name + ".csv");
                    info.Culture = new CultureInfo(info.SerializedCulture);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error while reading QueryMetadata; {0}", ex.Message);
            }

            return info;
        }

		public static void ConvertToMetaData2_0(Configurations config)
        {         
            try
            {
                List<string> directories = new List<string>();

                foreach (Profile profile in config.Profiles.Values)
                {               
					if (Directory.Exists(profile.Archive) == false)
                        continue;

					ReportInfo.ConvertToMetaData2_0(profile.Archive);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("Error while converting spectra to query meta data: {0}", ex.Message);
            }
        }
        
        private static void ConvertToMetaData2_0(string directory)
        {
			Regex folder_pattern = new Regex(@"^20\d\d$");

			foreach (string sub_dir in Directory.GetDirectories(directory))
            {
                if (folder_pattern.Match(Path.GetFileName(sub_dir)).Success == false)
                    continue;

                string query_dir = Path.Combine(sub_dir, FilesAndDirs.QUERY_DIR);
                if (Directory.Exists(query_dir) == false)
                    continue;

                foreach (string xml_file in Directory.GetFiles(query_dir, "*.xml"))
                {
					try
                    {
						ReportInfo info = ReportInfo.ReadOldVersion(xml_file);
                        if (info == null)
                            continue;

                        info.ReportInfoVersion = "1.0";

						ReportInfo.Write(info, xml_file);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Error while converting report info: File = {0}, message = {1}",
						                  xml_file, ex.Message);
                    }
                }
            }
        }

        private static ReportInfo ReadOldVersion(string file_name)
        {
            ReportInfo query = new ReportInfo();

            using (StreamReader sr = new StreamReader(file_name, Encoding.UTF8))
            {
                XmlDocument doc = new XmlDocument();
                doc.Load(sr);

                bool found_customer_result  = false;
                bool found_country          = false;
                bool found_dead_line        = false;
                bool found_is_measurable    = false;


                XmlNode root = doc.GetElementsByTagName(ReportInfo.QUERY_TAG)[0];
                if (root == null)
                    return null;

                foreach (XmlElement element in root.ChildNodes)
                {
                    if (element.Name == "Specimen")
                    {
                        query.DefaultName = element.InnerText;
                        continue;
                    }

                    if (element.Name == "Latin")
                    {
                        query.LatinName = element.InnerText;
                        continue;
                    }

                    foreach (PropertyInfo property in query.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                    {
                        if (property.Name == "Label" || property.Name == "FileName")
                            continue;

                        if (property.Name == element.Name)
                        {                   
                            TypeConverter converter = TypeDescriptor.GetConverter(property.PropertyType);
                            object value = converter.ConvertFromInvariantString(element.InnerText);                     
                            property.SetValue(query, value, null);

                            switch (property.Name)
                            {

                                case "CustomerResult":
                                    found_customer_result = true;
                                    break;

                                case "Country":
                                    found_country = true;
                                    break;

                                case "DeadLine":
                                    found_dead_line = true;
                                    break;

                                case "IsMeasurable":
                                    found_is_measurable = true;
                                    break;

                                default:
                                    break;

                            }   
                        }
                    }
                }

                if (found_customer_result == false)
                    query.CustomerResult = query.IsMatch;

                if (found_country == false)
                    query.ReportCountry = ReportCountryType.Germany;

                if (found_dead_line == false)
                    query.DeadLine = BatchDeadLine.DeadLineType.UseByDate;

                if (found_is_measurable == false)
                {
                    try
                    {
                        string spectrum_file = Path.GetFileNameWithoutExtension(file_name) + ".csv";
                        string path = new FileInfo(file_name).Directory.Parent.FullName;
                        query.IsMeasurable = File.Exists(Path.Combine(path, spectrum_file));
                    }
                    catch (Exception){}
                }

                query.Label = new Label(query);
            }

            return query;
        }
	}
}


