"""
Low-level PDF writer for constructing PDF files from individual objects.

This module provides direct control over PDF file construction by allowing
objects to be added individually and the final output written to a buffer.

Example usage:
    from micropdf.pdf_writer import PdfWriter

    writer = PdfWriter()
    writer.add_object(1, 0, obj_handle)
    data = writer.write()
    writer.drop()
"""

from .ffi_native import ffi, lib, is_native_available
from .errors import MicroPDFError, ErrorCode


def _check_native() -> None:
    if not is_native_available():
        raise MicroPDFError(ErrorCode.SYSTEM, "Native library not available.")


class PdfWriter:
    """A low-level PDF writer for constructing PDF files object by object."""

    __slots__ = ('_handle',)

    def __init__(self) -> None:
        _check_native()
        self._handle = lib.mp_writer_new()
        if self._handle == 0:
            raise MicroPDFError(ErrorCode.GENERIC, "Failed to create PDF writer")

    def add_object(self, obj_num: int, generation: int, obj_handle: int) -> None:
        """
        Add a PDF object to the writer.

        Args:
            obj_num: PDF object number.
            generation: PDF object generation number.
            obj_handle: Handle to the PDF object.

        Raises:
            MicroPDFError: If adding the object fails.
        """
        result = lib.mp_writer_add_object(self._handle, obj_num, generation, obj_handle)
        if result < 0:
            raise MicroPDFError(
                ErrorCode.GENERIC, f"Failed to add object {obj_num}: error code {result}"
            )

    def write(self, max_size: int = 10 * 1024 * 1024) -> bytes:
        """
        Write the PDF to a byte buffer.

        Args:
            max_size: Maximum buffer size in bytes (default 10MB).

        Returns:
            PDF file data as bytes.

        Raises:
            MicroPDFError: If writing fails.
        """
        buf = ffi.new(f"unsigned char[{max_size}]")
        written = lib.mp_writer_write(self._handle, buf, max_size)
        if written < 0:
            raise MicroPDFError(
                ErrorCode.GENERIC, f"Failed to write PDF: error code {written}"
            )
        return bytes(ffi.buffer(buf, written))

    def drop(self) -> None:
        """Release the writer resources."""
        if getattr(self, '_handle', 0) != 0:
            lib.mp_writer_free(self._handle)
            self._handle = 0

    def __del__(self) -> None:
        try:
            self.drop()
        except Exception:
            pass

    def __enter__(self) -> 'PdfWriter':
        return self

    def __exit__(self, *args: object) -> None:
        self.drop()


__all__ = ["PdfWriter"]
