<?php declare(strict_types=1);

namespace App\Captcha\Images;

use App\Captcha\Contracts\Image as ImageContract;
use App\Captcha\Dto\Coordinators;
use App\Captcha\Exceptions\CaptchaException;

final class Image implements ImageContract
{
    private readonly \GdImage $image;

    public function __construct(int $width, int $height) {
        $this->image = imagecreatetruecolor($width, $height);
    }

    public function getWidth(): int
    {
        return imagesx($this->image);
    }

    public function getHeight(): int
    {
        return imagesy($this->image);
    }

    public function insertBackground(string $pathToFile): ImageContract
    {
        list($backgroundWidth, $backgroundHeight) = getimagesize($pathToFile);
        $background = $this->createImageFromPathToFile($pathToFile);
        imagecopyresampled(
            dst_image: $this->image,
            src_image: $background,
            dst_x: 0,
            dst_y: 0,
            src_x: 0,
            src_y: 0,
            dst_width: $this->getWidth(),
            dst_height: $this->getHeight(),
            src_width: $backgroundWidth,
            src_height: $backgroundHeight
        );
        imagedestroy($background);

        return $this;
    }

    public function addText(string $text, int $x, int $y, float $size, float $angle, string $hexColor, string $fontName): Coordinators
    {
        $y += intval($size);
        $result = imagefttext(
            image: $this->image,
            size: $size,
            angle: $angle,
            x: $x,
            y: $y,
            color: $this->colorConvertHexToColorIndex($hexColor),
            font_filename: $fontName,
            text: $text
        );

        return new Coordinators(
            x1: $result[0],
            y1: $result[1],
            x2: $result[2],
            y2: $result[3],
            x3: $result[4],
            y3: $result[5],
            x4: $result[6],
            y4: $result[7]
        );
    }

    public function addLine(int $x1, int $y1, int $x2, int $y2, string $hexColor): self
    {
        imageline(
            image: $this->image,
            x1: $x1,
            y1: $y1,
            x2: $x2,
            y2: $y2,
            color: $this->colorConvertHexToColorIndex($hexColor)
        );

        return $this;
    }

    public function encode(): string
    {
        ob_start();
        imagealphablending($this->image, false);
        imagesavealpha($this->image, true);
        imagepng($this->image, null, -1);
        $mime = image_type_to_mime_type(IMAGETYPE_PNG);
        $buffer = ob_get_contents();
        ob_end_clean();

        return sprintf('data:%s;base64,%s',
            $mime,
            base64_encode($buffer)
        );
    }

    public function __destruct() {
        imagedestroy($this->image);
    }

    private function createImageFromPathToFile(string $pathToFile): \GdImage
    {
        return match (mime_content_type($pathToFile)) {
            'image/jpeg' => imagecreatefromjpeg($pathToFile),
            'image/png'  => imagecreatefrompng($pathToFile),
            default => throw new CaptchaException('Couldn\'t open the file. Not a valid type. File ' . $pathToFile . '.')
        };
    }

    private function colorConvertHexToColorIndex(string $hexColor): int
    {
        $hexColor = str_replace('#', '', $hexColor);

        if (strlen($hexColor) === 3) {
            return imagecolorexact(
                image: $this->image,
                red: hexdec(str_repeat(substr($hexColor, 0, 1), 2)),
                blue: hexdec(str_repeat(substr($hexColor, 1, 1), 2)),
                green: hexdec(str_repeat(substr($hexColor, 2, 1), 2))
            );
        }

        $colorVal = hexdec($hexColor);
        return imagecolorexact(
            image: $this->image,
            red: 0xFF & ($colorVal >> 0x10),
            blue: 0xFF & ($colorVal >> 0x8),
            green: 0xFF & $colorVal
        );
    }
}