<?php

declare(strict_types=1);

namespace svoboda\pechatalka\models;

// Files of the project
use svoboda\pechatalka\models\core,
	/* svoboda\pechatalka\models\enumerations\paper\type as paper_type, */
	svoboda\pechatalka\models\enumerations\paper\format as paper_format,
	svoboda\pechatalka\models\enumerations\product\type as product_type;

// Svoboda time
use svoboda\time\statement as svoboda;

// Baza database
use mirzaev\baza\database,
	mirzaev\baza\column,
	mirzaev\baza\record,
	mirzaev\baza\enumerations\encoding,
	mirzaev\baza\enumerations\type;

// Framework for Telegram
use Zanzara\Telegram\Type\User as telegram;

// Built-in libraries
use Imagick as imagick,
	ImagickKernel as imagick_kernel,
	ImagickPixel as imagick_pixel,
	ImagickDraw as imagick_draw,
	Exception as exception,
	RuntimeException as exception_runtime;

/**
 * Paper
 *
 * @package svoboda\pechatalka\models
 *
 * @license http://www.wtfpl.net/ Do What The Fuck You Want To Public License
 * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
 */
final class paper extends core
{
	/**
	 * Generate
	 * 
	 * @param paper_format $format
	 * @param product_type $type
	 * @param array $products
	 * @param array $canvas
	 * @param int|float $dpi Dots (pixels) per Inch (used with centimeters)
	 * @param bool $fit Fit the maximum number of blanks on paper? (duplication)
	 *
	 * @return record The account record from the database
	 */
	public static function generate(paper_format $format, product_type $type, array $products, array $canvas = [], int|float $dpi = 300, bool $fit = true): mixed
	{
		// Initializing the paper dimensions
		$dimensions = $format->dimensions();
		$width = (int) round($dimensions['width'] * $dpi);
		$height = (int) round($dimensions['height'] * $dpi);
		unset($dimensions);

		// Initializing the product areas
		$areas = $type->areas();

		// Calculating the product blank dimensions by the print $dpi
		$blank = [
			'width' => (int) round($areas['blank']['width'] * $dpi),
			'height' => (int) round($areas['blank']['height'] * $dpi)
		];

		// Calculating the $dpi ratio of the pechatalka interface to the blank dimensions
		$ratio = [
			'width' => $blank['width'] / $canvas['width'],
			'height' => $blank['height'] / $canvas['height']
		];

		// Initializing the circle mask
		$mask = new imagick();
		$mask->setResolution($dpi, $dpi);
		$mask->newImage($blank['width'], $blank['height'], '#0000');
		$mask->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
		$mask->setimageformat('png');
		$mask->setimagematte(true);
		$draw = new imagick_draw();
		$draw->setfillcolor('#fff');
		$draw->circle($blank['width'] / 2, $blank['height'] / 2, $blank['width'] / 2, $blank['height']);
		$mask->drawimage($draw);

		// Initializing the paper places
		$places = $format->places(type: $type);

		// Calculating the amount of products
		$have = count($products);

		// Calculating the amount of places per paper
		$limit = count($places);

		// Calculating the amount of missing products on the paper
		$need = $limit - $have;

		if ($fit && $have < $limit) {
			// Received less products then can be fit on the page

			for (; $need++ < $limit;) {
				// Iterating over free places

				// Dublicating the product
				$products[] = $products[rand(0, $have - 1)];
			}
		}

		// Calculating the amount of papers
		$amount = $have % $limit;

		for ($page = 0; $page < $amount; $page++) {
			// Iterating over pages

			// Initializing the paper
			$paper = new imagick();
			$paper->setResolution($dpi, $dpi);
			$paper->newImage($width, $height, new imagick_pixel("#fff"));
			$paper->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
			$paper->setImageFormat("jpg");
			$paper->setimagematte(true);

			// Calculating the products pages offset
			$offset = $page === 0 ? 0 : $page * $amount;

			foreach ($places as $index => $place) {
				// Iterating over places

				// Recalculating the place coordinates with DPI
				$place['x'] *= $dpi;
				$place['y'] *= $dpi;

				// Initializing the product
				$product = $products[$offset + $index];

				foreach ($product as $layer) {
					// Iterating over the product layers

					// Filtering and normalizing the layer parameters
					empty($layer['scale']) || $layer['scale'] == 0 and $layer['scale'] = 1;

					// Initializing the layer image
					$image = new imagick();
					$image->setResolution($dpi, $dpi);
					$image->readImage($layer['image']);
					$image->setImageUnits(imagick::RESOLUTION_PIXELSPERCENTIMETER);
					$image->setimagematte(true);

					// Initializing the layer image height before resizing
					$before = $image->getImageHeight();

					// Resizing the layer image
					$image->adaptiveResizeImage((int) round($blank['width'] * $layer['scale']), 0);

					if (!empty($layer['corners'])) {
						// Received the layer image corners

						// Masking the layer image with corners
						$image->roundCornersImage($layer['corners'], $layer['corners']);
					}

					// Calculating the layer image coordinates by the layer image mask
					$vertical = $blank['height'] - $before;
					$layer['x'] = (int) round($layer['x'] * $ratio['width'] + ($blank['width'] - $image->getImageWidth()) / 2);
					$layer['y'] = (int) round($layer['y'] * $ratio['height'] + ($blank['height'] - $image->getImageHeight() + ($vertical > 0 ? $vertical : 0)) / 2);
					unset($before, $vertical);

					// Compositing the layer image mask with the layer image
					$image->compositeImage(
						$mask,
						imagick::COMPOSITE_DSTIN,
						(int) round(-$layer['x']),
						(int) round(-$layer['y'])
					);

					// Drawing the cutting line
					$draw = new imagick_draw();
					$draw->setfillcolor($canvas['background'] ?? '#fff');
					$stroke = 2;
					$draw->setStrokeOpacity(1);
					$draw->setStrokeColor('#000');
					$draw->setStrokeWidth($stroke);
					$draw->circle(
						$place['x'] + $blank['width'] / 2,
						$place['y'] + $blank['height'] / 2,
						round($place['x'] + $blank['width'] / 2 + $stroke),
						round($place['y'] + $blank['height'] + $stroke)
					);
					$paper->drawimage($draw);

					// Compositing the layer image with the paper
					$paper->compositeImage(
						$image,
						$image->getImageCompose(),
						(int) round($place['x'] + $layer['x']),
						(int) round($place['y'] + $layer['y'])
					);
				}
			}

			// Writing the paper file
			$paper->writeImage(INDEX . DIRECTORY_SEPARATOR . 'test.jpg');
		}


		// Exit (success)
		return [];
	}
}
