// Created with MonoDevelop
//
//    Copyright (C) 2017 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 Hiperscan.PdfLib;
using System.Reflection;
using System.Collections.Generic;
using System.Drawing;
using System.IO;

using Hiperscan.Unix;


namespace Hiperscan.Pdf.SubstanceOverview
{

    public enum OverviewErrorType
    {
        None,
        NoAvailablePdf,
        NotMeasurablePdf,
        NoNewPdf,
    }

    public class OverviewPdfCreator
    {
        public static void Create(PdfModelStructure list, string file_name, OverviewErrorType error_type)
        {
            var pdf_creator = new OverviewPdfCreator();
            pdf_creator.Run(list, file_name, error_type);
        }

        private OverviewPdfCreator()
        {
        }

        public void Run(PdfModelStructure list, string file_name, OverviewErrorType error_type)
        {
            this.Document = new Layout(PdfPageSizes.A4_LANDSCAPE, file_name);
            this.Document.PDF.SetTitle(Catalog.GetString("Substance overview"));
            this.Document.PDF.SetSubject(Catalog.GetString("This document contains a list of substances."));

            this.Document.LowerMargin = 50;

            Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("SubstanceOverview.Resources.Apo_Ident_Logo.png");
            this.HiperscanLogo = new Bitmap(s);


            if (list == null || list.IsEmpty == true)
            {
                if (error_type != OverviewErrorType.None)
                    this.CreateSorryPage(error_type);
            }
            else
            {
                this.Version = list.Version;

                this.ModelStructure = list;

                this.Document.SimulateDrawing = true;
                {
                    this.CurrentPageNumber = 1;
                    this.CreateListOfContents();
                    this.CreateContents();
                    this.TotalPageNumber = this.CurrentPageNumber;
                }

                this.Document.SimulateDrawing = false;
                {
                    this.CurrentPageNumber = 1;
                    this.CreateListOfContents();
                    this.CreateContents();
                }

            }

            this.Document.Save();
        }

        private int CurrentPageNumber { get; set; }
        private int TotalPageNumber { get; set; }
        private Layout Document { get; set; }
        private string Version { get; set; }
        private Bitmap HiperscanLogo { get; set; }
        private PdfModelStructure ModelStructure { get; set; }


        #region List of contents
        private void CreateListOfContents()
        {
            this.Document.SetFont(PdfFontTypes.HELVETICA, 12, PdfColors.Black);
            this.LOC_PageEntryWidth = this.Document.MeasureString(" " + Catalog.GetString("Page") + " ");
            this.LOC_PageEntryFeed = 50;
            this.LOC_PageEntryX = 620;
            this.LOC_PageEntryY = 150;
            this.LOC_EntriesPerPage = new List<int>();
            int start_index = 0;
            int old_index = 0;

            for (; ; )
            {
                start_index = this.CreateOnePageForListOfContents(start_index);

                if (start_index == -1)
                {
                    this.LOC_EntriesPerPage.Add(this.ModelStructure.ModelClasses.Count - old_index);
                    break;
                }
                else
                {
                    this.LOC_EntriesPerPage.Add(start_index - old_index);
                    this.Document.AddNewPage();
                    this.CurrentPageNumber++;
                }
                old_index = start_index;
            }

            if (this.CurrentPageNumber % 2 == 1)
            {
                this.Document.AddNewPage();
                this.CurrentPageNumber++;
            }
        }


        private int CreateOnePageForListOfContents(int start_index)
        {
            double tmp;
            PdfRect rect = new PdfRect(0, 0, 0, 0);
            double left = 60;
            double right = this.Document.InnerPageWidth - 80;

            //Create firm address
            this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 10, PdfColors.Black);
            this.Document.YPosition = 0;
            this.Document.XPosition = 0;
            this.Document.AddHeader("HiperScan GmbH");

            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"));

            //Content header
            this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 14, PdfColors.White);
            tmp = this.Document.MeasureString(this.ModelStructure.ListOfContentsHeader);
            tmp += 20;
            rect.X = left;
            rect.Y = 70;
            rect.Width = tmp;
            rect.Height = 24;
            this.Document.AddRectangle(rect, PdfColors.HiperscanRed);
            this.Document.YPosition = 73;
            this.Document.XPosition = left;
            this.Document.AddParagraph(this.ModelStructure.ListOfContentsHeader,
                                                     tmp, PdfTextAlignments.Center);
            //Logo
            this.Document.AddBitmap(this.HiperscanLogo, new PdfPoint(right - 105, 55), PdfBitmapScales.X, 105);
            this.Document.AddHorizontalLine(left, 94.5, right - left, 1);


            //Page entry
            this.Document.SetFont(PdfFontTypes.HELVETICA, 10, PdfColors.Black);
            this.Document.XPosition = this.LOC_PageEntryX;
            this.Document.YPosition = 110;
            this.Document.AddParagraph(" " + Catalog.GetString("Page") + " ", this.LOC_PageEntryWidth, PdfTextAlignments.Center);

            //footer
            this.Document.XPosition = 0;
            this.Document.YPosition = this.Document.InnerPageHeight + 5;
            this.Document.SetFont(PdfFontTypes.HELVETICA, 8, PdfColors.Black);
            this.Document.AddFooter("\u00A9 Hiperscan GmbH");


            //page entry
            if (this.Document.SimulateDrawing == false)
            {
                this.Document.SetFont(PdfFontTypes.HELVETICA, 8, PdfColors.Black);
                this.Document.XPosition = 0;
                this.Document.YPosition = this.Document.InnerPageHeight + 20;
                string text = string.Format(Catalog.GetString("Page {0} of {1}"), this.CurrentPageNumber, this.TotalPageNumber);
                this.Document.AddFooter(text, PdfTextAlignments.Right);
            }


            //model class entries
            tmp = this.LOC_PageEntryY;

            for (int i = start_index; i < this.ModelStructure.ModelClasses.Count; i++)
            {
                if (this.Document.AddHorizontalLine(left, tmp + 20, right - left, 0.5) == false)
                    return i;

                //single model class entry
                this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 11, PdfColors.HiperscanRed);
                this.Document.YPosition = tmp;
                this.Document.XPosition = left;

                this.Document.AddParagraph(this.ModelStructure.ModelClasses[i].ModelClassName);

                //single model class entry page number
                if (this.Document.SimulateDrawing == false)
                {
                    this.Document.SetFont(PdfFontTypes.HELVETICA, 11, PdfColors.Black);
                    this.Document.XPosition = this.LOC_PageEntryX;
                    string page_number = this.ModelStructure.ModelClasses[i].PageNumber.ToString();
                    this.Document.AddParagraph(page_number, this.LOC_PageEntryWidth, PdfTextAlignments.Center);
                }

                tmp += this.LOC_PageEntryFeed;
            }
            return -1;
        }

        private double LOC_PageEntryWidth { get; set; }
        private double LOC_PageEntryFeed { get; set; }
        private double LOC_PageEntryX { get; set; }
        private double LOC_PageEntryY { get; set; }
        private List<int> LOC_EntriesPerPage { get; set; }
        #endregion List of contents



        #region table pages
        private void CreateContents()
        {
            this.Document.SetFont(PdfFontTypes.HELVETICA, 12, PdfColors.Black);
            this.SubstanceGroupNameWidth = this.Document.MeasureString(Catalog.GetString("Classifier:") + " ") + 5;

            this.Document.SetFont(PdfFontTypes.HELVETICA, 10, PdfColors.Black);
            this.VersionWidth = this.Document.MeasureString(this.Version) + 30;

            this.TableHeaderColor = 0x00B33333;
            this.TableRowColor = 0x00FFCCCC;
            this.TableLineColor = 0X00E68080;

            this.LineWidth = 1;
            this.CellSpace = this.LineWidth * 3;


            this.NamesSummaryColumnWidth = 0;
            if (this.ModelStructure.ShowClassificationColumn == true)
                this.NamesSummaryColumnWidth = 150 + 2 * this.CellSpace;

            if (this.ModelStructure.ShowClassificationColumn == true)
            {
                this.NamesEntryColumnWidth = (this.Document.InnerPageWidth -
                                                            this.NamesSummaryColumnWidth +
                                                            2 * this.LineWidth) / 2;
            }
            else
            {
                this.NamesEntryColumnWidth = (this.Document.InnerPageWidth +
                                                            this.LineWidth) / 2;
            }


            foreach (PdfModelClassEntry model_class in this.ModelStructure.ModelClasses)
            {
                this.CurrentSubstances = model_class.Substances;
                if (this.Document.SimulateDrawing == true)
                    model_class.PageNumber = this.CurrentPageNumber + 1;

                int start_index = 0;
                for (; ; )
                {

                    this.Document.AddNewPage();
                    this.CurrentPageNumber++;
                    start_index = this.CreateOnePageForEnumerations(start_index, model_class.ModelClassName);
                    if (start_index == -1)
                        break;
                }
            }
        }


        private int CreateOnePageForEnumerations(int start_index, string class_name)
        {
            double tmp1, tmp2, tmp3;
            double top;
            PdfRect rect;
            string text;
            int row = 1;


            this.Document.SetFont(PdfFontTypes.HELVETICA, 10, PdfColors.Black);
            this.Document.YPosition = 0;
            this.Document.XPosition = 0;
            this.Document.AddHeader("HiperScan GmbH");

            this.Document.SetFont(PdfFontTypes.HELVETICA, 12, PdfColors.Black);
            this.Document.YPosition = 50;
            this.Document.AddParagraph(Catalog.GetString("Classifier:") + " ");
            this.Document.XPosition = this.SubstanceGroupNameWidth;
            this.Document.AddParagraph(class_name);

            top = this.Document.ParagraphBoundingBox.Height + this.Document.YPosition;

            this.Document.SetFont(PdfFontTypes.HELVETICA, 8, PdfColors.Black);
            this.Document.XPosition = 0;
            this.Document.YPosition = this.Document.InnerPageHeight + 20;
            this.Document.AddFooter(this.Version);

            tmp1 = this.Document.InnerPageWidth - 2 * this.VersionWidth;
            this.Document.XPosition = this.VersionWidth;
            this.Document.AddFooter(class_name, tmp1, PdfTextAlignments.Center);

            this.Document.AddHeaderBitmap(this.HiperscanLogo, new PdfPoint(this.Document.InnerPageWidth - 105, -15), PdfBitmapScales.X, 105);

            this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 10, PdfColors.White);
            top += 10;

            //German column
            rect = new PdfRect(0, top, this.NamesEntryColumnWidth, 15);
            this.Document.AddRectangle(rect, this.LineWidth,
                                                     this.TableLineColor, this.TableHeaderColor);


            text = Catalog.GetString("Substance name");
            foreach (string producer in this.PinYinClassifiers)
            {
                string lower_group_name = class_name.ToLower();
                if (lower_group_name.Contains(producer) == true)
                {
                    text = Catalog.GetString("Pin Yin");
                    break;
                }
            }

            this.Document.YPosition = top + 1;
            this.Document.XPosition = 0;
            this.Document.AddParagraph(text, rect.Width, PdfTextAlignments.Center);


            //Latin Column
            rect = new PdfRect(this.NamesEntryColumnWidth - this.LineWidth,
                               top, this.NamesEntryColumnWidth, 15);
            this.Document.AddRectangle(rect, this.LineWidth, this.TableLineColor, this.TableHeaderColor);
            this.Document.XPosition = rect.X;
            this.Document.YPosition = top + 1;
            this.Document.AddParagraph(Catalog.GetString("Latin name"), rect.Width, PdfTextAlignments.Center);

            //Summary Column
            if (this.ModelStructure.ShowClassificationColumn == true)
            {
                rect = new PdfRect((this.NamesEntryColumnWidth - this.LineWidth) * 2,
                                   top, this.NamesSummaryColumnWidth, 15);
                this.Document.AddRectangle(rect, this.LineWidth,
                                           this.TableLineColor, this.TableHeaderColor);
                this.Document.XPosition = rect.X;
                this.Document.YPosition = top + 1;
                this.Document.AddParagraph(Catalog.GetString("Classification"), rect.Width, PdfTextAlignments.Center);
            }

            //page entry
            if (this.Document.SimulateDrawing == false)
            {
                this.Document.SetFont(PdfFontTypes.HELVETICA, 8, PdfColors.Black);
                this.Document.XPosition = 0;
                this.Document.YPosition = this.Document.InnerPageHeight + 20;
                string page_number = string.Format(Catalog.GetString("Page {0} of {1}"), this.CurrentPageNumber, this.TotalPageNumber);
                this.Document.AddFooter(page_number, PdfTextAlignments.Right);
            }



            top += 14;
            this.Document.SetFont(PdfFontTypes.HELVETICA, 10, PdfColors.Black);


            for (int i = start_index; i < this.CurrentSubstances.Count; i++)
            {
                string default_entry = string.Join("\n", this.CurrentSubstances[i].DefaultNames.ToArray());
                string latin_entry = string.Join("\n", this.CurrentSubstances[i].LatinNames.ToArray());
                string summary_entry = string.Empty;
                if (this.ModelStructure.ShowClassificationColumn == true)
                {
                    if (this.CurrentSubstances[i].IsSingleClassiification == false)
                    {
                        summary_entry = this.CurrentSubstances[i].ClassificationName;
                        summary_entry += string.Format(" " + Catalog.GetString("({0} substances)"), this.CurrentSubstances[i].NumberOfClassificationMembers);
                    }
                }

                int color = row % 2 == 1 ? this.TableRowColor : PdfColors.White;

                //cell widths
                double substance_paragraph_width = this.NamesEntryColumnWidth - 2 * this.CellSpace;
                double summary_paragraph_width = this.NamesSummaryColumnWidth - 2 * this.CellSpace;
                //cell heights
                tmp1 = this.Document.MeasureParagraph(default_entry, substance_paragraph_width).Height;
                tmp2 = this.Document.MeasureParagraph(latin_entry, substance_paragraph_width).Height;
                tmp3 = 0;
                if (this.ModelStructure.ShowClassificationColumn == true)
                    tmp3 = this.Document.MeasureParagraph(summary_entry, summary_paragraph_width).Height;

                double paragraph_height = tmp1 > tmp2 ? tmp1 : tmp2;
                paragraph_height = paragraph_height > tmp3 ? paragraph_height : tmp3;
                paragraph_height += 5;

                //create first column
                rect = new PdfRect(0, top, this.NamesEntryColumnWidth, paragraph_height);
                if (this.Document.AddRectangle(rect, this.LineWidth,
                                                             this.TableLineColor, color) == false)
                    return i;

                this.Document.XPosition = this.CellSpace;
                this.Document.YPosition = top + 0.5;
                this.Document.AddParagraph(default_entry, substance_paragraph_width);


                //create second column
                rect = new PdfRect(this.NamesEntryColumnWidth - this.LineWidth,
                                   top,
                                   this.NamesEntryColumnWidth, paragraph_height);
                this.Document.AddRectangle(rect, this.LineWidth,
                                                         this.TableLineColor, color);

                this.Document.XPosition = this.CellSpace +
                    this.NamesEntryColumnWidth - this.LineWidth;
                this.Document.AddParagraph(latin_entry, substance_paragraph_width);

                //create third column
                if (this.ModelStructure.ShowClassificationColumn == true)
                {
                    rect = new PdfRect((this.NamesEntryColumnWidth - this.LineWidth) * 2,
                                        top,
                                       this.NamesSummaryColumnWidth, paragraph_height);

                    this.Document.AddRectangle(rect, this.LineWidth,
                                               this.TableLineColor, color);

                    this.Document.XPosition = this.CellSpace +
                        (this.NamesEntryColumnWidth - this.LineWidth) * 2;
                    this.Document.AddParagraph(summary_entry, summary_paragraph_width);
                }

                top += paragraph_height - this.LineWidth;
                row++;
            }


            return -1;
        }

        private List<string> PinYinClassifiers = new List<string>(new string[]
                                                                {
                "herbasinica",
                "phytocomm",
                "chinamedica",
                                                                });

        private double SubstanceGroupNameWidth { get; set; }
        private double VersionWidth { get; set; }
        private List<PdfSubstanceEntry> CurrentSubstances { get; set; }
        private int TableHeaderColor { get; set; }
        private int TableRowColor { get; set; }
        private int TableLineColor { get; set; }
        //private List<int> ChapterIndices                   {get;set;}

        private double NamesSummaryColumnWidth { get; set; }
        private double NamesEntryColumnWidth { get; set; }
        private double CellSpace { get; set; }
        private double LineWidth { get; set; }
        #endregion table pages

        #region sorry page
        private void CreateSorryPage(OverviewErrorType error_type)
        {
            string text = string.Empty;

            //Create firm address
            this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 10, PdfColors.Black);
            this.Document.YPosition = 55;
            this.Document.XPosition = 0;
            this.Document.AddHeader("HiperScan GmbH");

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


            this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 20, PdfColors.Black);
            switch (error_type)
            {
                case OverviewErrorType.NoAvailablePdf:
                    text = Catalog.GetString("List of available substances cannot be shown.");
                    break;

                case OverviewErrorType.NoNewPdf:
                    text = Catalog.GetString("List of added substances cannot be shown.");
                    break;

                case OverviewErrorType.NotMeasurablePdf:
                    text = Catalog.GetString("List of not measurable substances cannot be shown.");
                    break;

                default:
                    break;
            }

            this.Document.YPosition = 150;
            this.Document.AddParagraph(text, PdfTextAlignments.Center);

            this.Document.AddBitmap(this.HiperscanLogo, new PdfPoint(this.Document.InnerPageWidth - 105, 55), PdfBitmapScales.X, 105);

            Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("SubstanceOverview.Resources.sorry.bmp");
            Bitmap bitmap = new Bitmap(s);
            this.Document.AddBitmap(bitmap, new PdfPoint(0.3 * this.Document.InnerPageWidth,
                                                         0.4 * this.Document.InnerPageHeight),
                                                        PdfBitmapScales.X, 0.3 * this.Document.InnerPageWidth);


            this.Document.SetFont(PdfFontTypes.HELVETICA, 12, PdfColors.Black);
            this.Document.YPosition = 0.875 * this.Document.InnerPageHeight;
            this.Document.XPosition = 150;
            text = Catalog.GetString("Possible reasons for this error are:");
            this.Document.AddParagraph(text);

            this.Document.XPosition = 170;
            this.Document.YPosition += 16;

            switch (error_type)
            {
                case OverviewErrorType.NoAvailablePdf:
                    text = Catalog.GetString("- No substance groups are defined for this device.");
                    this.Document.AddParagraph(text);
                    break;

                case OverviewErrorType.NoNewPdf:
                    text = Catalog.GetString("- No substances have been added since last Ident Module update.");
                    this.Document.AddParagraph(text);
                    break;

                case OverviewErrorType.NotMeasurablePdf:
                    text = Catalog.GetString("- There aren't any not measurable substances.");
                    this.Document.AddParagraph(text);
                    break;
            }
        }
        #endregion sorry page
    }
}

