<?php
/**
 * @package dompdf
 * @link    http://dompdf.github.com/
 * @author  Benj Carson <benjcarson@digitaljunkies.ca>
 * @author  Fabien Ménager <fabien.menager@gmail.com>
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 */
namespace Dompdf\FrameReflower;

use Dompdf\Frame;
use Dompdf\Helpers;
use Dompdf\FrameDecorator\Block as BlockFrameDecorator;
use Dompdf\FrameDecorator\Image as ImageFrameDecorator;

/**
 * Image reflower class
 *
 * @package dompdf
 */
class Image extends AbstractFrameReflower
{

    /**
     * Image constructor.
     * @param ImageFrameDecorator $frame
     */
    function __construct(ImageFrameDecorator $frame)
    {
        parent::__construct($frame);
    }

    /**
     * @param BlockFrameDecorator|null $block
     */
    function reflow(BlockFrameDecorator $block = null)
    {
        $this->_frame->position();

        //FLOAT
        //$frame = $this->_frame;
        //$page = $frame->get_root();

        //if ($frame->get_style()->float !== "none" ) {
        //  $page->add_floating_frame($this);
        //}

        // Set the frame's width
        $this->get_min_max_width();

        if ($block) {
            $block->add_frame_to_line($this->_frame);
        }
    }

    /**
     * @return array
     */
    function get_min_max_width()
    {
        $frame = $this->_frame;

        if ($this->get_dompdf()->getOptions()->getDebugPng()) {
            // Determine the image's size. Time consuming. Only when really needed?
            list($img_width, $img_height) = Helpers::dompdf_getimagesize($frame->get_image_url(), $this->get_dompdf()->getHttpContext());
            print "get_min_max_width() " .
                $frame->get_style()->width . ' ' .
                $frame->get_style()->height . ';' .
                $frame->get_parent()->get_style()->width . " " .
                $frame->get_parent()->get_style()->height . ";" .
                $frame->get_parent()->get_parent()->get_style()->width . ' ' .
                $frame->get_parent()->get_parent()->get_style()->height . ';' .
                $img_width . ' ' .
                $img_height . '|';
        }

        $style = $frame->get_style();

        $width_forced = true;
        $height_forced = true;

        //own style auto or invalid value: use natural size in px
        //own style value: ignore suffix text including unit, use given number as px
        //own style %: walk up parent chain until found available space in pt; fill available space
        //
        //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto

        $width = $this->get_size($frame, 'width');
        $height = $this->get_size($frame, 'height');

        if ($width === 'auto' || $height === 'auto') {
            // Determine the image's size. Time consuming. Only when really needed!
            list($img_width, $img_height) = Helpers::dompdf_getimagesize($frame->get_image_url(), $this->get_dompdf()->getHttpContext());

            // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable.
            // Resample according to px per inch
            // See also ListBulletImage::__construct
            if ($width === 'auto' && $height === 'auto') {
                $dpi = $frame->get_dompdf()->getOptions()->getDpi();
                $width = (float)($img_width * 72) / $dpi;
                $height = (float)($img_height * 72) / $dpi;
                $width_forced = false;
                $height_forced = false;
            } elseif ($height === 'auto') {
                $height_forced = false;
                $height = ($width / $img_width) * $img_height; //keep aspect ratio
            } else {
                $width_forced = false;
                $width = ($height / $img_height) * $img_width; //keep aspect ratio
            }
        }

        // Handle min/max width/height
        if ($style->min_width !== "none" ||
            $style->max_width !== "none" ||
            $style->min_height !== "none" ||
            $style->max_height !== "none"
        ) {

            list( /*$x*/, /*$y*/, $w, $h) = $frame->get_containing_block();

            $min_width = $style->length_in_pt($style->min_width, $w);
            $max_width = $style->length_in_pt($style->max_width, $w);
            $min_height = $style->length_in_pt($style->min_height, $h);
            $max_height = $style->length_in_pt($style->max_height, $h);

            if ($max_width !== "none" && $max_width !== "auto" && $width > (float)$max_width) {
                if (!$height_forced) {
                    $height *= (float)$max_width / $width;
                }

                $width = (float)$max_width;
            }

            if ($min_width !== "none" && $min_width !== "auto" && $width < (float)$min_width) {
                if (!$height_forced) {
                    $height *= (float)$min_width / $width;
                }

                $width = (float)$min_width;
            }

            if ($max_height !== "none" && $max_height !== "auto" && $height > (float)$max_height) {
                if (!$width_forced) {
                    $width *= (float)$max_height / $height;
                }

                $height = (float)$max_height;
            }

            if ($min_height !== "none" && $min_height !== "auto" && $height < (float)$min_height) {
                if (!$width_forced) {
                    $width *= (float)$min_height / $height;
                }

                $height = (float)$min_height;
            }
        }

        if ($this->get_dompdf()->getOptions()->getDebugPng()) {
            print $width . ' ' . $height . ';';
        }

        $style->width = $width . "pt";
        $style->height = $height . "pt";

        $style->min_width = "none";
        $style->max_width = "none";
        $style->min_height = "none";
        $style->max_height = "none";

        return [$width, $width, "min" => $width, "max" => $width];
    }

    private function get_size(Frame $f, string $type)
    {
        $ref_stack = [];
        $result_size = 0.0;
        do {
            $f_style = $f->get_style();
            $current_size = $f_style->$type;
            if (Helpers::is_percent($current_size)) {
                $ref_stack[] = str_replace('%px', '%', $current_size);
            } else {
                // auto is a valid first result. In case of previous percentage values we need a real size
                if ($current_size !== 'auto' || count($ref_stack) === 0) {
                    $result_size = $f_style->length_in_pt($current_size);
                    break;
                }
            }
        } while (($f = $f->get_parent()));

        // if we built a percentage stack walk up to find the real size
        if (count($ref_stack) > 0) {
            while (($ref = array_pop($ref_stack))) {
                $result_size = $f_style->length_in_pt($ref, $result_size);
            }
        }

        return $result_size;
    }
}
