use pdfium_bind_sys as ffi;

use crate::document::Document;
use crate::error::Error;
use crate::render::RenderResult;

/// A single page within a PDF [`Document`].
///
/// Borrows the parent document — the page must be dropped before the document.
/// Use [`Page::render`] to rasterize the page at a given DPI.
pub struct Page<'a> {
    pub(crate) handle: ffi::FPDF_PAGE,
    pub(crate) _doc: &'a Document,
}

impl Page<'_> {
    /// Page width in PDF points (1 point = 1/72 inch).
    pub fn width(&self) -> f32 {
        unsafe { ffi::FPDF_GetPageWidthF(self.handle) }
    }

    /// Page height in PDF points (1 point = 1/72 inch).
    pub fn height(&self) -> f32 {
        unsafe { ffi::FPDF_GetPageHeightF(self.handle) }
    }

    /// Render this page to an RGBA pixel buffer at the specified DPI.
    ///
    /// A `dpi` of 72.0 produces pixels at 1:1 with the PDF point grid.
    /// 150.0 and 300.0 are common choices for screen and print quality.
    ///
    /// The returned [`RenderResult`] contains RGBA pixel data with a white
    /// background. Annotations are rendered; the output matches print quality.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # let _lib = pdfium_bind::Library::init();
    /// # let doc = pdfium_bind::Document::open("input.pdf").unwrap();
    /// let page = doc.page(0).unwrap();
    /// let img = page.render(150.0).unwrap();
    /// assert_eq!(img.data.len(), (img.stride * img.height) as usize);
    /// ```
    pub fn render(&self, dpi: f32) -> Result<RenderResult, Error> {
        let scale = dpi / 72.0;
        let width = (self.width() * scale).round() as i32;
        let height = (self.height() * scale).round() as i32;

        let bitmap = unsafe { ffi::FPDFBitmap_Create(width, height, 1) };
        if bitmap.is_null() {
            return Err(Error::BitmapCreateFailed);
        }

        // Fill with opaque white background.
        unsafe {
            ffi::FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFF_FFFF);
        }

        // Render with annotations, print quality, and RGBA byte order.
        let flags = (ffi::FPDF_ANNOT | ffi::FPDF_PRINTING | ffi::FPDF_REVERSE_BYTE_ORDER) as i32;
        unsafe {
            ffi::FPDF_RenderPageBitmap(bitmap, self.handle, 0, 0, width, height, 0, flags);
        }

        // Copy pixel data out before destroying the bitmap.
        let stride = unsafe { ffi::FPDFBitmap_GetStride(bitmap) } as u32;
        let buf = unsafe { ffi::FPDFBitmap_GetBuffer(bitmap) };
        let len = (stride * height as u32) as usize;
        let data = unsafe { std::slice::from_raw_parts(buf as *const u8, len) }.to_vec();

        unsafe { ffi::FPDFBitmap_Destroy(bitmap) };

        Ok(RenderResult {
            data,
            width: width as u32,
            height: height as u32,
            stride,
        })
    }
}

impl Drop for Page<'_> {
    fn drop(&mut self) {
        unsafe { ffi::FPDF_ClosePage(self.handle) }
    }
}
