using Docnet.Core;
using Docnet.Core.Exceptions;
using Docnet.Core.Models;
using Docnet.Core.Readers;
using khofmann;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Serialization;

#region Defines

const double MM2IN = 25.4;

#endregion

#region Main Code

if (args.Length == 0)
{
    Console.WriteLine("No PDF specified");
    Console.ReadKey();
    return 1;
}

Console.WriteLine($"Conversion for {args[0]}\n");

Tuple<int, int> size = ReadConfig();
return ReadPDF("MD11_FCOM_vol1.pdf", size.Item1, size.Item2);

#endregion

#region Functions

Tuple<int, int> ReadConfig()
{
    try
    {
        using FileStream stream = new("config.json", FileMode.Open, FileAccess.Read);
        Settings? settings = JsonSerializer.Deserialize<Settings>(stream);
        if (settings != null)
        {
            Paper paper = settings.PaperSizes.First(p => p.Name.Equals(settings.SelectedPaper));
            if (paper != null)
            {
                if (paper.Unit == Unit.Millimetre)
                {
                    int width = Convert.ToInt32(Math.Round(paper.Width / MM2IN * settings.DPI));
                    int height = Convert.ToInt32(Math.Round(paper.Height / MM2IN * settings.DPI));

                    Console.WriteLine($"Using {paper.Name}, {width}mm x {height}mm at {settings.DPI} DPI");

                    if (width < height) return new(width, height);
                    else return new(height, width);
                }
                else
                {
                    int width = Convert.ToInt32(Math.Round(paper.Width * settings.DPI));
                    int height = Convert.ToInt32(Math.Round(paper.Height * settings.DPI));

                    Console.WriteLine($"Using {paper.Name}, ${width}in x {height}in at ${settings.DPI}");

                    if (width < height) return new(width, height);
                    else return new(height, width);
                }
            }
        }
    }
    catch { }

    Console.WriteLine("Using default A4 at 96 dpi\n");

    return new(794, 1122);
}

int ReadPDF(string path, int width, int height)
{
    string outPath = "";
    try
    {
        outPath = Path.GetFileNameWithoutExtension(path);
        Directory.CreateDirectory(outPath);
    }
    catch (Exception e)
    {
        Console.Error.WriteLine("Error creating file directory");
        Console.Error.WriteLine(e.Message);
        Console.ReadKey();
        return 2;
    }

    try
    {
        using IDocReader docReader = DocLib.Instance.GetDocReader(path, new PageDimensions(width, height));
        int pages = docReader.GetPageCount();
        Console.WriteLine($"Converting {pages} pages");

        for (int i = 0; i < pages; i++)
        {
            using (IPageReader pageReader = docReader.GetPageReader(i))
            {
                byte[] rawBytes = pageReader.GetImage();
                int _width = pageReader.GetPageWidth();
                int _height = pageReader.GetPageHeight();

                using (Bitmap doc = new(_width, _height, PixelFormat.Format32bppArgb))
                using (Bitmap bmp = new(_width, _height, PixelFormat.Format32bppArgb))
                {
                    AddBytes(bmp, rawBytes);

                    Graphics g = Graphics.FromImage(doc);
                    g.FillRegion(Brushes.White, new(new Rectangle(0, 0, _width, _height)));
                    g.DrawImage(bmp, new Point(0, 0));
                    g.Save();

                    if (i == 0)
                    {
                        Bitmap thumb = new(doc, new(doc.Width / 10, doc.Height / 10));
                        using (MemoryStream stream = new())
                        {
                            //saving and exporting
                            thumb.Save(stream, ImageFormat.Jpeg);
                            Console.WriteLine($"Ouputing tumbnail");
                            File.WriteAllText($"{outPath}\\thumb.bjpg", Convert.ToBase64String(stream.ToArray()));
                        };
                    }

                    using (MemoryStream stream = new())
                    {
                        //saving and exporting
                        doc.Save(stream, ImageFormat.Jpeg);
                        Console.WriteLine($"Ouputing page {i + 1}");
                        File.WriteAllText($"{outPath}\\{i + 1}.bjpg", Convert.ToBase64String(stream.ToArray()));
                    };
                };
            };
        };
    }
    catch (DocnetLoadDocumentException)
    {
        Console.Error.WriteLine("File is not a PDF");
        Console.ReadKey();
        return 3;
    }

    Console.ReadKey();
    return 0;
}

void AddBytes(Bitmap bmp, byte[] rawBytes)
{
    Rectangle rect = new(0, 0, bmp.Width, bmp.Height);

    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.WriteOnly, bmp.PixelFormat);
    nint pNative = bmpData.Scan0;

    Marshal.Copy(rawBytes, 0, pNative, rawBytes.Length);
    bmp.UnlockBits(bmpData);
}

#endregion

#region classes

namespace khofmann
{
    public class Settings
    {
        public Paper[] PaperSizes { get; set; } = [];
        public string? SelectedPaper { get; set; }
        public int DPI { get; set; }
    }

    public class Paper
    {
        public string Name { get; set; } = "";
        public double Width { get; set; }
        public double Height { get; set; }
        [JsonConverter(typeof(JsonStringEnumConverter))]
        public Unit Unit { get; set; }
    }

    public enum Unit
    {
        Inch,
        Millimetre
    }
}

#endregion