<?php
/**
 * Mpdf artifact rendering service.
 *
 * @package EntryFoundry
 */

namespace EntryFoundry\Core\Services\Render;

use EntryFoundry\Core\Views\Render\TemplateView;
use Mpdf\Mpdf;
use Mpdf\Output\Destination;

/**
 * Sends the shared view markup to mPDF.
 */
final class PdfRenderer {
	/**
	 * Shared template view.
	 *
	 * @var TemplateView
	 */
	private $view;

	/**
	 * Writable mPDF temporary directory.
	 *
	 * @var string
	 */
	private $temp_dir;

	/**
	 * Initialize the PDF renderer.
	 *
	 * @param TemplateView $view Shared template view.
	 * @param string       $temp_dir Writable temporary directory.
	 */
	public function __construct( TemplateView $view, $temp_dir ) {
		$this->view     = $view;
		$this->temp_dir = $temp_dir;
	}

	/**
	 * Write the PDF artifact.
	 *
	 * @param array<string, mixed> $template Validated template.
	 * @param string               $path Output path.
	 * @return void
	 */
	public function write( array $template, $path ) {
		$page = $template['page'];
		$mpdf = new Mpdf(
			array(
				'mode'          => 'utf-8',
				'format'        => array( (float) $page['width_mm'], (float) $page['height_mm'] ),
				'margin_left'   => 0,
				'margin_right'  => 0,
				'margin_top'    => 0,
				'margin_bottom' => 0,
				'tempDir'       => $this->temp_dir,
			)
		);

		$mpdf->SetDisplayMode( 'fullpage' );
		$mpdf->WriteHTML( $this->view->render_css( $template, false ), \Mpdf\HTMLParserMode::HEADER_CSS );
		$page_count = ! empty( $page['flow'] ) ? max( 1, (int) ( $template['page_count'] ?? 1 ) ) : 1;
		$boxes      = $this->view->render_fixed_boxes( $template );
		for ( $page_index = 0; $page_index < $page_count; ++$page_index ) {
			$mpdf->AddPage();
			$this->write_page_background( $mpdf, $template );
			foreach ( $boxes as $box ) {
				if ( $page_index !== $box['page_index'] ) {
					continue;
				}
				$mpdf->WriteFixedPosHTML(
					$box['html'],
					$box['x_mm'],
					$box['y_mm'],
					$box['w_mm'],
					$box['h_mm'],
					'hidden'
				);
			}
		}
		$mpdf->Output( $path, Destination::FILE );
	}

	/**
	 * Paint the page background below every positioned element.
	 *
	 * @param Mpdf                 $mpdf     PDF engine.
	 * @param array<string, mixed> $template Validated template.
	 * @return void
	 */
	private function write_page_background( Mpdf $mpdf, array $template ) {
		$page       = $template['page'];
		$width_mm   = (float) $page['width_mm'];
		$height_mm  = (float) $page['height_mm'];
		$background = $this->view->fixed_background( $template );
		$color      = ltrim( $background['color'], '#' );
		if ( 3 === strlen( $color ) ) {
			$color = $color[0] . $color[0] . $color[1] . $color[1] . $color[2] . $color[2];
		}

		$mpdf->SetFillColor( hexdec( substr( $color, 0, 2 ) ), hexdec( substr( $color, 2, 2 ) ), hexdec( substr( $color, 4, 2 ) ) );
		$mpdf->Rect( 0, 0, $width_mm, $height_mm, 'F' );

		if ( '' === $background['image'] ) {
			return;
		}

		$image_width  = max( 1, (int) $background['image_width_px'] );
		$image_height = max( 1, (int) $background['image_height_px'] );
		$scale        = 'contain' === $background['image_fit']
			? min( $width_mm / $image_width, $height_mm / $image_height )
			: max( $width_mm / $image_width, $height_mm / $image_height );
		$draw_width   = $image_width * $scale;
		$draw_height  = $image_height * $scale;
		$x            = ( $width_mm - $draw_width ) / 2;
		$y            = ( $height_mm - $draw_height ) / 2;

		$mpdf->Image( $background['image'], $x, $y, $draw_width, $draw_height, '', '', true, false );
	}
}
