// Created with MonoDevelop
//
//    Copyright (C) 2009 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;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml;


namespace Hiperscan.Spectroscopy
{
	
	public enum SubstanceType
	{
		Solid,
		Fluid
	}

    [DataContract(Name = "PcaResult", Namespace = "http://www.hiperscan.com/spectroscopy")]
    public class PcaResult : System.Collections.Generic.IEnumerable<PcaMatch>
    {
        [DataMember(Name = "SignificanceThreshold", Order = 0)] private double significance_threshold;
        [DataMember(Name = "ConfidenceThreshold",   Order = 1)] private double confidence_threshold;
        [DataMember(Name = "CorrelationThreshold",  Order = 2)] double correlation_threshold;

        [DataMember(Name = "IsSorted", Order = 3)] private bool is_sorted;

        [DataMember(Name = "PcaMatches", Order = 5)] List<PcaMatch> matches;
        private Dictionary<string, PcaMatch> matches_by_name;

        private Spectrum spectrum;
        private string group;
		
		public PcaResult()
		{
			this.matches_by_name  = new Dictionary<string,PcaMatch>();
			this.matches          = new List<PcaMatch>();
			this.is_sorted        = false;
			this.group            = string.Empty;

			this.significance_threshold = 0.0;
			this.confidence_threshold   = 0.0;
			this.correlation_threshold  = -1.0;
		}
			
		
		public PcaResult(Spectrum spectrum) : this()
		{
			this.spectrum = spectrum;
		}
		
		public void Add(PcaMatch match)
		{
			this.is_sorted = false;
			this.matches.Add(match);
			this.matches_by_name.Add(match.Id, match);
		}

		private void Sort()
		{
			if (this.is_sorted)
				return;
			
			this.matches.Sort();
			this.matches.Reverse();

			for (int ix=0; ix < this.matches.Count; ++ix)
			{
				this.matches[ix].SetRanking(ix);
			}
			
			this.is_sorted = true;
		}
		
		public IEnumerator<PcaMatch> GetEnumerator()
		{
			foreach (PcaMatch match in this.matches)
			{
				yield return match;
			}
		}
		
		IEnumerator IEnumerable.GetEnumerator()
		{ 
			return GetEnumerator();    
		}
		
		public bool ExceedsThresholds(PcaMatch match)
		{
			return (match.Significance >= this.SignificanceThreshold &&
			        match.Confidence   >= this.ConfidenceThreshold   &&
			        match.Correlation  >= this.CorrelationThreshold);
		}

		public PcaMatch this[string name]
		{
			get
			{
				this.Sort();
				return this.matches_by_name[name];
			}
		}
		
		public int Count
		{
			get { return this.matches.Count; }
		}
		
		public Spectrum Spectrum
		{
			get { return this.spectrum; }
		}
		
		public double SignificanceThreshold
		{
			get { return this.significance_threshold;  }
			set { this.significance_threshold = value; }
		}
		
		public double ConfidenceThreshold
		{
			get { return this.confidence_threshold;  }
			set { this.confidence_threshold = value; }
		}
		
		public double CorrelationThreshold
		{
			get { return this.correlation_threshold;  }
			set { this.correlation_threshold = value; }
		}
		
		public PcaMatch this[int ranking]
		{
			get
			{
				this.Sort();
				return this.matches[ranking];
			}
		}
		
		public string Group
		{
			get { return this.group;  }
			set { this.group = value; }
		}
		
		public bool IsSorted
		{
			get { return this.is_sorted;  }
			set { this.is_sorted = value; }
		}
		
		public bool HasMatch
		{
			get
			{
				if (this.matches.Count < 1)
					return false;
				
				return (this.matches[0].Significance >= this.SignificanceThreshold &&
				        this.matches[0].Confidence   >= this.ConfidenceThreshold   &&
				        this.matches[0].Correlation  >= this.CorrelationThreshold);
			}
		}

        public static void Write(PcaResult pca_result, string fname)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.IndentChars = "\t";
            //settings.NewLineOnAttributes = true;
            settings.NewLineChars = "\n";

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

                // we want to register a global namespace to prevent thousands of repeats
                serializer.WriteStartObject(writer, pca_result);
                writer.WriteAttributeString("xmlns", "p", null, "http://schemas.microsoft.com/2003/10/Serialization/Arrays");
                writer.WriteAttributeString("xmlns", "q", null, "http://www.w3.org/2001/XMLSchema");
                serializer.WriteObjectContent(writer, pca_result);
                serializer.WriteEndObject(writer);
            }
        }
    }
}