// 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.Drawing;
using System.Globalization;
using System.IO;
using System.Collections.Generic;
using System.Reflection;

using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.IdentModule;
using Hiperscan.Unix;
using Hiperscan.PdfLib;


namespace Hiperscan.Extensions.ApoIdent.RankingList
{
	
	public abstract class Generic
	{
		
        public Generic(ResultSet result, string test_id, CultureInfo culture)
		{
			this.LineWidth 	= 0.5;
			this.FontHeight	= 10;
			this.Culture = culture;
            this.Result = result;
            this.TestId = test_id;
			this.MaxWidth = 180;

			this.CreateHeader();
			this.CreateContent();
			this.CreateFooter();
			this.Save();
		}

		private void CreateHeader()
		{
			this.FileName = Path.Combine(Path.GetTempPath(),  Path.GetRandomFileName() + ".pdf");

			this.Document = new Layout(PdfPageSizes.A4_LANDSCAPE, this.FileName);
			this.Document.PDF.SetTitle(Catalog.GetString("Ranking list"));
			this.Document.PDF.SetSubject(Catalog.GetString("This ranking list shows the result of identification."));
			this.Document.RightMargin = 50;

			//Address
			this.Document.SetFont(PdfFontTypes.HELVETICA, 9, PdfColors.Black);
			this.Document.YPosition = 11;
			this.Document.XPosition = 0;
			this.Document.AddHeader(Catalog.GetString("Weißeritzstr. 3, 01067 Dresden\nTel. 0351-212 496 33"));

			//Logo
			Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("Hiperscan.Extensions.ApoIdent.Resources.Images.Apo_Ident_Logo.png");
			this.HiperscanLogo = new Bitmap(s);
			this.Document.AddBitmap(this.HiperscanLogo, new PdfPoint(this.Document.InnerPageWidth - 105, 11), PdfBitmapScales.X, 105);			


		}

		protected void AddHeaderLine(string header)
		{
			//Content header
			this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 14, PdfColors.Black);

			this.Document.YPosition = 63;
			this.Document.XPosition = 0;
			this.Document.AddParagraph(header, PdfTextAlignments.Left);
		}

		protected void MeasureClassificationNames(string header)
		{			
			this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, this.FontHeight, PdfColors.Black);
			this.ClassificationWidth = this.Document.MeasureString(header);

			this.Document.SetFont(PdfFontTypes.HELVETICA, this.FontHeight, PdfColors.Black);
			foreach (NormalizedPCAMatch match in this.Result.PcaMatches)
			{
				double tmp = this.Document.MeasureString(match.ClassificationName);
				if (tmp > this.ClassificationWidth)
					this.ClassificationWidth = tmp;
			}

			if (this.ClassificationWidth > this.MaxWidth)
				this.ClassificationWidth = this.MaxWidth;

			this.ClassificationWidth += 10;
		}

		protected void MeasureRanking(string header)
		{
			this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, this.FontHeight, PdfColors.Black);
			this.RankingWidth = this.Document.MeasureString(header) + 10;
		}


		protected void MeasureCellWidth()
		{
			double tmp = this.Document.InnerPageWidth - this.RankingWidth - this.ClassificationWidth;
			tmp += 7 * this.LineWidth;
			this.CellWidth = tmp / 6;
		}

		public double GetPosition(int column)
		{
			double x = -column * this.LineWidth;

			switch(column)
			{
			case 0:
				break;

			case 1:
				x += this.RankingWidth;
				break;

			case 2:
				x += this.RankingWidth + this.ClassificationWidth;
				break;

			default:
				x += this.RankingWidth + this.ClassificationWidth;
				x += (column - 2) * this.CellWidth;
				break;
			}

			return x;
		}

		public double GetWidth(int column)
		{
			double width = 0;

			switch(column)
			{
			case 0:
				width = this.RankingWidth;
				break;

			case 1:
				width = this.ClassificationWidth;
				break;

			default:
				width = this.CellWidth;
				break;
			}

			return width;
		}

		protected void CreateTableHeader(List<string> headers)
		{
			this.Document.SetFont(PdfFontTypes.HELVETICA, this.FontHeight, PdfColors.Black);
			double height = this.FontHeight * 1.5;
			this.Document.YPosition = 94 + 0.15 * this.FontHeight;


			for (int i = 0; i < headers.Count; i++) 
			{
				double pos_x = this.GetPosition(i);
				double width = this.GetWidth(i);

				PdfRect rect = new PdfRect(pos_x, 94, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, PdfColors.White);

				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(headers[i], width, PdfTextAlignments.Center);
			}
		}

		protected void CreateList()
		{
			double height 	= this.FontHeight * 1.5;
			double pos_x 	= 0;
			double width	= 0;
			double pos_y 	= 94 + height - this.LineWidth;
			string text		= null;
			PdfRect rect	= null;

			this.Document.YPosition = 94 + 0.1 * this.FontHeight + height;

            string first_evaluation = (this.Result.Rating * 100).ToString("f2") + "%";
            bool draw_green = false;
            bool show_number = false;

            if (this.Result.IdentficationMode == ResultSet.IdentificationMode.BlindClassification)
            {
                if (this.Result.ClassificationId != ResultSet.NO_ID && this.Result.PositiveIdentified == true)
                    draw_green = true;

                if (this.Result.ClassificationId != ResultSet.NO_ID)
                    show_number = true;
            }
            else
            {
                if (this.Result.TruePositive == true)
                    draw_green = true;

                if (this.Result.ClassificationMatch == true)
                    show_number = true;
            }



			for (int i = 0; i < this.Result.PcaMatches.Count; i++) 
			{
				this.Document.SetFont(PdfFontTypes.HELVETICA, this.FontHeight, PdfColors.HiperscanRed);
				string evaluation = 0.ToString("f2", this.Culture) + "%";

				int background = PdfColors.White;
				if ( i % 2 == 0)
					background = 0x00DDDDDD;

                if (i == 0 && draw_green)
					this.Document.SetFont(PdfFontTypes.HELVETICA, this.FontHeight, 0x00008000);

                if (i == 0 && show_number)
                    evaluation = first_evaluation;

				NormalizedPCAMatch match = this.Result.PcaMatches[i];

				pos_x = this.GetPosition(0);
				width = this.GetWidth(0);
			 	rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				text = (i + 1).ToString("D2");
				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Center);

				pos_x = this.GetPosition(1);
				width = this.GetWidth(1);
				rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				text = this.ClipText(match.ClassificationName);
				this.Document.XPosition = pos_x + 5;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Left);

				pos_x = this.GetPosition(2);
				width = this.GetWidth(2);
				rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				text = match.SampleId;
				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Center);

				pos_x = this.GetPosition(3);
				width = this.GetWidth(3);
				rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				text = match.Significance.ToString("f4", this.Culture);
				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Center);

				pos_x = this.GetPosition(4);
				width = this.GetWidth(4);
				rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				text = match.Confidence.ToString("f4", this.Culture);
				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Center);

				pos_x = this.GetPosition(5);
				width = this.GetWidth(5);
				rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				text = match.Correlation.ToString("f4", this.Culture);
				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Center);

				pos_x = this.GetPosition(6);
				width = this.GetWidth(6);
				rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				if (match.Distance > 999999)
					text = "> " + 999999.ToString("f1", this.Culture);
				else
					text = (match.Distance).ToString("f1", this.Culture);
				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Center);

				pos_x = this.GetPosition(7);
				width = this.GetWidth(7);
				rect = new PdfRect(pos_x, pos_y, width, height);
				this.Document.AddRectangle(rect, this.LineWidth, PdfColors.Gray, background);
				text = evaluation;
				this.Document.XPosition = pos_x;
				this.Document.AddParagraph(text, width, PdfTextAlignments.Center);

				pos_y += height - this.LineWidth;
				this.Document.YPosition += height - this.LineWidth;
			}
		}

		protected string ClipText(string text)
		{
			double tmp = this.Document.MeasureString(text);
			if (tmp <= this.MaxWidth)
				return text;
			string clipped_text = String.Empty;

			foreach (char c in text)
			{
				string test = clipped_text + c.ToString() + "...";
				tmp = this.Document.MeasureString(test);
				if (tmp > this.MaxWidth)
					break;

				clipped_text += c.ToString();
			}

			return clipped_text + "...";
		}


		protected abstract void CreateContent();
		protected abstract void CreateFooter();


		private void Save()
		{
			this.Document.Save();
		}

		public void Show()
		{
            System.Diagnostics.Process.Start(this.FileName);
		}

		public void Delete()
		{
			try
			{
				File.Delete(this.FileName);
			}
			catch (Exception)
			{
			}
		}

		protected Layout Document 				{get; set;}
		protected  Bitmap HiperscanLogo 		{get; set;}
		protected CultureInfo Culture			{get; set;}
		protected string FileName				{get; set;}
		protected ResultSet Result          	{get; set;}
		protected double LineWidth				{get; set;}
		protected double CellWidth				{get; set;}
		protected double RankingWidth			{get; set;}
		protected double ClassificationWidth	{get; set;}
		protected double FontHeight				{get; set;}
		protected string TestId					{get; set;}
		protected double MaxWidth				{get; set;}
	}
}

