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

using Hiperscan.PdfLib;
using Hiperscan.Spectroscopy.IdentServer;
using Hiperscan.Unix;


namespace Hiperscan.Extensions.ApoIdent.RankingList
{

    public abstract class Generic
    {
        
        public Generic(ApoIdentResultSet 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.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()
        {
            Process.OpenUri(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 ApoIdentResultSet 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; }
    }
}

