
// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2024. 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.IO;
using System.Linq;
using System.Drawing;
using System.Reflection;
using System.Collections.Generic;

using Hiperscan.Unix;
using Hiperscan.PdfLib;
using Hiperscan.Spectroscopy.Processing;
using Hiperscan.Extensions.ReferenceCheck.Control.Evaluation;

namespace Hiperscan.Extensions.ReferenceCheck.Control.Protocol
{
    public class ReferenceCheckProtocol
    {
        private const int TABLE_SPACING = 15;
        private const int FOOT_INFORMATION = 700;

        public ReferenceCheckProtocol(ResultData data, string directory)
        {
            this.Resultdata = data;

            FileIO.CreateDirectory(directory);

            FileIO.GetFiles(directory)
                     .Where(x => Path.GetExtension(x) == ".pdf")
                     .ToList()
                     .ForEach(FileIO.DeleteFile);

            this.Resultdata.ReportNumber = Report.ReportNumber(this.Resultdata.DeviceSerialNumber, this.Resultdata.CheckDate);
            string file_name = Path.Combine(directory, this.Resultdata.ReportNumber);

            this.Resultdata.SessionIDs.ForEach(x =>
            {
                DateTime session_time = this.Resultdata.CalibrationSessions.First(y => y.Id == x).AcquisitionTime;
                string report_number = Report.ReportNumber(this.Resultdata.DeviceSerialNumber, session_time);
                string session_file_name = Path.Combine(directory, report_number);

                string pdf_file = $"{session_file_name}_session_id_{x}.pdf";
                this.CreateProtocolBySessionID(pdf_file, report_number, x);
            });

            this.Resultdata.ReportNumber = file_name;
            IEnumerable<string> pdf_files = FileIO.GetFiles(directory).Where(x => Path.GetExtension(x) == ".pdf");
            Pdf.AppendPdfFiles(pdf_files, $"{file_name}.pdf");

            pdf_files.ToList().ForEach(FileIO.DeleteFile);
        }

        private void CreateProtocolBySessionID(string file_name, string report_number, int session_id)
        {
            this.Document = new Layout(PdfPageSizes.A4_PORTRAIT, file_name);

            this.Document.PDF.SetTitle(String.Format(Catalog.GetString("Reference Check {0}"), this.Resultdata.DisplayName));
            this.Document.PDF.SetSubject(String.Format(Catalog.GetString("This document contains all data of the Reference Check for device {0}."),
                                                       this.Resultdata.DeviceName));
            this.Document.LowerMargin = 50;

            this.TableYFeed = 35;
            this.TableFirstColumnWidth = 220;
            this.TableCaptureFontSize = 11;
            this.TableBorderWidth = 0.5;

            this.CreatePage1(report_number, session_id);
            this.Document.AddNewPage();
            this.CreatePage2(report_number, session_id);

            this.Document.Save();
        }

        private void CreatePage1( string report_number, int session_id)
        {
            this.AddHeaderAndFooter(1, report_number);

            double font_size = 20;
            double max_width = this.Document.InnerPageWidth;
            string text = Catalog.GetString("Reference Check");

            for (; ; )
            {
                this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, font_size, PdfColors.Black);
                this.Document.YPosition = 30;
                this.Document.XPosition = 0;

                if (this.Document.MeasureString(text) > max_width)
                {
                    font_size -= 0.5;
                    continue;
                }

                this.Document.AddParagraph(text);
                break;
            }

            this.SetReportNumber(report_number);

            double current_y_pos = 90;
            this.TableCapture = string.Format(Catalog.GetString("Data:"));
            PdfTableStringCell[,] data = this.CreateApoIdentData();
            current_y_pos = this.CreateTable(data, current_y_pos, current_y_pos + ReferenceCheckProtocol.TABLE_SPACING);
            current_y_pos += this.TableYFeed;

            this.TableCapture = Catalog.GetString("Reference wheel check:");
            data = this.CreateReferenceWheelEvaluationData();
            current_y_pos = this.CreateTable(data, current_y_pos, current_y_pos + ReferenceCheckProtocol.TABLE_SPACING);
            current_y_pos += this.TableYFeed;

            this.TableCapture = Catalog.GetString("Checking Black references:");
            data = this.Resultdata.GetBlackReferenceEvaluationData(session_id);
            current_y_pos = this.CreateTable(data, current_y_pos, current_y_pos + ReferenceCheckProtocol.TABLE_SPACING);
            current_y_pos += this.TableYFeed;

            this.Document.SetFont(PdfFontTypes.HELVETICA, 7, PdfColors.Black);
            this.Document.XPosition = 0;
            this.Document.YPosition = current_y_pos - 20;
            text = Catalog.GetString("* according to the manufacturer's recommendation of HiperScan GmbH");
            this.Document.AddParagraph(text, PdfTextAlignments.Right);

            this.Document.SetFont(PdfFontTypes.HELVETICA, 7, PdfColors.Black);
            this.Document.XPosition = 0;
            this.Document.YPosition = ReferenceCheckProtocol.FOOT_INFORMATION;
            text = Catalog.GetString(@"The results are determined on the basis of several measurements.
The result output is based on the comparison of the stored limit values for each device type.");
            this.Document.AddParagraph(text, PdfTextAlignments.Right);
        }

        private void CreatePage2(string report_number, int session_id)
        {
            this.AddHeaderAndFooter(2, report_number);

            PdfTableStringCell[,] data = null;
            double current_y_pos = 50;
            double max_width = this.Document.InnerPageWidth;

            this.TableCapture = Catalog.GetString("Checking White references:");
            data = this.Resultdata.GetWhiteReferenceEvaluationData(session_id);
            current_y_pos = this.CreateTable(data, current_y_pos, current_y_pos + ReferenceCheckProtocol.TABLE_SPACING);
            current_y_pos += this.TableYFeed;

            if (this.Resultdata.TransflectanceCount > 0)
            {
                this.TableCapture = Catalog.GetString("Checking the transflectance inserts:");
                data = this.Resultdata.GetTransflectanceReferenceEvaluationData(session_id);
                current_y_pos = this.CreateTable(data, current_y_pos, current_y_pos + ReferenceCheckProtocol.TABLE_SPACING);
                current_y_pos += this.TableYFeed;
            }

            if (this.Resultdata.WhitePECount > 0)
            {
                this.TableCapture = Catalog.GetString("Checking White references for measuring small quantities:");
                data = this.Resultdata.GetWhitePEReferenceEvaluationData(session_id);
                current_y_pos = this.CreateTable(data, current_y_pos, current_y_pos + ReferenceCheckProtocol.TABLE_SPACING);
                current_y_pos += this.TableYFeed;
            }

            this.TableCapture = String.Format(Catalog.GetString("Result:"));
            data = this.CreateTotalEvaluationData();
            current_y_pos = this.CreateTable(data, current_y_pos, current_y_pos + ReferenceCheckProtocol.TABLE_SPACING);
            current_y_pos += this.TableYFeed;

            this.Document.SetFont(PdfFontTypes.HELVETICA, 7, PdfColors.Black);
            this.Document.XPosition = 0;
            this.Document.YPosition = current_y_pos - 20;
            string text = Catalog.GetString("This document is computer-generated and therefore does not require any signature.");
            this.Document.AddParagraph(text, PdfTextAlignments.Right);
        }

        private Bitmap LoadLogo()
        {
            Bitmap bitmap = null;
            try
            {
                string destination = this.Resultdata.FirmLogo;
                bitmap = new Bitmap(destination);
            }
            catch (Exception)
            {
                bitmap = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Hiperscan.Extensions.ReferenceCheck.View.Resources.logo_hiperscan.png"));
            }

            return bitmap;
        }

        private void AddHeaderAndFooter(int page_number, string report_number)
        {
            this.Document.AddHeaderBitmap(this.LoadLogo(), new PdfPoint(this.Document.InnerPageWidth - 150, -20), PdfBitmapScales.X, 150);
            Report.AddFooterTo(this.Document, report_number, page_number, 2);
        }

        private PdfTableStringCell[,] CreateApoIdentData()
        {
            PdfTableStringCell[,] table_data = new PdfTableStringCell[6, 2];
            for (int i = 0; i < table_data.GetLength(0); i++)
            {
                for (int j = 0; j < table_data.GetLength(1); j++)
                {
                    table_data[i, j] = new PdfTableStringCell();
                    if (j == 0)
                        table_data[i, j].FontType = PdfFontTypes.HELVETICA_BOLD;
                }
            }

            string[] tool_info = this.Resultdata.DisplayName.Split(',');
            string tool_version = tool_info[0] + tool_info[1];

            table_data[0, 0].Value = Catalog.GetString("Device Serial Number");
            table_data[0, 1].Value = this.Resultdata.DeviceSerialNumber;

            table_data[1, 0].Value = Catalog.GetString("Device type");
            table_data[1, 1].Value = String.Format("{0}", this.Resultdata.DeviceName);

            table_data[2, 0].Value = Catalog.GetString("Spectrometer");
            table_data[2, 1].Value = this.Resultdata.SGSVersion + "  /  " + this.Resultdata.SGSSerialNumber;

            table_data[3, 0].Value = Catalog.GetString("Tool version");
            table_data[3, 1].Value = tool_version;

            table_data[4, 0].Value = Catalog.GetString("Reference Set");
            if (this.Resultdata.ProcessorTypeName == typeof(ReferenceWheelProcessor).Name)
                table_data[4, 1].Value = Catalog.GetString("Reference Wheel Set");
            else if (this.Resultdata.ProcessorTypeName == typeof(ReferenceBlockProcessor).Name)
                table_data[4, 1].Value = Catalog.GetString("Reference Block Set");

            table_data[5, 0].Value = Catalog.GetString("Date of inspection");
            table_data[5, 1].Value = this.Resultdata.CheckDate.ToString("d");

            return table_data;
        }

        private PdfTableStringCell[,] CreateReferenceWheelEvaluationData()
        {
            PdfTableStringCell[,] table_data = new PdfTableStringCell[1, 2];
            for (int i = 0; i < table_data.GetLength(0); i++)
            {
                for (int j = 0; j < table_data.GetLength(1); j++)
                {
                    table_data[i, j] = new PdfTableStringCell();
                    if (j == 0)
                        table_data[i, j].FontType = PdfFontTypes.HELVETICA_BOLD;
                    else
                        table_data[i, j].Align = PdfTextAlignments.Center;
                }
            }

            table_data[0, 0].Value = Catalog.GetString("Reference wheel condition:");
            table_data[0, 1].FontType = PdfFontTypes.HELVETICA_BOLD;

            if (this.Resultdata.ReferenceWheelDegraded == false)
            {
                table_data[0, 1].FontColor = PdfColors.DarkGreen;
                table_data[0, 1].Value = ResultData.PASS;
            }
            else
            {
                table_data[0, 1].FontColor = PdfColors.Red;
                table_data[0, 1].Value = ResultData.FAIL;
            }

            return table_data;
        }

        private PdfTableStringCell[,] CreateTotalEvaluationData()
        {
            PdfTableStringCell[,] table_data = new PdfTableStringCell[2, 2];
            for (int i = 0; i < table_data.GetLength(0); i++)
            {
                for (int j = 0; j < table_data.GetLength(1); j++)
                {
                    table_data[i, j] = new PdfTableStringCell();
                    if (j == 0)
                        table_data[i, j].FontType = PdfFontTypes.HELVETICA_BOLD;
                    else
                        table_data[i, j].Align = PdfTextAlignments.Center;
                }
            }
            table_data[0, 0].Value = Catalog.GetString("Timestamp:");
            table_data[0, 1].Value = this.Resultdata.CheckDate.ToString("d");

            table_data[1, 0].Value = Catalog.GetString("Examiner:");
            table_data[1, 1].Value = this.Resultdata.Creator;

            return table_data;
        }

        private void SetReportNumber(string report_number)
        {
            this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, 10, PdfColors.Black);
            this.Document.XPosition = 0;
            this.Document.YPosition = 60;
            string text = String.Format(Catalog.GetString("Report number"));
            this.Document.AddParagraph(text);

            this.Document.SetFont(PdfFontTypes.HELVETICA, 10, PdfColors.Black);
            this.Document.XPosition = 90;
            text = String.Format(report_number);
            this.Document.AddParagraph(text);
        }

        private double CreateTable(PdfTableStringCell[,] data, double y_pos_header, double y_pos_table)
        {
            if (String.IsNullOrEmpty(this.TableCapture) == false)
            {
                this.Document.SetFont(PdfFontTypes.HELVETICA_BOLD, this.TableCaptureFontSize, PdfColors.Black);
                this.Document.YPosition = y_pos_header;
                this.Document.XPosition = 0;
                this.Document.AddParagraph(this.TableCapture);
            }

            if (data == null)
                return y_pos_table;

            int columns = data.GetLength(1);
            double[] column_widths = new double[columns];
            double other_column_widths = this.Document.InnerPageWidth - this.TableFirstColumnWidth;
            other_column_widths += (columns - 1) * this.TableBorderWidth;
            other_column_widths /= (columns - 1);
            column_widths[0] = this.TableFirstColumnWidth;
            for (int i = 1; i < columns; i++)
                column_widths[i] = other_column_widths;

            PdfTable table = new PdfTable(this.Document, data, column_widths, 5, 5, this.TableBorderWidth);
            y_pos_table = table.Draw(0, y_pos_table, PdfColors.Black);
            return y_pos_table;
        }

        public void Show(string directory)
        {
            string file_name = Path.Combine(directory, this.Resultdata.ReportNumber + ".pdf");
            Process.OpenUri("file://" + file_name.Replace("\\", "/"));
        }

        private Layout Document { get; set; }
        private int TableYFeed { get; set; }
        private int TableFirstColumnWidth { get; set; }
        private int TableCaptureFontSize { get; set; }
        private double TableBorderWidth { get; set; }
        internal ResultData Resultdata { get; }
        public string TableCapture { get; private set; }
        private bool PageTwoNeeded { get; set; } = false;
    }
}
