<?php
/**
 * Rendering for the [flipa] shortcode AND the Gutenberg block — both call
 * render_book(), which builds an iframe to the engine (assets/viewer.html)
 * served from THIS site (self-contained), fed the book's settings as the
 * engine's URL params (pdf, dir, lang, view, bg, snd, hide, arrows, pwh,
 * toc, toct). M3 will replace the iframe with an inline render.
 */
if ( ! defined( 'ABSPATH' ) ) { exit; }

class Flipa_Render {

	public static function init() {
		add_shortcode( 'flipa', array( __CLASS__, 'shortcode' ) );
	}

	/** djb2 hash → base36, byte-for-byte match with the engine's hash() in JS (ASCII). */
	public static function djb2( $s ) {
		$h = 5381;
		$len = strlen( $s );
		for ( $i = 0; $i < $len; $i++ ) {
			$h = ( ( $h * 33 ) + ord( $s[ $i ] ) ) % 4294967296;
		}
		if ( 0 === $h ) { return '0'; }
		$chars = '0123456789abcdefghijklmnopqrstuvwxyz';
		$out = '';
		while ( $h > 0 ) { $out = $chars[ $h % 36 ] . $out; $h = intdiv( $h, 36 ); }
		return $out;
	}

	/**
	 * Strict CSS length for inline width/height (a number + an allowed unit only).
	 * Bare numbers become px; anything else falls back to $fallback. This prevents
	 * arbitrary CSS injection via the shortcode atts or saved width/height settings.
	 */
	public static function css_len( $v, $fallback ) {
		$v = trim( (string) $v );
		if ( '' === $v ) { return $fallback; }
		if ( is_numeric( $v ) ) { return $v . 'px'; }
		if ( preg_match( '/^[0-9]+(?:\.[0-9]+)?(?:px|%|em|rem|vw|vh|vmin|vmax)$/', $v ) ) { return $v; }
		return $fallback;
	}

	/** Read the saved TOC rows (array of ['name'=>..,'page'=>..]). */
	public static function get_toc( $id ) {
		$json = get_post_meta( $id, '_flipa_toc', true );
		$toc  = $json ? json_decode( $json, true ) : array();
		return is_array( $toc ) ? $toc : array();
	}

	/** Read saved publisher bookmarks (array of ['name'=>..,'page'=>..]). */
	public static function get_bookmarks( $id ) {
		$json = get_post_meta( $id, '_flipa_bookmarks', true );
		$bm   = $json ? json_decode( $json, true ) : array();
		return is_array( $bm ) ? $bm : array();
	}

	/** Build the engine config param array for a book (mirror of buildConfigUrl in the engine). */
	public static function build_params( $id ) {
		// If the site is served over HTTPS, force an http:// PDF URL to https:// so the browser
		// doesn't block it as mixed content ("Failed to fetch"). Visitors fetch the PDF client-side.
		$pdf_url = Flipa_Admin::get( $id, 'pdf_url', '' );
		if ( is_ssl() && 0 === stripos( $pdf_url, 'http://' ) ) {
			$pdf_url = set_url_scheme( $pdf_url, 'https' );
		}
		$p = array(
			'pdf'   => $pdf_url,
			'dir'   => Flipa_Admin::get( $id, 'dir', 'ltr' ),
			'lang'  => Flipa_Admin::get( $id, 'lang', 'en' ),
			'view'  => Flipa_Admin::get( $id, 'view', 'double' ),
			'cover' => 'hard',
		);

		if ( '1' === Flipa_Admin::get( $id, 'bg_transparent', '0' ) ) {
			$p['bg'] = 'transparent';
		} else {
			$p['bg'] = Flipa_Admin::get( $id, 'bg', '#222a36' );
		}
		

		if ( 'light' === Flipa_Admin::get( $id, 'theme', 'dark' ) ) { $p['theme'] = 'light'; }

		// autoplay (free)
		if ( '1' === Flipa_Admin::get( $id, 'autoplay_on', '0' ) ) {
			$p['auto']    = '1';
			$p['autoint'] = (string) max( 1, min( 60, intval( Flipa_Admin::get( $id, 'autoplay_interval', '4' ) ) ) );
			if ( '1' === Flipa_Admin::get( $id, 'autoplay_loop', '0' ) )  { $p['autoloop']  = '1'; }
			if ( '1' === Flipa_Admin::get( $id, 'autoplay_start', '0' ) ) { $p['autostart'] = '1'; }
		}
		// print (free)
		if ( '1' === Flipa_Admin::get( $id, 'print_on', '0' ) ) { $p['print'] = '1'; }

		// "Powered by flipa" credit — OFF by default; shown only when the site owner opts in (free, never forced).
		if ( '1' === Flipa_Admin::get( $id, 'credit_on', '0' ) ) { $p['credit'] = '1'; }

		// ---- Pro-only viewer params (scroll, tilt, media, bookmarks, sounds) ----
		// This whole block lives inside an is__premium_only() guard, so the build
		// strips it from the free (WordPress.org) zip — the free build never builds them.
		

		// toggles → hide list (off toggles, excluding sidearrows which uses its own param)
		$hide = array();
		foreach ( array_keys( Flipa_Admin::TOGGLES ) as $key ) {
			if ( 'sidearrows' === $key ) { continue; }
			if ( ! Flipa_Admin::toggle_on( $id, $key ) ) { $hide[] = $key; }
		}
		if ( $hide ) { $p['hide'] = implode( ',', $hide ); }
		if ( Flipa_Admin::toggle_on( $id, 'sidearrows' ) ) { $p['arrows'] = '1'; }

		// ---- Pro-only viewer params (password gate, table of contents) ----
		

		// Pro-only viewer surfaces (clickable links, QR, page-stack depth, sounds, toc, password, etc.)
		// are NOT built above in the free build — every block that adds them lives inside an
		// is__premium_only() guard that is stripped from the free (WordPress.org) zip, and the engine
		// defaults them OFF. A licensed premium install turns them on via the enable block above.

		// Share the HOST page (where this book is embedded), not the raw viewer URL.
		$permalink = get_permalink();
		if ( $permalink ) { $p['share'] = $permalink; }

		// cache-bust the engine file so an updated viewer.html always loads (engine ignores _v)
		$p['_v'] = (string) @filemtime( FLIPA_DIR . 'assets/viewer.html' );

		return $p;
	}

	/** Full viewer URL for a book. */
	public static function viewer_src( $id ) {
		return FLIPA_URL . 'assets/viewer.html?' . http_build_query( self::build_params( $id ) );
	}

	/**
	 * Render a flipbook as a responsive iframe. Shared by the shortcode and the block.
	 * $atts may include width/height/align to override the book's defaults.
	 */
	public static function render_book( $id, $atts = array() ) {
		$id = absint( $id );
		if ( ! $id || FLIPA_CPT !== get_post_type( $id ) ) {
			return self::notice( __( 'Flipa: set a valid flipbook id, e.g. [flipa id="123"].', 'flipa-pdf-flipbook' ) );
		}
		if ( ! Flipa_Admin::get( $id, 'pdf_url', '' ) ) {
			return self::notice( __( 'Flipa: this flipbook has no PDF yet. Edit it and select a PDF.', 'flipa-pdf-flipbook' ) );
		}

		$src = self::viewer_src( $id );

		// ---- lightbox mode: render a trigger (cover image or button) that opens the book in a modal ----
		$mode = isset( $atts['mode'] ) ? $atts['mode'] : '';
		if ( 'lightbox' === $mode ) {
			Flipa_Frontend::enqueue();
			$thumb = ! empty( $atts['thumb'] ) ? $atts['thumb'] : Flipa_Admin::get( $id, 'cover', '' );
			$label = ! empty( $atts['text'] ) ? $atts['text'] : get_the_title( $id );
			if ( '' === $label ) { $label = __( 'Open flipbook', 'flipa-pdf-flipbook' ); }
			if ( $thumb ) {
				return '<img class="flipa-lb-trigger" src="' . esc_url( $thumb ) . '" data-flipa-src="' . esc_url( $src ) . '" alt="' . esc_attr( get_the_title( $id ) ) . '" loading="lazy" />';
			}
			return '<button type="button" class="flipa-lb-trigger" data-flipa-src="' . esc_url( $src ) . '">' . esc_html( $label ) . '</button>';
		}

		// size & placement: passed attr → book default → built-in default
		$a_w = isset( $atts['width'] )  ? $atts['width']  : '';
		$a_h = isset( $atts['height'] ) ? $atts['height'] : '';
		$a_al= isset( $atts['align'] )  ? $atts['align']  : '';
		$width  = '' !== $a_w  ? $a_w  : Flipa_Admin::get( $id, 'width',  '100%' );
		$height = '' !== $a_h  ? $a_h  : Flipa_Admin::get( $id, 'height', '600' );
		$align  = '' !== $a_al ? $a_al : Flipa_Admin::get( $id, 'align',  'center' );
		if ( ! in_array( $align, array( 'left', 'center', 'right' ), true ) ) { $align = 'center'; }

		$w_css = self::css_len( $width,  '100%' );   // strict: number + allowed unit only; else default (no CSS injection)
		$h_css = self::css_len( $height, '600px' );
		$margin = ( 'center' === $align ) ? '0 auto' : ( ( 'right' === $align ) ? '0 0 0 auto' : '0 auto 0 0' );
		$title = esc_attr( get_the_title( $id ) );

		// render mode: 'inline' (default) = seamless, borderless, auto-height (parent resizes via
		// postMessage); 'iframe' = a fixed-height frame. $h_css is the initial height in inline mode.
		$render = ( isset( $atts['render'] ) && 'iframe' === $atts['render'] ) ? 'iframe' : 'inline';

		if ( 'inline' === $render ) {
			Flipa_Frontend::enqueue();
			$src = add_query_arg( 'inline', '1', $src );
			return sprintf(
				'<div class="flipa-embed flipa-align-%s" style="width:%s;max-width:100%%;margin:%s"><iframe src="%s" data-flipa-autoheight="1" scrolling="no" style="border:0;display:block;width:100%%;height:%s;max-width:100%%" allowfullscreen loading="lazy" title="%s"></iframe></div>',
				esc_attr( $align ), esc_attr( $w_css ), esc_attr( $margin ), esc_url( $src ), esc_attr( $h_css ), $title
			);
		}

		return sprintf(
			'<div class="flipa-embed flipa-align-%s" style="width:%s;max-width:100%%;margin:%s"><iframe src="%s" style="border:0;display:block;width:100%%;height:%s;max-width:100%%" allowfullscreen loading="lazy" title="%s"></iframe></div>',
			esc_attr( $align ), esc_attr( $w_css ), esc_attr( $margin ), esc_url( $src ), esc_attr( $h_css ), $title
		);
	}

	public static function shortcode( $atts ) {
		$atts = shortcode_atts( array(
			'id'     => 0,
			'width'  => '',        // e.g. 100%, 800, 60vw — overrides the book's default
			'height' => '',        // e.g. 600, 80vh
			'align'  => '',        // left | center | right
			'mode'   => '',        // '' = embed | lightbox
			'render' => '',        // '' = inline (seamless, auto-height) | iframe (fixed frame)
			'text'   => '',        // lightbox button label
			'thumb'  => '',        // lightbox cover image URL (else the book's cover)
		), $atts, 'flipa' );
		return self::render_book( $atts['id'], $atts );
	}

	protected static function notice( $msg ) {
		// Only show the hint to users who can edit; visitors see nothing.
		if ( ! current_user_can( 'edit_posts' ) ) { return ''; }
		return '<div class="flipa-notice" style="padding:10px 14px;border:1px dashed #c00;color:#c00;border-radius:6px;font:14px sans-serif">' . esc_html( $msg ) . '</div>';
	}
}
