<?php defined('BLUDIT') or die('Bludit CMS.');

class Image {

    private $image;
    private $width;
    private $height;
    private $imageResized;

    public function setImage($fileName, $newWidth, $newHeight, $option="auto")
    {
        // *** Open up the file
        $this->image = $this->openImage($fileName);

        // *** Check if image was opened successfully
        if ($this->image === false) {
            return false;
        }

        // *** Get width and height
        $this->width  = imagesx($this->image);
        $this->height = imagesy($this->image);

        $this->resizeImage($newWidth, $newHeight, $option);
        return true;
    }

    public function saveImage($savePath, $imageQuality="100", $forceJPG=false, $forcePNG=false)
    {
        $extension = strtolower(pathinfo($savePath, PATHINFO_EXTENSION));

        // Remove the extension
        $filename = substr($savePath, 0,strrpos($savePath,'.'));

        if ($forcePNG) {
            $extension = 'png';
        } elseif ($forceJPG) {
            $extension = 'jpg';
        }

        $path_complete = $filename.'.'.$extension;

        switch ($extension) {
            case 'jpg':
            case 'jpeg':
                // Checking for JPG support
                if (imagetypes() & IMG_JPG) {
                    imagejpeg($this->imageResized, $path_complete, $imageQuality);
                }
                break;

            case 'gif':
                // Checking for GIF support
                if (imagetypes() & IMG_GIF) {
                    imagegif($this->imageResized, $path_complete);
                }
                break;

            case 'png':
                // *** Scale quality from 0-100 to 0-9
                $scaleQuality = round(($imageQuality/100) * 9);

                // *** Invert quality setting as 0 is best, not 9
                $invertScaleQuality = 9 - $scaleQuality;

                // Checking for PNG support
                if (imagetypes() & IMG_PNG) {
                    imagepng($this->imageResized, $path_complete, $invertScaleQuality);
                }
                break;
            case 'webp':
                // Checking for WEBP support
                if (imagetypes() & IMG_WEBP) {
                    imagewebp($this->imageResized, $path_complete, $imageQuality);
                }
                break;

            default:
                // Fail extension detection
                break;
        }

    }

    private function openImage($file)
    {
        // *** Get extension
        $extension = strtolower(strrchr($file, '.'));

        // GD may be compiled without support for some formats (often WebP, and
        // sometimes JPEG on minimal builds). Guard each decoder with both a
        // runtime capability check and a function_exists() check to avoid a
        // fatal "call to undefined function" on stripped builds.
        $types = imagetypes();
        switch($extension)
        {
            case '.jpg':
            case '.jpeg':
                if (!($types & IMG_JPG) || !function_exists('imagecreatefromjpeg')) {
                    return false;
                }
                $img = imagecreatefromjpeg($file);
                break;
            case '.gif':
                if (!($types & IMG_GIF) || !function_exists('imagecreatefromgif')) {
                    return false;
                }
                $img = imagecreatefromgif($file);
                break;
            case '.png':
                if (!($types & IMG_PNG) || !function_exists('imagecreatefrompng')) {
                    return false;
                }
                $img = imagecreatefrompng($file);
                break;
            case '.webp':
                if (!($types & IMG_WEBP) || !function_exists('imagecreatefromwebp')) {
                    return false;
                }
                $img = imagecreatefromwebp($file);
                break;
            default:
                $img = false;
                break;
        }
        return $img;
    }

    private function resizeImage($newWidth, $newHeight, $option)
    {
        // *** Get optimal width and height - based on $option
        $optionArray = $this->getDimensions($newWidth, $newHeight, $option);

        $optimalWidth  = (int) $optionArray['optimalWidth'];
        $optimalHeight = (int) $optionArray['optimalHeight'];


        // *** Resample - create image canvas of x, y size
        $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
        imagealphablending($this->imageResized, false);
        imagesavealpha($this->imageResized, true);
        imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);


        // *** if option is 'crop', then crop too
        if ($option == 'crop') {
            $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);
        }
    }

    private function getDimensions($newWidth, $newHeight, $option)
    {

        if( ($this->width < $newWidth) and ($this->height < $newHeight) )
        {
            return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height);
        }

        switch ($option)
        {
            case 'exact':
                $optimalWidth = $newWidth;
                $optimalHeight= $newHeight;
                break;
            case 'portrait':
                $optimalWidth = $this->getSizeByFixedHeight($newHeight);
                $optimalHeight= $newHeight;
                break;
            case 'landscape':
                $optimalWidth = $newWidth;
                $optimalHeight= $this->getSizeByFixedWidth($newWidth);
                break;
            case 'auto':
                $optionArray = $this->getSizeByAuto($newWidth, $newHeight);
                $optimalWidth = $optionArray['optimalWidth'];
                $optimalHeight = $optionArray['optimalHeight'];
                break;
            case 'crop':
                $optionArray = $this->getOptimalCrop($newWidth, $newHeight);
                $optimalWidth = $optionArray['optimalWidth'];
                $optimalHeight = $optionArray['optimalHeight'];
                break;
            default:
                $optionArray = $this->getSizeByAuto($newWidth, $newHeight);
                $optimalWidth = $optionArray['optimalWidth'];
                $optimalHeight = $optionArray['optimalHeight'];
                break;
        }

        return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
    }

    private function getSizeByFixedHeight($newHeight)
    {
        $ratio = $this->width / $this->height;
        $newWidth = $newHeight * $ratio;
        return $newWidth;
    }

    private function getSizeByFixedWidth($newWidth)
    {
        $ratio = $this->height / $this->width;
        $newHeight = $newWidth * $ratio;
        return $newHeight;
    }

    private function getSizeByAuto($newWidth, $newHeight)
    {
        if ($this->height < $this->width)
            // *** Image to be resized is wider (landscape)
        {
            $optimalWidth = $newWidth;
            $optimalHeight= $this->getSizeByFixedWidth($newWidth);
        }
        elseif ($this->height > $this->width)
            // *** Image to be resized is taller (portrait)
        {
            $optimalWidth = $this->getSizeByFixedHeight($newHeight);
            $optimalHeight= $newHeight;
        }
        else
            // *** Image to be resizerd is a square
        {
            if ($newHeight < $newWidth) {
                $optimalWidth = $newWidth;
                $optimalHeight= $this->getSizeByFixedWidth($newWidth);
            } else if ($newHeight > $newWidth) {
                $optimalWidth = $this->getSizeByFixedHeight($newHeight);
                $optimalHeight= $newHeight;
            } else {
                // *** Sqaure being resized to a square
                $optimalWidth = $newWidth;
                $optimalHeight= $newHeight;
            }
        }

        return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
    }

    private function getOptimalCrop($newWidth, $newHeight)
    {

        $heightRatio = $this->height / $newHeight;
        $widthRatio  = $this->width /  $newWidth;

        if ($heightRatio < $widthRatio) {
            $optimalRatio = $heightRatio;
        } else {
            $optimalRatio = $widthRatio;
        }

        $optimalHeight = $this->height / $optimalRatio;
        $optimalWidth  = $this->width  / $optimalRatio;

        return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
    }

    private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
    {
        // *** Find center - this will be used for the crop
        $cropStartX = (int) (( $optimalWidth / 2) - ( $newWidth /2 ));
        $cropStartY = (int) (( $optimalHeight/ 2) - ( $newHeight/2 ));

        $crop = $this->imageResized;

        // *** Now crop from center to exact requested size
        $this->imageResized = imagecreatetruecolor($newWidth , $newHeight);
        imagealphablending($this->imageResized, false);
        imagesavealpha($this->imageResized, true);
        imagecopyresampled($this->imageResized, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight);
    }
}
