<?php

namespace Mtc\Core\Images;

use Illuminate\Support\Facades\App;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\FileBag;

/**
 * Class ImageUploader.
 *
 * Handles image uploads based on folder settings
 *
 * @author mtc.
 * @author Aleksey Lavrinenko <aleksey.lavrinenko@mtcmedia.co.uk>
 * @version 18.08.2016.
 */
class ImageUploader
{
    /**
     * @return self
     */
    public static function newInstance($folders)
    {
        $class = App::make(__CLASS__);
        $class->setFolders($folders);

        return $class;
    }

    /**
     * Image settings array from the $image_folders
     * @var array
     */
    protected $folders;

    /**
     * Flag to either rename the image on upload or not.
     * Set to false if when re-uploading existing images
     *
     * @var bool
     */
    public $rename;

    /**
     * ImageUploader constructor.
     * @param array $folders
     * @param bool $rename
     */
    public function __construct($folders = [], $rename = true)
    {
        $this->folders = $folders;
        $this->rename = $rename;
    }

    /**
     * Moved the folder parameter from constructor to a setter
     * to fix the problem with 5.4 make method - it doesn't
     * accept additional parameters.
     * TODO: upgrade this to makeWith() in the future.
     * See https://github.com/laravel/internals/issues/391#issuecomment-285204109
     * for reference
     * @param $folders
     */
    public function setFolders($folders)
    {
        if (! isset($folders)) {
            throw new \RuntimeException("'folders' parameter not defined");
        }

        $this->folders = $folders;
    }

    /**
     * uploadImage()
     *
     * Used to validate an image upload, upload the image, resize, crop, etc.
     *
     * @param UploadedFile $file
     * @param int $item_id
     * @return array|bool filename on success. false on error.
     */
    public function uploadImage(UploadedFile $file, $item_id = 0)
    {
        $extension = $file->getClientOriginalExtension();

        // Get and clean the image name
        $new_name = $this->newName($file, $item_id);

        $allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];

        if (
            in_array(strtolower($extension), $allowed_extensions)
            && str_contains(\mime_content_type($file->getPathname()), 'image')
        ) {
            // Do all the copying and resizing
            foreach ($this->folders as $resize) {
                if (! file_exists(SITE_PATH.'/'.$resize['path'])) {
                    //var_dump($resize['path']); exit;
                    mkdir(SITE_PATH.'/'.$resize['path'], 0o755, true);
                }

                $new_file_path = SITE_PATH.'/'.$resize['path']."/{$new_name}";

                // if destination is the same as original, just skip this file. This is handy for using the reupload
                // script with existing images as source (i.e. originals folder)
                if ($file->getPathname() === $new_file_path) {
                    continue;
                }

                if (! copy($file->getPathname(), $new_file_path)) {
                    die('Cant copy to:'.$new_file_path);
                }

                $error = self::processImage($new_name, $new_file_path, $extension, $resize);
            }

            return array('error' => $error, 'name' => $new_name);
        } else {
            return false;
        }
    }

    /**
     * Process image resizing & cropping
     * @param string $new_name target file name
     * @param string $new_file_path path to target file
     * @param string $extension target file extension
     * @param array $resize array of image folders structure for size to process
     * @return string $error info about error encountered;
     */
    public static function processImage($new_name, $new_file_path, $extension, $resize)
    {
        $error = '';
        // Get Image size
        $size = getimagesize($new_file_path);
        $size['width'] = $size[0];
        $size['height'] = $size[1];

        // Check & process CMYK Images
        if (! empty($size['channels']) && $size['channels'] == 4) {
            image_cmyk_to_rgb($resize['path'], $new_name);
            $error = 'This image was converted automatically to RGB from CMYK.
                This can sometimes result in colours not looking as sharp.
                If this is an issue, we advise to convert images using a graphics package before uploading.';
        }

        // If crop image, set specific sizes
        if ($resize['crop']) {
            $target_width = $resize['width'];
            $target_height = $resize['height'];
            $ratio = $target_width / $target_height;

            //if the output is to be landscape
            if ($resize['width'] >= $resize['height']) {
                if ($resize['width'] == $resize['height'] && $size['width'] > $size['height']) {
                    //if uploaded is square
                    $resize['width'] = 50_000;
                } elseif ($size['width'] > $size['height']) {
                    //if uploaded is landscape
                    $resize['height'] = $resize['width'];
                    $resize['width'] = $target_width * $ratio;
                } else {
                    //if uploaded is portrait
                    $resize['height'] = 50_000;
                }
            } else {
                //if the output is to be potrait
                $resize['width'] = 50_000;
            }
        }

        $wasResized = false;

        if ($resize['forced']) {
            the_image_resize($resize['width'], $resize['height'], $resize['path'], $new_name);
            $wasResized = true;
        } else {
            if ($size['width'] > $resize['width'] || $size['height'] > $resize['height']) {
                the_image_resize($resize['width'], $resize['height'], $resize['path'], $new_name);
                $wasResized = true;
            }
        }

        if ($resize['crop']) {
            image_crop_centercrop($resize['path'], $new_name, $target_width, $target_height, 0, 0);
        }

        if (in_array(strtolower($extension), ['jpg', 'jpeg'])) {
            \ImageOptimizer::optimizeLossy($new_file_path);
        }

        \ImageOptimizer::optimizeLosslessly($new_file_path, IMAGE_OPTIMISE_LOSSLESS);

        // Create WebP image if it wasn't already created during resize
        // the_image_resize() calls webpImage(), so only call it if resize was skipped
        if (! $wasResized && ! webpImage($new_file_path)) {
            $error .= 'WebP image not created. ';
        }

        return $error;
    }

    /**
     * Returns new file name to save the image with
     *
     * @param UploadedFile $file
     * @param int $item_id
     * @return null|string
     */
    protected function newName(UploadedFile $file, $item_id = 0)
    {
        $new_name = $file->getClientOriginalName();

        if (! $this->rename) {
            return $new_name;
        }

        /*
         * Add more item detail to image name
         */
        $namePrefix = '';
        if (IMAGES_SEO_NAMES && $item_id > 0) {
            $item = new \Item($item_id);

            if ($item->id > 0) {
                $brandName = $item->brand_name ? $item->brand_name.'_' : '';
                $namePrefix = strtolower(config('app.name').'_'.$brandName.$item->name.'_');
            }
        }

        $image_name = $namePrefix.time().$file->getClientOriginalName();

        // replace special chars and separate by dash
        $regex = '#([^a-zA-Z\-0-9\.])#';

        // remove duplicate dashes
        $duplicate = '#-{2,}#';

        $image_name = preg_replace($regex, '-', $image_name);

        return preg_replace($duplicate, '-', $image_name);
    }

    /**
     * Helper method, returns an UploadedFile by key.
     *
     * @param $key
     * @return UploadedFile
     */
    public function file($key)
    {
        return new FileBag($_FILES)->get($key);
    }
}
