using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Printing;
using System.Runtime.InteropServices;

namespace Elwig.Helpers.Printing {
    public class PdfPrintDocument : PrintDocument {

        private readonly IntPtr _handle;
        private readonly int _pageCount;
        private readonly double _dpi;

        private int _currentPage;

        public PdfPrintDocument(string path, string? password = null, double dpi = 300.0) :
            base() {
            _handle = PdfiumNative.FPDF_LoadDocument(path, password);
            _pageCount = PdfiumNative.FPDF_GetPageCount(_handle);
            _dpi = dpi;
        }

        protected override void Dispose(bool disposing) {
            PdfiumNative.FPDF_CloseDocument(_handle);
            base.Dispose(disposing);
        }

        protected override void OnBeginPrint(PrintEventArgs evt) {
            _currentPage = (PrinterSettings.FromPage != 0) ? (PrinterSettings.FromPage - 1) : 0;
            base.OnBeginPrint(evt);
        }

        protected override void OnPrintPage(PrintPageEventArgs evt) {
            if (_currentPage < _pageCount) {
                IntPtr page = PdfiumNative.FPDF_LoadPage(_handle, _currentPage);
                double width = PdfiumNative.FPDF_GetPageWidth(page);
                double height = PdfiumNative.FPDF_GetPageHeight(page);
                int pixelWidth = (int)(width / 72.0 * _dpi);
                int pixelHeight = (int)(height / 72.0 * _dpi);

                IntPtr bitmap = PdfiumNative.FPDFBitmap_Create(pixelWidth, pixelHeight, 1);
                PdfiumNative.FPDF_RenderPageBitmap(bitmap, page, 0, 0, pixelWidth, pixelHeight, 0, 0);

                IntPtr buffer = PdfiumNative.FPDFBitmap_GetBuffer(bitmap);
                int stride = PdfiumNative.FPDFBitmap_GetStride(bitmap);
                using (var bmp = new Bitmap(pixelWidth, pixelHeight, stride, PixelFormat.Format32bppArgb, buffer)) {
                    evt.Graphics?.DrawImage(bmp, evt.PageBounds);
                }
                _currentPage++;

                PdfiumNative.FPDFBitmap_Destroy(bitmap);
                PdfiumNative.FPDF_ClosePage(page);
            }
            evt.HasMorePages = _currentPage < ((PrinterSettings.ToPage == 0) ? _pageCount : Math.Min(PrinterSettings.ToPage, _pageCount));
            base.OnPrintPage(evt);
        }
    }

    internal partial class PdfiumNative {
        [LibraryImport("pdfium.dll")]
        public static partial void FPDF_InitLibrary();

        [LibraryImport("pdfium.dll")]
        public static partial void FPDF_DestroyLibrary();

        [LibraryImport("pdfium.dll", StringMarshalling = StringMarshalling.Utf8)]
        public static partial IntPtr FPDF_LoadDocument(string filePath, string? password);

        [LibraryImport("pdfium.dll")]
        public static partial void FPDF_CloseDocument(IntPtr document);

        [LibraryImport("pdfium.dll")]
        public static partial int FPDF_GetPageCount(IntPtr document);

        [LibraryImport("pdfium.dll")]
        public static partial IntPtr FPDF_LoadPage(IntPtr document, int pageIndex);

        [LibraryImport("pdfium.dll")]
        public static partial void FPDF_ClosePage(IntPtr page);

        [LibraryImport("pdfium.dll")]
        public static partial double FPDF_GetPageWidth(IntPtr page);

        [LibraryImport("pdfium.dll")]
        public static partial double FPDF_GetPageHeight(IntPtr page);

        [LibraryImport("pdfium.dll")]
        public static partial IntPtr FPDFBitmap_Create(int width, int height, int alpha);

        [LibraryImport("pdfium.dll")]
        public static partial void FPDFBitmap_Destroy(IntPtr bitmap);

        [LibraryImport("pdfium.dll")]
        public static partial IntPtr FPDFBitmap_GetBuffer(IntPtr bitmap);

        [LibraryImport("pdfium.dll")]
        public static partial int FPDFBitmap_GetStride(IntPtr bitmap);

        [LibraryImport("pdfium.dll")]
        public static partial void FPDF_RenderPageBitmap(IntPtr bitmap, IntPtr page, int start_x, int start_y, int size_x, int size_y, int rotate, int flags);
    }
}
