<?php

namespace IZON\Thumber\Factory;

use IZON\Filesystem\FilesystemSourceInterface;
use IZON\IO\RasterImageInterface;
use IZON\Thumber\Exceptions\ThumbnailException;
use IZON\Thumber\Image\IImageURLBuilder;
use IZON\Thumber\Image\ImageURLBuilder;
use IZON\Thumber\ImageCache\ImageCache;
use IZON\Thumber\ImageConverter\IImageConverterFactory;
use IZON\Thumber\ImageConverter\ImagineImageConverterFactory;
use IZON\Thumber\ImageResizer\IImageResizer;
use IZON\Thumber\ImageResizer\ImageResizer;
use IZON\Thumber\ImageResizer\Strategies\ContainImageResizerStrategy;
use IZON\Thumber\ImageResizer\Strategies\CoverImageResizerStrategy;
use IZON\Thumber\ImageResizer\Strategies\CropImageResizerStrategy;
use IZON\Thumber\Services\ThumbnailService;
use IZON\Thumber\Services\ThumbnailServiceInterface;
use IZON\Thumber\Thumbnailer;


/**
 * creates thumbnail service
 * @author Lukáš Linhart
 */
class ThumbnailServiceFactory {

    /**
     * @var FilesystemSourceInterface
     */
    protected $filesystemSource;

    /**
     * @var string temporary directory
     */
    protected $tmpDir;

    /**
     * @var string driver name
     */
    protected $driver;

    /**
     * @var string cache directory
     */
    protected $cacheDir;

    /**
     * @var string builder url path
     */
    protected $builderPath;

    /**
     * @var bool
     */
    protected $interlace = false;

    /**
     * @var RasterImageInterface|null
     */
    protected $noImage = null;

    /**
     * @var RasterImageInterface|null
     */
    protected $watermarkImage = null;

    /**
     * @var array
     */
    protected $watermarkConfig = [];

    /**
     * factory construtor
     *
     * @param string $rootDir root directory
     * @param string $tmpDir tmp directory
     * @param string $cacheDir cache diretory
     * @param string $builderPath url path for builder
     */
    public function __construct(
        FilesystemSourceInterface $filesystemSource,
        string $tmpDir,
        string $cacheDir,
        string $builderPath
    ) {
        $this->filesystemSource = $filesystemSource;
        $this->tmpDir = $tmpDir;
        $this->cacheDir = $cacheDir;
        $this->builderPath = $builderPath;
        $this->driver = 'gd';
    }
    /**
     * set driver for thumbnail service
     *
     * @param string $driver driver gd|imagemagick
     */
    public function setDriver(string $driver) {
        $this->driver = $driver;
    }
    /**
     *
     * @param bool $interlace if true progressive/interlace image internal formatting is used
     */
    function setInterlace(bool $interlace) {
        $this->interlace = $interlace;
    }

    /**
     * sets location of image to be used if null of empty string is passed to thumber
     * @param string|null $originUID
     * @param string $fileUID
     * @throws ThumbnailException
     */
    public function setNoImage(
        ?string $originUID,
        string $fileUID
    ): void {
        if( !$this->filesystemSource->hasFilesystem($originUID) ) {
            throw new ThumbnailException("Filesystem with UID = ". $originUID ." is not registered");
        }
        $fileSystem = $this->filesystemSource->getFilesystem($originUID);
        if( !$fileSystem->has($fileUID) ) {
            throw new ThumbnailException("File with UID = ". $fileUID ." is not present in FileSystem = ". $originUID);
        }
        $this->noImage = $fileSystem->getFile($fileUID);
    }

    /**
     * @return RasterImageInterface|null
     */
    public function getNoImage(): ?RasterImageInterface {
        return $this->noImage;
    }

    /**
     * configures watermark to be applied to thumbnails
     * @param string $watermarkOriginUID file system to get thumbnail image from
     * @param string $watermarkFileUID image containing water mark
     * @param float $watermarkSizePercent percent of size of target image watermark will be applied to
     * @param array $watermarkAutoApplicationRules rules for automatic application of watermark (format ['originUIDPattern' => 'db-public', 'fileUIDPattern' => '*'] regular expression is used for matching)
     * @throws ThumbnailException
     */
    public function setWatermark(
        ?string $watermarkOriginUID,
        string $watermarkFileUID,
        float $watermarkSizePercent = 60.0,
        array $watermarkAutoApplicationRules = []
    ) {
        if( !$this->filesystemSource->hasFilesystem($watermarkOriginUID) ) {
            throw new ThumbnailException("Filesystem with UID = ". $watermarkOriginUID ." is not registered");
        }
        $fileSystem = $this->filesystemSource->getFilesystem($watermarkOriginUID);
        if( !$fileSystem->has($watermarkFileUID) ) {
            throw new ThumbnailException("File with UID = ". $watermarkFileUID ." is not present in FileSystem = ". $watermarkOriginUID);
        }
        $this->watermarkImage = $fileSystem->getFile($watermarkFileUID);

        $this->watermarkConfig['autoApplicationRules'] = $watermarkAutoApplicationRules;
        $this->watermarkConfig['sizePercent'] = $watermarkSizePercent;
    }

    /**
     * configures position of watermark
     * @param string $alignment
     * @param float $xPercent
     * @param float $yPercent
     * @throws ThumbnailException
     */
    public function setWatermarkPosition(
        string $alignment = 'botom-right',
        float $xPercent = 10.0,
        float $yPercent = 10.0
    ) {
        if( !in_array($alignment, Thumbnailer::SUPPORTED_ALIGNMENTS) ) {
            throw new ThumbnailException('aligment '. $alignment .' is not supported');
        }
        $this->watermarkConfig['alignment'] = $alignment;
        $this->watermarkConfig['xPercent'] = $xPercent;
        $this->watermarkConfig['yPercent'] = $yPercent;
    }


    /**
     * create thumbnail service
     *
     * @return ThumbnailServiceInterface
     */
    public function create(): ThumbnailServiceInterface {
        $imageResizer = $this->createResizer();
        $imageCache = $this->createCache();
        $imageConverterFactory = $this->createConvertor();
        $imageBuilder = $this->createURLBuilder();

        $service = new ThumbnailService($this->filesystemSource, $imageResizer, $imageCache, $imageConverterFactory);
        $service->setImageURLBuilder($imageBuilder);
        $service->setInterlace($this->interlace);
        $service->setNoImage($this->noImage);

        if( $this->watermarkImage !== null ) {
            $service->setWatermark($this->watermarkImage, $this->watermarkConfig);
        }

        return $service;
    }

    /**
     * create resizer for thumbnail service
     * @return IImageResizer
     */
    protected function createResizer(): IImageResizer {
        $strategies = [
            ImageResizer::CONTAIN => new ContainImageResizerStrategy(),
            ImageResizer::COVER => new CoverImageResizerStrategy(),
            ImageResizer::CROP => new CropImageResizerStrategy()
        ];
        $resizer = new ImageResizer($strategies);
        return $resizer;
    }

    /**
     * create image cache for thumbnail service
     * @return ImageCache
     */
    protected function createCache(): ImageCache {
        $cache = new ImageCache($this->filesystemSource->getFilesystem('image-cache'), $this->cacheDir);
        return $cache;
    }
    /**
     * create convertor factory for thumbnail service
     *
     * @return IImageConverterFactory
     */
    protected function createConvertor(): IImageConverterFactory {
        $convertor = new ImagineImageConverterFactory($this->tmpDir, $this->driver);
        return $convertor;
    }
    /**
     * create image url builder for thumbnail service
     * @return IImageURLBuilder
     */
    protected function createURLBuilder(): IImageURLBuilder {
        $builder = new ImageURLBuilder($this->filesystemSource);
        $builder->setPath($this->builderPath);
        return $builder;
    }

}
