// 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 Hiperscan.GtkExtensions.Compiler;
using Hiperscan.GtkExtensions.Printing;
using Hiperscan.Unix;
using Hiperscan.ApoIdentCore.Misc;
using Hiperscan.ApoIdentCommon.ApoConfig;
using Hiperscan.ApoIdentCommon.ApoConfig.Enums;

namespace Hiperscan.ApoIdentCore.Reporting.LabelPrint
{
    public abstract partial class LabelPrinter
    {
        public static readonly string USER_DEFINED_SIZE_NAME = Catalog.GetString("User defined");
        public const double USER_DEFINED_DEFAULT_WIDTH = 50;
        public const double USER_DEFINED_DEFAULT_HEIGHT = 30;

        protected LabelPrinter(LabelPrinterSettings settings)
        {
            this.Settings = settings;
        }

        private void PrintLabel(LabelInfo label_info)
        {
            if (this.Settings.HasPrinter == false)
                throw new Exception(Catalog.GetString("Printer settings are invalid."));

            this.LabelInfo = label_info;

            GraphicsSize page_size = new GraphicsSize()
            {
                Width = this.Settings.PaperWidthMM,
                Height = this.Settings.PaperHeightMM,
            };

            GraphicsPoint page_offset = new GraphicsPoint()
            {
                X = this.Settings.X_IndentMM,
                Y = this.Settings.Y_IndentMM,
            };

            int orientation = ((int)this.Settings.Orientation) * 90;

            Printing print_job = null;



            if (EnvironmentInfo.OS == Misc.OperatingSystem.Linux)
                print_job = new LinuxPrinting(this.Settings.PrinterName,
                                              this.LabelInfo.PrintName,
                                              page_size, page_offset,
                                              orientation,
                                              this.CreateLabelGraphics);
            else
                print_job = new WindowsPrinting(this.Settings.PrinterName,
                                                this.LabelInfo.PrintName,
                                                page_size,
                                                this.CreateLabelGraphics);

            print_job.PrintData();
        }

        protected abstract GraphicsObject CreateLabelGraphics(GraphicsRectangle area, GenericGraphicCompiler compiler);

        protected void CalcSheetPosition(GraphicsRectangle area)
        {
            this.LabelWidth = 480;
            this.LabelHeight = 240;

            this.OffsetX = this.Settings.X_IndentMM / 25.4 * this.Compiler.ReferenceDpi;
            this.OffsetY = this.Settings.Y_IndentMM / 25.4 * this.Compiler.ReferenceDpi;

            this.ScaleFactor = this.Settings.Scale;
            this.Rotation = 0;
            this.TranslationX = 0;
            this.TranslationY = 0;

            this.DrawOffsetX = 0.025 * area.Width;
            this.DrawOffsetY = 0.025 * area.Height;

            this.DrawWidth = 0.95 * area.Width;
            this.DrawHeight = 0.95 * area.Height;

            double rescale = 0.92;
            double reposition_y = 0.01;

            switch (this.Settings.Orientation)
            {
                case PrinterOrientation.Degree_0:
                    this.Rotation = 0;
                    this.ScaleFactor *= Math.Min(this.DrawWidth / this.LabelWidth, this.DrawHeight / this.LabelHeight) * rescale;
                    this.TranslationX = 0;
                    this.TranslationY = this.DrawHeight * reposition_y;
                    break;

                case PrinterOrientation.Degree_90:
                    this.Rotation = 90;
                    this.ScaleFactor *= Math.Min(this.DrawWidth / this.LabelHeight, this.DrawHeight / this.LabelWidth) * rescale;
                    this.TranslationX = 0;
                    this.TranslationY = (-1 + reposition_y) * this.DrawWidth;
                    break;

                case PrinterOrientation.Degree_180:
                    this.Rotation = 180;
                    this.ScaleFactor *= Math.Min(this.DrawWidth / this.LabelWidth, this.DrawHeight / this.LabelHeight) * rescale;
                    this.TranslationX = (-1 + reposition_y) * this.DrawWidth;
                    this.TranslationY = (-1 + reposition_y) * this.DrawHeight;
                    break;

                case PrinterOrientation.Degree_270:
                    this.Rotation = 270;
                    this.ScaleFactor *= Math.Min(this.DrawWidth / this.LabelHeight, this.DrawHeight / this.LabelWidth) * rescale;
                    this.TranslationX = (-1 + reposition_y) * this.DrawHeight;
                    this.TranslationY = 0;
                    break;

                default:
                    break;
            }
        }

        protected void CreateMainCanvas(GraphicsRectangle area)
        {
            this.DrawBugFixPoint(area);

            this.MainCanvas = new GraphicsGroup();
            //First move to available drawing area
            this.MainCanvas.TransformGroup.Add(new GraphicsTranslate(this.DrawOffsetX, this.DrawOffsetY));
            //Second rotate the available drawing area
            this.MainCanvas.TransformGroup.Add(new GraphicsRotate(this.Rotation));
            //Third  move available drawing area back in visible range
            this.MainCanvas.TransformGroup.Add(new GraphicsTranslate(this.TranslationX, this.TranslationY));
            //Fourth adapt the available drawing area to the given label area
            this.MainCanvas.TransformGroup.Add(new GraphicsScale(this.ScaleFactor, this.ScaleFactor));
            //Fifth move to the userdefined offsets
            this.MainCanvas.TransformGroup.Add(new GraphicsTranslate(this.OffsetX / this.ScaleFactor, this.OffsetY / this.ScaleFactor));
            this.VisualRootElement.Children.Add(this.MainCanvas);

            if (this.Debug == false)
                return;

            GraphicsShapeRectangle test_rectangle = new GraphicsShapeRectangle(0, 0, this.LabelWidth, this.LabelHeight);
            test_rectangle.Brush = new GraphicsSolidColorBrush(new GraphicsColor(0, 1, 0, 0.2));
            test_rectangle.Pen = new GraphicsPen(new GraphicsColor(0, 0, 0, 0.3));
            test_rectangle.Pen.LineWidth = 3;
            this.MainCanvas.Children.Add(test_rectangle);
        }

        protected void DrawBugFixPoint(GraphicsRectangle area)
        {
            //is necessary for endless paper in linux
            GraphicsShapeCircle circle = new GraphicsShapeCircle()
            {
                Radius = this.Debug ? 10 : 0.5,
                X = area.Width / 2,
                Y = area.Height,
                Brush = new GraphicsSolidColorBrush(GraphicsColors.Black),
            };
            this.VisualRootElement.Children.Add(circle);
        }

        protected void DrawBackground()
        {
            if (this.Debug == false)
                return;

            GraphicsShapeRectangle background = new GraphicsShapeRectangle(this.DrawOffsetX, this.DrawOffsetY, this.DrawWidth, this.DrawHeight);
            background.Brush = new GraphicsSolidColorBrush(new GraphicsColor(0, 0, 1, 0.2));
            background.Pen = new GraphicsPen(new GraphicsColor(0, 0, 1));
            this.VisualRootElement.Children.Add(background);

            GraphicsShapeLine line1 = new GraphicsShapeLine(this.DrawOffsetX, this.DrawOffsetY, this.DrawWidth + this.DrawOffsetX, this.DrawHeight + this.DrawOffsetY);
            line1.Pen = new GraphicsPen(GraphicsColors.Red, 2);
            this.VisualRootElement.Children.Add(line1);

            GraphicsShapeLine line2 = new GraphicsShapeLine(this.DrawOffsetX, this.DrawOffsetY + this.DrawHeight, this.DrawWidth + this.DrawOffsetX, this.DrawOffsetY);
            line2.Pen = new GraphicsPen(GraphicsColors.Red, 1);
            this.VisualRootElement.Children.Add(line2);
        }

        protected void DrawText(string text, double x_pos, double y_pos, GraphicsFont font, double width = Int32.MaxValue)
        {
            GraphicsText graphics_text = new GraphicsText()
            {
                Brush = this.Color,
                Font = font,
                Position = new GraphicsPoint(x_pos, y_pos),
                Text = text,
                MaxWidth = width,
            };

            this.MainCanvas.Children.Add(graphics_text);
        }

        protected double MeasureText(List<string> values, GraphicsFont font)
        {
            GraphicsText text = new GraphicsText();
            text.Font = font;

            double width = 0;

            for (int i = 0; i < values.Count; i++)
            {
                text.Text = values[i];
                double tmp = this.Compiler.MeasureText(text).Width;
                if (tmp > width)
                    width = tmp;
            }

            return width;
        }


        protected void DrawHorizentalLine(double x_pos, double y_pos, double width, double line_width)
        {
            GraphicsShapeLine line = new GraphicsShapeLine(x_pos, y_pos, x_pos + width, y_pos);
            line.Pen = new GraphicsPen(GraphicsColors.Black, line_width);
            this.MainCanvas.Children.Add(line);
        }

        private GraphicsGroup visual_root_element = null;
        protected GraphicsGroup VisualRootElement
        {
            get
            {
                if (this.visual_root_element == null)
                    visual_root_element = new GraphicsGroup();

                return this.visual_root_element;
            }
        }

        private GraphicsFont bold_font = null;
        protected GraphicsFont BoldFont
        {
            get
            {
                if (this.bold_font == null)
                {
                    this.bold_font = new GraphicsFont()
                    {
                        FontName = "Helvetica",
                        FontSize = this.FontSize,
                        FontWeight = GraphicsFontWeights.Bold,
                    };
                }

                return this.bold_font;
            }
        }

        private GraphicsFont normal_font = null;
        protected GraphicsFont NormalFont
        {
            get
            {
                if (this.normal_font == null)
                {
                    this.normal_font = new GraphicsFont()
                    {
                        FontName = "Helvetica",
                        FontSize = this.FontSize,
                        FontWeight = GraphicsFontWeights.Normal,
                    };
                }

                return this.normal_font;
            }
        }

        private GraphicsFont small_font = null;
        protected GraphicsFont SmallFont
        {
            get
            {
                if (this.small_font == null)
                {
                    this.small_font = new GraphicsFont()
                    {
                        FontName = "Helvetica",
                        FontSize = this.SmallFontSize,
                        FontWeight = GraphicsFontWeights.Normal,
                    };
                }

                return this.small_font;
            }
        }

        private GraphicsSolidColorBrush color = null;
        protected GraphicsSolidColorBrush Color
        {
            get
            {
                if (this.color == null)
                    this.color = new GraphicsSolidColorBrush(GraphicsColors.Black);

                return this.color;
            }
        }

        protected GraphicsGroup MainCanvas { get; set; }
        protected GenericGraphicCompiler Compiler { get; set; }


        protected double LabelWidth { get; set; }
        protected double LabelHeight { get; set; }
        protected double OffsetX { get; set; }
        protected double OffsetY { get; set; }
        protected double ScaleFactor { get; set; }
        protected double Rotation { get; set; }
        protected double TranslationX { get; set; }
        protected double TranslationY { get; set; }
        protected double DrawOffsetX { get; set; }
        protected double DrawOffsetY { get; set; }
        protected double DrawWidth { get; set; }
        protected double DrawHeight { get; set; }

        protected double FontSize { get; set; }
        protected double SmallFontSize { get; set; }
        protected double LineWidth { get; set; }

        protected LabelInfo LabelInfo { get; set; }
        protected LabelPrinterSettings Settings { get; set; }

        private bool Debug { get; set; } = false;
    }

}

