<?php
namespace boru\boruai\OCR\Tesseract;

use boru\boruai\OCR\Contract\PageImageProviderInterface;
use Imagick;
use ImagickException;

class ImagickPdfPageImageProvider implements PageImageProviderInterface
{
    private $pdfPath;
    private $imageDir;

    /** @var array<int, string|array<int, array{path:string,offset_x:int,offset_y:int}>> */
    private $images = [];

    /** Tiling config */
    private $tilePixelThreshold = 0; // 0 = disabled
    private $tileCols = 2;
    private $tileRows = 2;
    private $tileOverlap = 20; // pixels

    /** Base page DPI and tile DPI */
    private $imageDpi = 400;
    private $tileDpi  = 600;

    public function __construct(string $pdfPath, string $imageDir, array $options = [])
    {
        $this->pdfPath  = $pdfPath;
        $this->imageDir = $imageDir;

        // base DPI / tile DPI
        if (isset($options['imageDpi'])) {
            $this->imageDpi = max(72, (int)$options['imageDpi']);
        }
        if (isset($options['tileDpi'])) {
            $this->tileDpi = max(72, (int)$options['tileDpi']);
        }

        // tiling options
        if (isset($options['tilePixelThreshold'])) {
            $this->tilePixelThreshold = (int)$options['tilePixelThreshold'];
        }
        if (isset($options['tileCols'])) {
            $this->tileCols = max(1, (int)$options['tileCols']);
        }
        if (isset($options['tileRows'])) {
            $this->tileRows = max(1, (int)$options['tileRows']);
        }
        if (isset($options['tileOverlap'])) {
            $this->tileOverlap = max(0, (int)$options['tileOverlap']);
        }
    }

    /**
     * @inheritDoc
     * @throws ImagickException
     */
    public function getPageImages()
    {
        if (!empty($this->images)) {
            return $this->images;
        }

        $startDpi = $this->imageDpi;
        $continue = true;

        while ($startDpi > 72 && $continue) {
            try {
                $imagick = $this->getImagick($startDpi);
                $imagick->readImage($this->pdfPath);

                foreach ($imagick as $i => $page) {
                    // Preprocess page (flatten, fix colors)
                    $page->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
                    $page->setImageBackgroundColor('white');
                    $page = $page->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
                    $page->setColorspace(Imagick::COLORSPACE_RGB);

                    $width  = $page->getImageWidth();
                    $height = $page->getImageHeight();
                    $pixels = $width * $height;

                    // Decide to tile or not
                    if ($this->tilePixelThreshold > 0 && $pixels > $this->tilePixelThreshold) {
                        $this->images[$i] = $this->createTilesForPage($page, $i, $width, $height);
                    } else {
                        // Single full-page image
                        $page->setImageFormat('png');
                        $page->setImageCompressionQuality(100);

                        $imgPath = "{$this->imageDir}/page_{$i}.png";
                        $page->writeImage($imgPath);
                        $this->images[$i] = $imgPath;

                        // Optional debug copy
                        @copy($imgPath, '/var/work/libs/boruai/tests/pdfs/last_ocr_page.png');
                    }
                }

                $imagick->clear();
                $imagick->destroy();
                $continue = false;
            } catch (ImagickException $e) {
                //echo "ImagickException at DPI {$startDpi}: " . $e->getMessage() . "\n";
                $startDpi = (int)($startDpi * 0.75);
                //echo "Retrying full-page render with lower DPI: {$startDpi}\n";
                $this->images = [];
            }
        }

        return $this->images;
    }

    public function cleanup()
    {
        foreach (glob($this->imageDir . '/*.png') as $file) {
            @unlink($file);
        }
        @rmdir($this->imageDir);
    }

    /**
     * Create tiles for a given Imagick page (Imagick-only version).
     *
     * NOTE: This is your existing logic, still trying to re-render tiles
     * at tileDpi, with fallback to cloning the existing page if tileDpi
     * == imageDpi, and DPI fallback loop if memory fails.
     *
     * This is where we could later plug in a MuPDF-based tiler as a
     * separate strategy, but for now we just keep your Imagick-based
     * fallback behaviour.
     *
     * @param Imagick $page
     * @param int     $pageIndex
     * @param int     $width
     * @param int     $height
     * @return array<int,array{path:string,offset_x:int,offset_y:int}>
     * @throws ImagickException
     */
    protected function createTilesForPage(Imagick $page, int $pageIndex, int $width, int $height)
    {
        $tiles = [];

        $cols    = max(1, $this->tileCols);
        $rows    = max(1, $this->tileRows);
        $overlap = $this->tileOverlap;

        $tileWidth  = (int)ceil($width / $cols);
        $tileHeight = (int)ceil($height / $rows);

        $startTileDpi = $this->tileDpi;

        for ($ty = 0; $ty < $rows; $ty++) {
            for ($tx = 0; $tx < $cols; $tx++) {

                $x = $tx * $tileWidth;
                $y = $ty * $tileHeight;

                // Apply overlap
                $x = max(0, $x - $overlap);
                $y = max(0, $y - $overlap);

                $wTile = min($tileWidth + $overlap * 2, $width - $x);
                $hTile = min($tileHeight + $overlap * 2, $height - $y);

                if ($wTile <= 0 || $hTile <= 0) {
                    continue;
                }

                $tileDpi  = $startTileDpi;
                $continue = true;
                $theseTiles = [];

                while ($tileDpi > 72 && $continue) {
                    try {
                        if ($this->tileDpi !== $this->imageDpi) {
                            $tile = $this->getImagick($this->tileDpi);
                            // Load entire page at high DPI
                            $tile->readImage($this->pdfPath . "[$pageIndex]");
                        } else {
                            $tile = clone $page;
                        }

                        // Crop the high-resolution tile area
                        $scaleX = $tile->getImageWidth() / $width;
                        $scaleY = $tile->getImageHeight() / $height;

                        $cropX = (int)($x * $scaleX);
                        $cropY = (int)($y * $scaleY);
                        $cropW = (int)($wTile * $scaleX);
                        $cropH = (int)($hTile * $scaleY);

                        $tile->cropImage($cropW, $cropH, $cropX, $cropY);
                        $tile->setImagePage(0, 0, 0, 0);

                        // Cleanup and save
                        $tile->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
                        $tile->setImageBackgroundColor("white");
                        $tile = $tile->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);

                        $tile->setImageFormat('png');
                        $tile->setImageCompressionQuality(100);

                        $tilePath = sprintf(
                            "%s/page_%d_tile_%d_%d.png",
                            $this->imageDir,
                            $pageIndex,
                            $tx,
                            $ty
                        );

                        $tile->writeImage($tilePath);

                        $theseTiles[] = [
                            'path'     => $tilePath,
                            'offset_x' => $x,
                            'offset_y' => $y,
                        ];

                        $continue = false;
                    } catch (ImagickException $e) {
                        //echo "ImagickException for tile ({$tx},{$ty}) at DPI {$tileDpi}: " . $e->getMessage() . "\n";
                        $tileDpi = (int)($tileDpi * 0.75);
                        //echo "Retrying tile with lower DPI: {$tileDpi}\n";
                    }
                }

                $tiles = array_merge($tiles, $theseTiles);
            }
        }

        // Optional: debug last tile
        if (!empty($tiles)) {
            @copy($tiles[count($tiles) - 1]['path'], '/var/work/libs/boruai/tests/pdfs/last_ocr_page.png');
        }

        return $tiles;
    }

    /**
     * Generate a new Imagick instance with defaults applied for simplicity.
     *
     * @param int $imageDpi
     * @param int $mem
     * @param int $map
     * @param int $disk
     * @return Imagick
     */
    protected function getImagick(
        int $imageDpi = 400,
        int $mem = 2 * 1024 * 1024 * 1024,
        int $map = 4 * 1024 * 1024 * 1024,
        int $disk = 16 * 1024 * 1024 * 1024
    ){
        $imagick = new Imagick();

        // Resource limits (bytes)
        $imagick->setResourceLimit(Imagick::RESOURCETYPE_MEMORY, $mem);
        $imagick->setResourceLimit(Imagick::RESOURCETYPE_MAP,    $map);
        $imagick->setResourceLimit(Imagick::RESOURCETYPE_DISK,   $disk);

        $imagick->setOption('pdf:use-cropbox', 'true');
        $imagick->setOption('pdf:use-trimbox', 'true');

        $imagick->setResolution($imageDpi, $imageDpi);

        return $imagick;
    }
}
