<?php

namespace DZ\ImageShield\Util;

use Imagick;
use ImagickDraw;
use ImagickPixel;
use InvalidArgumentException;
use RuntimeException;
use XF;
use XF\Util\Color;
use XF\Util\File;
use function class_exists;
use function extension_loaded;
use function imagealphablending;
use function imagecolorallocate;
use function imagecreate;
use function imagedestroy;
use function imagefill;
use function imagepng;
use function imagesavealpha;
use function imagettfbbox;
use function imagettftext;

class OverlayRenderer
{
    protected static ?string $fontPath = null;

    public static function setFontPath(string $path): void
    {
        self::$fontPath = $path;
    }

    public static function fromText(string $text, int $fontSize, string $bgColor, string $textColor, int $padding = 5)
    {
        if (class_exists(Imagick::class, false)) {
            return self::drawImageImagick($text, $fontSize, $bgColor, $textColor, $padding);
        }

        return self::drawImageGd($text, $fontSize, $bgColor, $textColor, $padding);
    }

    protected static function drawImageGd(
        string $text,
        int $fontSize,
        string $bgColor,
        string $textColor,
        int $padding = 5
    ): string {
        self::assertGdExtensionLoaded();

        $fontFile = self::getFontPath();
        $bounds = imagettfbbox($fontSize, 0, $fontFile, $text);

        $width = (int) abs($bounds[4] - $bounds[6]) + $padding * 2;
        $height = (int) abs($bounds[7] - $bounds[1]) + $padding * 2;

        $x = (int) max((int) floor($padding / 2) + 1, 0);
        $y = (int) max((int) floor(($height - $padding / 2) - 2), 0);

        $image = imagecreate($width, $height);
        if ($image === false) {
            throw new InvalidArgumentException('Cannot create image');
        }

        imagesavealpha($image, true);

        $bgColorRgb = Color::colorToRgb($bgColor);
        if ($bgColorRgb === null) {
            throw new InvalidArgumentException('Invalid background color');
        }
        $textColorRgb = Color::colorToRgb($textColor);
        if ($textColorRgb === null) {
            throw new InvalidArgumentException('Invalid text color');
        }

        $bg = imagecolorallocate($image, $bgColorRgb[0], $bgColorRgb[1], $bgColorRgb[2]);
        imagefill($image, 0, 0, $bg);

        $fg = imagecolorallocate($image, $textColorRgb[0], $textColorRgb[1], $textColorRgb[2]);
        imagettftext($image, $fontSize, 0, $x, $y, $fg, $fontFile, $text);

        imagealphablending($image, false);
        imagesavealpha($image, true);

        $output = File::getTempFile();
        imagepng($image, $output, 9, PNG_ALL_FILTERS);

        imagedestroy($image);

        return $output;
    }

    protected static function drawImageImagick(
        string $text,
        int $fontSize,
        string $bgColor,
        string $textColor,
        int $padding = 5
    ): string {
        $image = new Imagick();
        $draw = new ImagickDraw();
        $pixel = new ImagickPixel($bgColor);

        $draw->setFont(self::getFontPath());
        $draw->setFontSize($fontSize);
        $draw->setFillColor(new ImagickPixel($textColor));

        $info = $image->queryFontMetrics($draw, $text);

        $textWidth = $info['textWidth'];
        $textHeight = $info['textHeight'];

        $image->newImage($textWidth + $padding * 2, $textHeight + $padding * 2, $pixel);
        $image->setFormat('png');

        $image->annotateImage(
            $draw,
            $padding,
            $textHeight,
            0,
            $text
        );

        $output = File::getTempFile();
        if (!$image->writeImage($output)) {
            throw new InvalidArgumentException('Cannot write image');
        }

        $image->destroy();
        $draw->destroy();
        $pixel->destroy();

        return $output;
    }

    public static function getFontPath(): string
    {
        if (self::$fontPath !== null && self::$fontPath !== '') {
            return self::$fontPath;
        }

        return self::getDefaultFontPath();
    }

    public static function getDefaultFontPath(): string
    {
        return XF::getAddOnDirectory() . '/DZ/ImageShield/fonts/Arial.ttf';
    }

    private static function assertGdExtensionLoaded(): void
    {
        if (!extension_loaded('gd')) {
            throw new RuntimeException('PHP extensions GD is not loaded');
        }
    }
}
