<?php

namespace Kirby\Image;

use Kirby\Toolkit\A;
use Kirby\Toolkit\Str;

/**
 * @package   Kirby Image
 * @author    Nico Hoffmann <nico@getkirby.com>
 * @link      https://getkirby.com
 * @copyright Bastian Allgeier
 * @license   https://opensource.org/licenses/MIT
 */
class Focus
{
	/**
	 * Generates crop coordinates based on focal point
	 */
	public static function coords(
		string $crop,
		int $sourceWidth,
		int $sourceHeight,
		int $width,
		int $height
	): array|null {
		$crop = static::normalize($crop);
		[$x, $y] = static::parse($crop);

		// determine aspect ratios
		$ratioSource = static::ratio($sourceWidth, $sourceHeight);
		$ratioThumb  = static::ratio($width, $height);

		// no cropping necessary
		if ($ratioSource == $ratioThumb) {
			return null;
		}

		// defaults
		$width  = $sourceWidth;
		$height = $sourceHeight;

		if ($ratioThumb > $ratioSource) {
			$height = $sourceWidth / $ratioThumb;
		} else {
			$width = $sourceHeight * $ratioThumb;
		}

		// calculate focus for original image
		$x = $sourceWidth * $x;
		$y = $sourceHeight * $y;

		$x1 = max(0, $x - $width / 2);
		$y1 = max(0, $y - $height / 2);

		// off canvas?
		if ($x1 + $width > $sourceWidth) {
			$x1 = $sourceWidth - $width;
		}

		if ($y1 + $height > $sourceHeight) {
			$y1 =  $sourceHeight - $height;
		}

		return [
			'x1'     => (int)floor($x1),
			'y1'     => (int)floor($y1),
			'x2'     => (int)floor($x1 + $width),
			'y2'     => (int)floor($y1 + $height),
			'width'  => (int)floor($width),
			'height' => (int)floor($height),
		];
	}

	public static function isFocalPoint(string $value): bool
	{
		return Str::contains($value, '%') === true;
	}

	public static function normalize(string $value): string
	{
		// support for former Focus plugin
		if (Str::startsWith($value, '{') === true) {
			$focus = json_decode($value);

			// ignore malformed/partial JSON
			if (
				is_object($focus) === true &&
				is_numeric($focus->x ?? null) === true &&
				is_numeric($focus->y ?? null) === true
			) {
				$x = round(100 * $focus->x);
				$y = round(100 * $focus->y);
				return "$x% $y%";
			}

			return '50% 50%';
		}

		if (static::isFocalPoint($value) === false) {
			return Gravity::from($value)->toPercentageString();
		}

		return $value;
	}

	/**
	 * Transforms the focal point's string value (from content field)
	 * to a [x, y] array (values 0.0-1.0)
	 */
	public static function parse(string $value): array
	{
		preg_match_all("/(\d{1,3}\.?\d*)[%|,|\s]*/", $value, $points);

		return A::map(
			$points[1],
			function ($point) {
				$point = (float)$point;
				$point = $point > 1 ? $point / 100 : $point;
				return round($point, 3);
			}
		);
	}

	/**
	 * Calculates the image ratio
	 */
	public static function ratio(int $width, int $height): float
	{
		return $height !== 0 ? $width / $height : 0;
	}
}
