<?php 
/**
 * Pimcore
 *
 * This source file is available under two different licenses:
 * - GNU General Public License version 3 (GPLv3)
 * - Pimcore Enterprise License (PEL)
 * Full copyright and license information is available in
 * LICENSE.md which is distributed with this source code.
 *
 * @copyright  Copyright (c) 2009-2016 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     GPLv3 and PEL
 */

namespace Pimcore\Image;

abstract class Adapter
{

    /**
     * @var int
     */
    protected $width;

    /**
     * @var int
     */
    protected $height;

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

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

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

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

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

    /**
     * @param $height
     * @return $this
     */
    public function setHeight($height)
    {
        $this->height = $height;

        return $this;
    }

    /**
     * @return int
     */
    public function getHeight()
    {
        return $this->height;
    }

    /**
     * @param $width
     * @return $this
     */
    public function setWidth($width)
    {
        $this->width = $width;

        return $this;
    }

    /**
     * @return int
     */
    public function getWidth()
    {
        return $this->width;
    }


    /**
     * @return void
     */
    protected function removeTmpFiles()
    {
        // remove tmp files
        if (!empty($this->tmpFiles)) {
            foreach ($this->tmpFiles as $tmpFile) {
                if (file_exists($tmpFile)) {
                    unlink($tmpFile);
                }
            }
        }
    }


    /**
     * @param  $colorhex
     * @return array
     */
    public function colorhex2colorarray($colorhex)
    {
        $r = hexdec(substr($colorhex, 1, 2));
        $g = hexdec(substr($colorhex, 3, 2));
        $b = hexdec(substr($colorhex, 5, 2));

        return [$r, $g, $b, 'type' => 'RGB'];
    }


    /**
     * @param  $width
     * @param  $height
     * @return self
     */
    public function resize($width, $height)
    {
        return $this;
    }

    /**
     * @param  $width
     * @return self
     */
    public function scaleByWidth($width, $forceResize = false)
    {
        if ($forceResize || $width <= $this->getWidth() || $this->isVectorGraphic()) {
            $height = round(($width / $this->getWidth()) * $this->getHeight(), 0);
            $this->resize(max(1, $width), max(1, $height));
        }

        return $this;
    }

    /**
     * @param  $height
     * @return self
     */
    public function scaleByHeight($height, $forceResize = false)
    {
        if ($forceResize || $height < $this->getHeight() || $this->isVectorGraphic()) {
            $width = round(($height / $this->getHeight()) * $this->getWidth(), 0);
            $this->resize(max(1, $width), max(1, $height));
        }

        return $this;
    }

    /**
     * @param  $width
     * @param  $height
     * @return self
     */
    public function contain($width, $height)
    {
        $x = $this->getWidth() / $width;
        $y = $this->getHeight() / $height;
        if ($x <= 1 && $y <= 1 && !$this->isVectorGraphic()) {
            return $this;
        } elseif ($x > $y) {
            $this->scaleByWidth($width);
        } else {
            $this->scaleByHeight($height);
        }

        return $this;
    }

    /**
     * @param  $width
     * @param  $height
     * @param string $orientation
     * @return self
     */
    public function cover($width, $height, $orientation = "center", $doNotScaleUp = true)
    {
        $scaleUp = $doNotScaleUp ? false : true;

        $ratio = $this->getWidth() / $this->getHeight();

        if (($width / $height) > $ratio) {
            $this->scaleByWidth($width, $scaleUp);
        } else {
            $this->scaleByHeight($height, $scaleUp);
        }

        if ($orientation == "center") {
            $cropX = ($this->getWidth() - $width)/2;
            $cropY = ($this->getHeight() - $height)/2;
        } elseif ($orientation == "topleft") {
            $cropX = 0;
            $cropY = 0;
        } elseif ($orientation == "topright") {
            $cropX = $this->getWidth() - $width;
            $cropY = 0;
        } elseif ($orientation == "bottomleft") {
            $cropX = 0;
            $cropY = $this->getHeight() - $height;
        } elseif ($orientation == "bottomright") {
            $cropX = $this->getWidth() - $width;
            $cropY = $this->getHeight() - $height;
        } elseif ($orientation == "centerleft") {
            $cropX = 0;
            $cropY = ($this->getHeight() - $height)/2;
        } elseif ($orientation == "centerright") {
            $cropX = $this->getWidth() - $width;
            $cropY = ($this->getHeight() - $height)/2;
        } elseif ($orientation == "topcenter") {
            $cropX = ($this->getWidth() - $width)/2;
            $cropY = 0;
        } elseif ($orientation == "bottomcenter") {
            $cropX = ($this->getWidth() - $width)/2;
            $cropY = $this->getHeight() - $height;
        } else {
            $cropX = null;
            $cropY = null;
        }

        if ($cropX !== null && $cropY !== null) {
            $this->crop($cropX, $cropY, $width, $height);
        } else {
            \Logger::error("Cropping not processed, because X or Y is not defined or null, proceeding with next step");
        }

        return $this;
    }

    /**
     * @param $width
     * @param $height
     * @return $this
     */
    public function frame($width, $height)
    {
        return $this;
    }

    /**
     * @param  int $tolerance
     * @return self
     */
    public function trim($tolerance)
    {
        return $this;
    }

    /**
     * @param $angle
     * @return $this
     */
    public function rotate($angle)
    {
        return $this;
    }

    /**
     * @param  $x
     * @param  $y
     * @param  $width
     * @param  $height
     * @return self
     */
    public function crop($x, $y, $width, $height)
    {
        return $this;
    }


    /**
     * @param  $color
     * @return self
     */
    public function setBackgroundColor($color)
    {
        return $this;
    }

    /**
     * @param  $image
     * @return self
     */
    public function setBackgroundImage($image)
    {
        return $this;
    }


    /**
     * @param  $x
     * @param  $y
     * @return self
     */
    public function roundCorners($x, $y)
    {
        return $this;
    }

    /**
     * @param string $image
     * @param int $x
     * @param int $y
     * @param int $alpha
     * @param string $origin Origin of the X and Y coordinates (top-left, top-right, bottom-left, bottom-right or center)
     * @return self
     */
    public function addOverlay($image, $x = 0, $y = 0, $alpha = 100, $composite = "COMPOSITE_DEFAULT", $origin = 'top-left')
    {
        return $this;
    }

    /**
     * @param $image
     * @param string $composite
     * @return $this
     */
    public function addOverlayFit($image, $composite = "COMPOSITE_DEFAULT")
    {
        return $this;
    }

    /**
     * @param  $image
     * @return self
     */
    public function applyMask($image)
    {
        return $this;
    }

    /**
     * @param $width
     * @param $height
     * @param $x
     * @param $y
     * @return self
     */
    public function cropPercent($width, $height, $x, $y)
    {
        if ($this->isVectorGraphic()) {
            // rasterize before cropping
            $dimensions = $this->getVectorRasterDimensions();
            $this->resize($dimensions["width"], $dimensions["height"]);
        }

        $originalWidth = $this->getWidth();
        $originalHeight = $this->getHeight();

        $widthPixel = ceil($originalWidth * ($width / 100));
        $heightPixel = ceil($originalHeight * ($height / 100));
        $xPixel = ceil($originalWidth * ($x / 100));
        $yPixel = ceil($originalHeight * ($y / 100));

        return $this->crop($xPixel, $yPixel, $widthPixel, $heightPixel);
    }

    /**
     * @return self
     */
    public function grayscale()
    {
        return $this;
    }

    /**
     * @return self
     */
    public function sepia()
    {
        return $this;
    }

    /**
     * @return self
     */
    public function sharpen()
    {
        return $this;
    }

    /**
     * @return self
     */
    public function mirror($mode)
    {
        return $this;
    }

    /**
     * @param int $radius
     * @param float $sigma
     * @return $this|Adapter
     */
    public function gaussianBlur($radius = 0, $sigma = 1.0)
    {
        return $this;
    }

    /**
     * @param int $brightness
     * @param int $saturation
     * @param int $hue
     * @return $this
     */
    public function brightnessSaturation($brightness = 100, $saturation = 100, $hue = 100)
    {
        return $this;
    }

    /**
     * @abstract
     * @param  $imagePath
     * @return self
     */
    abstract public function load($imagePath, $options = []);


    /**
     * @param $path
     * @param null $format
     * @param null $quality
     * @return mixed
     */
    abstract public function save($path, $format = null, $quality = null);


    /**
     * @abstract
     * @return void
     */
    abstract protected function destroy();

    /**
     *
     */
    public function preModify()
    {
        if ($this->getModified()) {
            $this->reinitializeImage();
        }
    }

    /**
     *
     */
    public function postModify()
    {
        $this->setModified(true);
    }

    /**
     * @return void
     */
    protected function reinitializeImage()
    {
        $tmpFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . "/" . uniqid() . "_pimcore_image_tmp_file.png";
        $this->tmpFiles[] = $tmpFile;

        $this->reinitializing = true;
        $this->save($tmpFile); // do not optimize image
        $this->destroy();
        $this->load($tmpFile);
        $this->reinitializing = false;

        $this->modified = false;
    }

    /**
     *
     */
    public function __destruct()
    {
        $this->destroy();
        $this->removeTmpFiles();
    }


    /**
     * @return bool
     */
    public function isVectorGraphic()
    {
        return false;
    }

    /**
     * @return array
     */
    public function getVectorRasterDimensions()
    {
        $targetWidth = 5000;
        $factor = $targetWidth / $this->getWidth();

        return [
            "width" => $this->getWidth() * $factor,
            "height" => $this->getHeight() * $factor
        ];
    }

    /**
     * @param string $type
     * @return $this
     */
    public function setColorspace($type = "RGB")
    {
        return $this;
    }

    /**
     * @param boolean $useContentOptimizedFormat
     */
    public function setUseContentOptimizedFormat($useContentOptimizedFormat)
    {
        $this->useContentOptimizedFormat = $useContentOptimizedFormat;
    }

    /**
     * @return boolean
     */
    public function getUseContentOptimizedFormat()
    {
        return $this->useContentOptimizedFormat;
    }

    /**
     * @param boolean $modified
     */
    public function setModified($modified)
    {
        $this->modified = $modified;
    }

    /**
     * @return boolean
     */
    public function getModified()
    {
        return $this->modified;
    }

    /**
     * @param bool $value
     */
    public function setIsAlphaPossible($value)
    {
        $this->isAlphaPossible = $value;
    }
}
