<?php
namespace Helpers\Fichier;

use Helpers\Log\LogSysteme;
use Helpers\Outil\Color;
use Psr\Http\Message\StreamInterface;

class AvatarFileService
{
    private string $_directory;

    public const FORMAT = [
        'small' => ['file_name' => 'sm','size' => 16],
        'medium' => ['file_name' => 'md','size' => 32],
        'large' => ['file_name' => 'lg','size' => 64],
    ];

//_____________________________________________________________________________
// Constructor

    public function __construct(string $directory)
    {
        if (! is_dir($directory)) {
            // Create dir
            Fichier::touchDir($directory);
            LogSysteme::prefixErrorLog('Creating "'.$directory.'"');
        }
        $this->_directory = (string) realpath($directory);
    }

//_____________________________________________________________________________
// Accessor

    /**
     * @return string main directory absolute path without trailing slash
     */
    protected function getDirectory() :string
    {
        return $this->_directory;
    }
    public function getSize(string $format) :int
    {
        return self::FORMAT[ $format ]['size'];
    }

    public function getBgColor(string $seed) :string
    {
        /** @phpstan-ignore-next-line */
        $test = unpack('cchars/nint', substr($seed, 0, 4))['int']??0;
        srand($test);

        return Color::hsl2hex([rand(0, 255)/255/* HUE */, 0.5, 0.5]);
    }

    public function getAvatarPath(
        string $user_code,
        string $format_key
    ) :?string {
        $format = self::FORMAT[ $format_key ];
        $file_path = Fichier::buildPath(
            $this->getDirectory(),
            Fichier::filterFileName($user_code),
            Fichier::filterFileName($format['file_name'].'.jpeg')
        );
        if (!is_file($file_path)) {
            return null;
        }
        return $file_path;
    }

//_____________________________________________________________________________
// Modifier

    /**
     * @param array{x:int,y:int,width:int,height:int}|null $crop_info
     */
    public function addAvatar(
        string $user_code,
        StreamInterface $avatar_file,
        ?array $crop_info
    ) :void {
        $image_source = $this->createImageFromFile($avatar_file);

        // TODO : convert png to jpeg ?

        foreach (self::FORMAT as $format) {
            $image_dest = $this->_resize($image_source, $format['size'], $crop_info);
            $this->_saveImage($image_dest, $user_code, $format['file_name']);
        }
    }

    public function delete(string $user_code) :bool
    {
        $success = true;
        foreach (array_keys(self::FORMAT) as $key) {
            $success = unlink($this->getAvatarPath($user_code, $key)) && $success;
        }
        return $success;
    }
//_____________________________________________________________________________
// Sub-routine

    /**
     * @param \GdImage $source_file
     * @param array{x:int,y:int,width:int,height:int}|null $crop_info
     * @return \GdImage
     */
    private function _resize($source_file, int $size, ?array $crop_info)
    {
        if ($crop_info === null) {
            $crop_info = [
                'x' => 0, 'y' => 0,
                'width' => imagesx($source_file)/*width*/,
                'height' => imagesy($source_file)/*height*/,
            ];
        }
        
        $image_dest = imagecreatetruecolor($size, $size);
        if ($image_dest === false) {
            throw new \Exception('imagecreatetruecolor failed');
        }
        $b = imagecopyresized(
            $image_dest,
            $source_file,
            0,
            0,
            $crop_info['x'],
            $crop_info['y'],
            $size,
            $size,
            $crop_info['width'],
            $crop_info['height']
        );
        if ($b === false) {
            throw new \Exception('imagecopyresized failed');
        }
        return $image_dest;
    }

    /**
     * @param \GdImage $image
     */
    private function _saveImage(
        $image,
        string $user_code,
        string $file_plain_name
    ) :void {
        $path = Fichier::buildPath(
            $this->getDirectory(),
            $user_code,
            $file_plain_name.'.jpeg'
        );

        // Create dir
        Fichier::touchDir(dirname($path));

        // Save image
        $b = imagejpeg($image, $path);
        if ($b === false) {
            throw new \Exception('imagejpeg failed for "'.$path.'"');
        }
    }

//_____________________________________________________________________________
// Tools

    

    /**
     * creates a image ressource from file (or url)
     * @param StreamInterface $avatar_file file stream
     * @return \GdImage
     */
    private function createImageFromFile(StreamInterface $avatar_file)
    {
        $GDfile = imagecreatefromstring($avatar_file->getContents());
        if ($GDfile === false) {
            throw new \Exception('!!!');
        }
        return $GDfile;
    }
}
