// 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.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Hiperscan.Spectroscopy;
using System.Xml;
using System.Globalization;

namespace Hiperscan.Extensions.ApoIdentCommon.PerformanceQualification
{
	public class ReferenceSet
	{
		private const string SET_TAG 			= "Set";
		private const string COMMENT_ATTRINUTE	= "Comment"; 

		private const string REFERENCE_B_TAG 	= "ReferenceB";
		private const string REFERENCE_1_TAG 	= "Reference1";
		private const string REFERENCE_2_TAG 	= "Reference2";
		private const string REFERENCE_5_TAG 	= "Reference5";
		private const string REFERENCE_8_TAG 	= "Reference8";
		private const string REFERENCE_W_TAG 	= "ReferenceW";
		private const string REFERENCE_X_TAG 	= "ReferenceX";
		private const string ID_ATTRINUTE		= "Id";
		private const string DATE_ATTRIBUTE		= "Date";
		
		
		public const string RB = "B";
		public const string R1 = "1";
		public const string R2 = "2";
		public const string R5 = "5";
		public const string R8 = "8";
		public const string RW = "W";
        public const string RX = "X";
		
		public const string X1132_9 = "1132_9";
		public const string X1261_8 = "1261_8";
		public const string X1320_2 = "1320_2";
		public const string X1534_6 = "1534_6";
		public const string X1681_4 = "1681_4";
		public const string X1757_6 = "1757_6";
		public const string X1847_3 = "1847_3";
		
		public class SingleSet
		{
			public double Reflectance 			   {get;set;}
			public string Id					   {get;set;}
			public string ScannerId				   {get;set;}
			public bool IdIsInvariant			   {get;set;}
            public CertificateInfo CertificateInfo {get;set;}
			
			public SingleSet(bool id_invariant)
			{
				this.Reflectance 	= -1;
				this.Id				= String.Empty;
				this.ScannerId		= String.Empty;
				this.IdIsInvariant	= id_invariant;
			}
		}

		public ReferenceSet ()
		{
			this.InitializeSets(false);
			this.Id = -1;
			this.AcquistionDate = DateTime.MinValue;
		}
			
		public void InitializeSets(bool id_invariant)
		{
			this.SingleSets = new Dictionary<string, SingleSet>();
			this.SingleSets.Add(ReferenceSet.RB, new SingleSet(id_invariant));
			this.SingleSets.Add(ReferenceSet.R1, new SingleSet(id_invariant));
			this.SingleSets.Add(ReferenceSet.R2, new SingleSet(id_invariant));
			this.SingleSets.Add(ReferenceSet.R5, new SingleSet(id_invariant));
			this.SingleSets.Add(ReferenceSet.R8, new SingleSet(id_invariant));
			this.SingleSets.Add(ReferenceSet.RW, new SingleSet(id_invariant));
			this.SingleSets.Add(ReferenceSet.RX, new SingleSet(id_invariant));
		}
		

		
		public SingleSet this[string indexer]
		{
			get
			{
				if (this.SingleSets.ContainsKey(indexer) == false)
					return null;
				else
					return this.SingleSets[indexer];
			}
		}
		
		private bool IsSingleSetComplete(string index)
		{
			bool is_complete = true;
			if (String.IsNullOrEmpty(this[index].Id) == true)
				is_complete = false;
			if (String.IsNullOrEmpty(this[index].ScannerId) == true)
				is_complete = false;
			if (index != ReferenceSet.RB && index != ReferenceSet.RX)
			{
				if (this[index].Reflectance < 0)
					is_complete = false;
			}				
			return is_complete;
		}
		
		private bool IsSingleSetValid(string index)
		{
			string id = this[index].Id;
			string id_pattern = @"^(\d\d((0[1-9])|([1-4]\d)|(5[0-3])))" + index + @"-\d{3}$";
			Regex id_regex = new Regex(id_pattern);
			if (id_regex.Match(id ?? String.Empty).Success == false)
				return false;
			
			string scanner_id = this[index].ScannerId;
			string scanner_id_pattern = @"^" + index + @"\d{3}$";
			Regex scanner_idregex = new Regex(scanner_id_pattern);
			if (scanner_idregex.Match(scanner_id ?? String.Empty).Success == false)
				return false;
			
			return id.Substring(4).Replace("-", String.Empty) == scanner_id;
		}
		
		private bool IsSingleSetUniqueLocal(string index)
		{
			string id = this[index].Id;
			string scanner_id = this[index].ScannerId;
			foreach (string other_index in this.SingleSets.Keys)
			{
				if (other_index != index)
				{
					if (this[other_index].Id == id || this[other_index].ScannerId == scanner_id)
						return false;
				}	
			}			
			return true;
		}
		
		public bool SetsValidAndComplete
		{
			get
			{
				foreach (string index in this.SingleSets.Keys)
				{
					if (this.IsSingleSetComplete(index) == false)
						return false;
					if (this.IsSingleSetValid(index) == false)
						return false;
				}
				return true;
			}
		}
		
		public bool CanBeAdded
		{
			get
			{
				foreach (string index in this.SingleSets.Keys)
				{
					if (this.IsSingleSetComplete(index) == false)
						return false;
					if (this.IsSingleSetValid(index) == false)
						return false;
					if (this.IsSingleSetUniqueLocal(index) == false)
						return false;
					if (this.Id < 0)
						return false;
				}
				return true;
			}
		}
		
		public double[] AbsorbanceValues
		{
			get
			{
				double[] absorbance_values = new double[5];
				double max_reflecetion = this[ReferenceSet.RW].Reflectance; 
				absorbance_values[0] =  Math.Log10(max_reflecetion / this[ReferenceSet.RW].Reflectance); 
				absorbance_values[1] =  Math.Log10(max_reflecetion / this[ReferenceSet.R8].Reflectance); 
				absorbance_values[2] =  Math.Log10(max_reflecetion / this[ReferenceSet.R5].Reflectance); 
				absorbance_values[3] =  Math.Log10(max_reflecetion / this[ReferenceSet.R2].Reflectance); 
				absorbance_values[4] =  Math.Log10(max_reflecetion / this[ReferenceSet.R1].Reflectance); 
				return (absorbance_values);
			}
		}

		public double[] ReflectanceValues
		{
			get
			{
				double[] reflectance_values = new double[5];
				reflectance_values[0] =  this[ReferenceSet.RW].Reflectance; 
				reflectance_values[1] =  this[ReferenceSet.R8].Reflectance; 
				reflectance_values[2] =  this[ReferenceSet.R5].Reflectance; 
				reflectance_values[3] =  this[ReferenceSet.R2].Reflectance; 
				reflectance_values[4] =  this[ReferenceSet.R1].Reflectance; 
				return (reflectance_values);
			}
		}
		
		public void WriteToXml(XmlNode parent_node)
		{
			CultureInfo culture = new CultureInfo("en-US");
			
			XmlDocument doc = parent_node.OwnerDocument;
			
			XmlElement set_element = doc.CreateElement(ReferenceSet.SET_TAG);
			parent_node.AppendChild(set_element);

			XmlAttribute set_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			set_id_attribute.Value = this.Id.ToString();
			set_element.Attributes.Append(set_id_attribute);

			if (this.AcquistionDate != DateTime.MinValue)
			{
				XmlAttribute set_date_attribute = doc.CreateAttribute(ReferenceSet.DATE_ATTRIBUTE);
				set_date_attribute.Value = this.AcquistionDate.ToString(CultureInfo.InvariantCulture);
				set_element.Attributes.Append(set_date_attribute);
			}
			
			if (this.Id == 0)
			{
				XmlAttribute comment_attribute = doc.CreateAttribute(ReferenceSet.COMMENT_ATTRINUTE);
				comment_attribute.Value = "MASTER";
				set_element.Attributes.Append(comment_attribute);
			}
			
			XmlElement ref_b_element = doc.CreateElement(ReferenceSet.REFERENCE_B_TAG);
			set_element.AppendChild(ref_b_element);
			XmlAttribute ref_b_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			ref_b_id_attribute.Value = this.SingleSets[ReferenceSet.RB].Id;
			ref_b_element.Attributes.Append(ref_b_id_attribute);
			
			XmlElement ref_1_element = doc.CreateElement(ReferenceSet.REFERENCE_1_TAG);
			set_element.AppendChild(ref_1_element);
			ref_1_element.InnerText = this.SingleSets[ReferenceSet.R1].Reflectance.ToString(culture);
			XmlAttribute ref_1_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			ref_1_id_attribute.Value = this.SingleSets[ReferenceSet.R1].Id;
			ref_1_element.Attributes.Append(ref_1_id_attribute);
			
			XmlElement ref_2_element = doc.CreateElement(ReferenceSet.REFERENCE_2_TAG);
			set_element.AppendChild(ref_2_element);
			ref_2_element.InnerText = this.SingleSets[ReferenceSet.R2].Reflectance.ToString(culture);
			XmlAttribute ref_2_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			ref_2_id_attribute.Value = this.SingleSets[ReferenceSet.R2].Id;
			ref_2_element.Attributes.Append(ref_2_id_attribute);
			
			XmlElement ref_5_element = doc.CreateElement(ReferenceSet.REFERENCE_5_TAG);
			set_element.AppendChild(ref_5_element);
			ref_5_element.InnerText = this.SingleSets[ReferenceSet.R5].Reflectance.ToString(culture);
			XmlAttribute ref_5_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			ref_5_id_attribute.Value = this.SingleSets[ReferenceSet.R5].Id;
			ref_5_element.Attributes.Append(ref_5_id_attribute);
			
			XmlElement ref_8_element = doc.CreateElement(ReferenceSet.REFERENCE_8_TAG);
			set_element.AppendChild(ref_8_element);
			ref_8_element.InnerText = this.SingleSets[ReferenceSet.R8].Reflectance.ToString(culture);
			XmlAttribute ref_8_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			ref_8_id_attribute.Value = this.SingleSets[ReferenceSet.R8].Id;
			ref_8_element.Attributes.Append(ref_8_id_attribute);
			
			XmlElement ref_w_element = doc.CreateElement(ReferenceSet.REFERENCE_W_TAG);
			set_element.AppendChild(ref_w_element);
			ref_w_element.InnerText = this.SingleSets[ReferenceSet.RW].Reflectance.ToString(culture);
			XmlAttribute ref_w_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			ref_w_id_attribute.Value = this.SingleSets[ReferenceSet.RW].Id;
			ref_w_element.Attributes.Append(ref_w_id_attribute);
			
			XmlElement ref_x_element = doc.CreateElement(ReferenceSet.REFERENCE_X_TAG);
			set_element.AppendChild(ref_x_element);
			XmlAttribute ref_x_id_attribute = doc.CreateAttribute(ReferenceSet.ID_ATTRINUTE);
			ref_x_id_attribute.Value = this.SingleSets[ReferenceSet.RX].Id;
			ref_x_element.Attributes.Append(ref_x_id_attribute);	
		}
		
		public void ReadFromXml(XmlNode parent_node)
		{
			CultureInfo culture = new CultureInfo("en-US");
			
			if (parent_node.Name != ReferenceSet.SET_TAG)
				throw new Exception("Invalid xml node.");
			
			this.Id = Int32.Parse(parent_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value);

			try 
			{
				if (parent_node.Attributes[ReferenceSet.DATE_ATTRIBUTE] != null)
					this.AcquistionDate = this.AcquistionDate = Convert.ToDateTime(parent_node.Attributes[ReferenceSet.DATE_ATTRIBUTE].Value, CultureInfo.InvariantCulture);
			}
			catch 
			{
				this.AcquistionDate = DateTime.Now;
			}

			XmlNode ref_b_node = parent_node.SelectNodes(ReferenceSet.REFERENCE_B_TAG)[0];
			this.SingleSets[ReferenceSet.RB].Id = ref_b_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value;
			this.SingleSets[ReferenceSet.RB].ScannerId = this.SingleSets[ReferenceSet.RB].Id.Substring(4).Replace("-", String.Empty);
			
			XmlNode ref_1_node = parent_node.SelectNodes(ReferenceSet.REFERENCE_1_TAG)[0];
			this.SingleSets[ReferenceSet.R1].Id = ref_1_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value;
			this.SingleSets[ReferenceSet.R1].ScannerId = this.SingleSets[ReferenceSet.R1].Id.Substring(4).Replace("-", String.Empty);
			this.SingleSets[ReferenceSet.R1].Reflectance = Double.Parse(ref_1_node.InnerText, culture);
			
			XmlNode ref_2_node = parent_node.SelectNodes(ReferenceSet.REFERENCE_2_TAG)[0];
			this.SingleSets[ReferenceSet.R2].Id = ref_2_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value;
			this.SingleSets[ReferenceSet.R2].ScannerId = this.SingleSets[ReferenceSet.R2].Id.Substring(4).Replace("-", String.Empty);
			this.SingleSets[ReferenceSet.R2].Reflectance = Double.Parse(ref_2_node.InnerText, culture);
			
			XmlNode ref_5_node = parent_node.SelectNodes(ReferenceSet.REFERENCE_5_TAG)[0];
			this.SingleSets[ReferenceSet.R5].Id = ref_5_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value;
			this.SingleSets[ReferenceSet.R5].ScannerId = this.SingleSets[ReferenceSet.R5].Id.Substring(4).Replace("-", String.Empty);
			this.SingleSets[ReferenceSet.R5].Reflectance = Double.Parse(ref_5_node.InnerText, culture);
			
			XmlNode ref_8_node = parent_node.SelectNodes(ReferenceSet.REFERENCE_8_TAG)[0];
			this.SingleSets[ReferenceSet.R8].Id = ref_8_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value;
			this.SingleSets[ReferenceSet.R8].ScannerId = this.SingleSets[ReferenceSet.R8].Id.Substring(4).Replace("-", String.Empty);
			this.SingleSets[ReferenceSet.R8].Reflectance = Double.Parse(ref_8_node.InnerText, culture);
			
			XmlNode ref_w_node = parent_node.SelectNodes(ReferenceSet.REFERENCE_W_TAG)[0];
			this.SingleSets[ReferenceSet.RW].Id = ref_w_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value;
			this.SingleSets[ReferenceSet.RW].ScannerId = this.SingleSets[ReferenceSet.RW].Id.Substring(4).Replace("-", String.Empty);
			this.SingleSets[ReferenceSet.RW].Reflectance = Double.Parse(ref_w_node.InnerText, culture);

			XmlNode ref_x_node = parent_node.SelectNodes(ReferenceSet.REFERENCE_X_TAG)[0];
			this.SingleSets[ReferenceSet.RX].Id = ref_x_node.Attributes[ReferenceSet.ID_ATTRINUTE].Value;
			this.SingleSets[ReferenceSet.RX].ScannerId = this.SingleSets[ReferenceSet.RX].Id.Substring(4).Replace("-", String.Empty);
		}
		
		public int Id								{get; set;}
		public DateTime AcquistionDate					{get; set;}
		public Dictionary<string, SingleSet> SingleSets {get; private set;}
	}
}

