<?php

/**
 * @file
 *
 * Image
 */

namespace Pyramid\Component\Image;

use Exception;

class Image {

    /**
     * 比例系数
     * @var int
     */
    public $aspect = 1;
    
    /**
     * 目标图资源
     * @var resource $dst_im
     */
    protected $dst_im;
    
    /**
     * 源图资源
     * @var resource $src_im
     */
    protected $src_im;

    /**
     * 是否需要交换src_im和dst_im
     */
    protected $needsExchange = false;

    /**
     * 源图信息
     * @var array $info
         0: width
         1: height
         2:
         3: width= height=
         bits: 位数 (不一定有)
         channels: 3RGB 4CMYK (不一定有)
         mine: image/jpeg
         
         ext: 后缀
         width
         height
     */
    protected $info = array();
    
    /**
     * 背景设置
     */
    protected $background = array(255,255,255,0);
    
    /**
     * 是否为可靠的图片格式
     */
    protected $is_image = true;

    /*
     * 获取实例
     */
    public static function gethandler($img, $isBinary = false) {
        if (extension_loaded('Imagick')) {
            return new Imagick($img, $isBinary);
        } else {
            return new Image($img, $isBinary);
        }
    }

    /**
     * 析构函数
     */
    public function __construct($img, $isBinary = false) {
        if ($img) {
            if (!$isBinary) {
                if ($info = getimagesize($img)) {
                    $this->info += $info;
                }
                $file = $img;
                $img = file_get_contents($img);
            } elseif ($info = getimagesizefromstring($img)) {
                $this->info += $info;
            }
            $this->info['ext'] = $this->extension(substr($img,0,64));
            if ($this->info['ext'] == 'webp') {
                $this->src_im = @imagecreatefromwebp($file);
            } else {
                $this->src_im = @imagecreatefromstring($img);
            }
            if ($this->src_im === false) {
                $this->is_image = false;
                throw new Exception('unvalidated image source.');
            }
            $this->info['height'] = $this->getHeight();
            $this->info['width'] = $this->getWidth();
        }
    }
    
    /**
     * 是否为可靠的图片
     */
    public function isImage() {
        return $this->is_image;
    }
    
    /**
     * 销毁资源以释放内存
     */
    public function __destruct() {
        $this->dst_im && imagedestroy($this->dst_im);
        $this->src_im && imagedestroy($this->src_im);
        $this->dst_im = null;
        $this->src_im = null;
    }
    
    /**
     * 返回图像宽度
     */
    public function getWidth() {
        return imagesx($this->src_im);
    }
    
    /**
     * 返回图像高度
     */
    public function getHeight() {
        return imagesy($this->src_im);
    }
    
    /**
     * 缩略图
     * @param int $width
     * @param int $height
     * @param int $bestfit true:最大限度容纳图片
     * @param int $background array(r,g,b,a)
     */
    public function thumbnail($width = 0, $height = 0, $bestfit = true, $background = array()) {
        if ($background) {
            $this->setBackgroud($background);
        }
        return $this->autoCrop($width, $height, 0, 0, $bestfit);
    }

    /**
     * 剪切
     * @param int $x
     * @param int $y
     * @param int $width
     * @param int $height
     * @param int $pct
     */
    public function crop($x = 0, $y = 0, $width = 0, $height = 0, $pct = 100) {
        $this->exchange();
        list($r, $g, $b, $a) = $this->background;
        $src_width  = imagesx($this->src_im);
        $src_height = imagesy($this->src_im);
        $width  = $width ? $width : $src_width;
        $height = $height ? $height : $src_height;
        if ($x == 'auto') {
            $x = max(0, floor(($src_width-$width)/2));
        }
        if ($y == 'auto') {
            $y = max(0, floor(($src_height-$height)/2));
        }
        $im = imagecreatetruecolor($width, $height);
        imagesavealpha($im, true);
        $bg = imagecolorallocatealpha($im, $r, $g, $b, $a);
        imagefill($im, 0, 0, $bg);
        $width  = min($width, $src_width - $x);
        $height = min($height, $src_height - $y);
        imagecopymerge($im, $this->src_im, 0, 0, $x, $y, $width, $height, $pct);        
        $this->dst_im = $im;
        $this->needsExchange = true;
        
        return $this;
    }
    
    /**
     * 旋转
     * @param int $degrees
     * @param int $bgd_color
     */
    public function rotate($degrees = 0, $bgd_color = 0xFFFFFF) {
        $this->exchange();
        $this->dst_im = imagerotate($this->src_im, $degrees, $bgd_color);
        $this->needsExchange = true;
        return $this;
    }
    
    /**
     * 缩放
     * @param int $width
     * @param int $height
     * @param bool $tensile false:定比拉伸
     */
    public function scale($width = 0, $height = 0, $tensile = false) {
        $this->exchange();
        list($r, $g, $b, $a) = $this->background;        
        $src_width  = imagesx($this->src_im);
        $src_height = imagesy($this->src_im);
        $aspect = $src_height / $src_width;
        if ($tensile && $width && $height) {        
        } elseif (($width && !$height) || ($width && $height && $aspect < $height / $width)) {
            $height = round($width * $aspect);
            $this->aspect = $src_width / $width;
        } else {
            $width = round($height / $aspect);
            $this->aspect = $src_height / $height;
        }
        $im = imagecreatetruecolor($width, $height);
        imagesavealpha($im, true);
        $bg = imagecolorallocatealpha($im, $r, $g, $b, $a);
        imagefill($im, 0, 0, $bg);
        imagecopyresampled($im, $this->src_im, 0, 0, 0, 0, $width, $height, $src_width, $src_height);
        $this->dst_im = $im;
        $this->needsExchange = true;
        
        return $this;
    }
    
    /**
     * 缩放
     * @param float $scale
     */
    public function zoom($scale = 1) {
        $this->exchange();
        list($r, $g, $b, $a) = $this->background;
        $src_width  = imagesx($this->src_im);
        $src_height = imagesy($this->src_im);
        $width  = floor($src_width  * $scale);
        $height = floor($src_height * $scale);
        $im = imagecreatetruecolor($width, $height);
        imagesavealpha($im, true);
        $bg = imagecolorallocatealpha($im, $r, $g, $b, $a);
        imagefill($im, 0, 0, $bg);
        imagecopyresampled($im, $this->src_im, 0, 0, 0, 0, $width, $height, $src_width, $src_height);
        $this->dst_im = $im;
        $this->needsExchange = true;
        
        return $this;
    }
    
    /**
     * 自动缩放后剪切
     * @param int $width
     * @param int $height
     * @param int $x
     * @param int $y
     * @param boolean $force
     * @param int $pct
     */
    public function autoCrop($width = 0, $height = 0, $x = 0, $y = 0, $force = true, $pct = 100) {
        $this->exchange();
        $src_width  = imagesx($this->src_im);
        $src_height = imagesy($this->src_im);
        $zoom = max($width/$src_width, $height/$src_height);
        if (!$force) { //非强制时,原图长宽均小于缩放后数值,则不做任何处理
            if (($width && $src_width >= $width) || ($height && $src_height >= $height)) {} else {
                return $this->exchange();
            }
        }
        return $this->zoom($zoom)->crop($x,$y,$width,$height);
    }

    /**
     * 将目标图资源设置为源图资源
     */
    public function exchange() {
        if ($this->needsExchange) {
            $tmp = $this->src_im;
            $this->src_im = $this->dst_im;
            $this->dst_im = $tmp;
            $this->needsExchange = false;
        }
        return $this;
    }
    
    //输出jpeg
    public function jpeg($filename = null, $quality = 90) {
        return $this->output('imagejpeg', $filename, $quality);
    }
    
    //输出gif
    public function gif($filename = null) {
        return $this->output('imagegif', $filename);
    }
    
    //输出png
    public function png($filename = null) {
        return $this->output('imagepng', $filename);
    }
    
    //输出webp
    public function webp($filename = null, $quality = 90) {
        return $this->output('imagewebp', $filename, $quality);
    }
    
    //自动后缀输出
    public function autoSave($filename = null, $quality = 90) {
        $ext = strtolower(strrchr($filename, '.'));
        if ($ext != '.'.$this->info['ext']) {
            $filename = $filename . '.' . $this->info['ext'];
        }
        switch($this->info['ext']) {
            case 'jpg':
                return $this->jpeg($filename, $quality);
            case 'gif':
                return $this->gif($filename);
            case 'png':
                return $this->png($filename);
            case 'webp':
                return $this->webp($filename, $quality);
            default:
                return false;
        }
    }
    
    //输出
    public function output($func) {
        if (!function_exists($func)) {
            return false;
        }
        if (!$this->dst_im) {
            $this->dst_im = $this->src_im;
        }
        $args = func_get_args();
        array_shift($args);
        if (isset($args[0])) {
            return $func($this->dst_im, $args[0], isset($args[1]) ? $args[1] : null);
        }
        else {
            ob_start();
            $func($this->dst_im, null, isset($args[1]) ? $args[1] : null);
            $content = ob_get_clean();
            return $content;
        }
    }
    
    //获取信息
    public function info($dst = '') {
        if ($dst) {
            $this->info['width']  = @imagesx($this->dst_im);
            $this->info['height'] = @imagesy($this->dst_im);
        } else {
            $this->info['width']  = imagesx($this->src_im);
            $this->info['height'] = imagesy($this->src_im);
        }
        return $this->info; 
    }
    
    //获取RGB数
    public function rgb() {
        $rr = $gg = $bb = 0; $total = 100;
        $im = $this->dst_im;
        if (!$im) { $im = $this->src_im; }
        if ($im) {
            $total = 0;
            $width = imagesx($im);
            $height = imagesy($im);
            if ($width && $height) {
                for($x = 0; $x < $width; $x++) {
                    for($y = 0; $y < $height; $y++) {
                        $rgb = imagecolorat($im, $x, $y);
                        $r = ($rgb >> 16) & 0xFF;
                        $g = ($rgb >> 8) & 0xFF;
                        $b = $rgb & 0xFF;
                        $rr += $r;
                        $gg += $g;
                        $bb += $b;
                        $total++;
                    }
                }
            }
        }
        return array(
            'r' => round($rr / $total),
            'g' => round($gg / $total),
            'b' => round($bb / $total),
        );
    }
    
    //获取源图资源
    public function getSource() {
        return $this->src_im;
    }
    
    //设置源图资源
    public function setSource($im) {
        $this->src_im = $im;
        return $this;
    }
    
    //获取目标图资源
    public function getDestination() {
        return $this->dst_im;
    }
    
    //设置目标图资源
    public function setDestination($im) {
        $this->dst_im = $im;
        return $this;
    }
    
    //设置背景
    public function setBackgroud(array $bg = array()) {
        $this->background = $bg;
        return $this;
    }
    
    //根据获取扩展名
    public function extension($binaryData) {
        $bin  = substr($binaryData, 0, 2);
        $str  = @unpack('C2chars', $bin);
        $code = intval($str['chars1'] . $str['chars2']);
        $ext  = '';
        switch ($code) {
            case 7790:  $ext = 'exe';   break;
            case 7784:  $ext = 'midi';  break;
            case 8297:  $ext = 'rar';   break;
            case 8075:  $ext = 'zip';   break;
            case 255216:$ext = 'jpg';   break;
            case 7173:  $ext = 'gif';   break;
            case 6677:  $ext = 'bmp';   break;
            case 13780: $ext = 'png';   break;
            case 8273:  $ext = 'webp';  break;
        }
        if ($str['chars1'] == '-1' && $str['chars2'] == '-40') {
            $ext = 'jpg';
        } elseif ($str['chars1'] == '-119' && $str['chars2'] == '80') {
            $ext = 'png';
        }
        //mozJPEG @不太准
        if ($ext == 'jpg') {
            if ($v = @unpack('C10chars', substr($binaryData,0,10))) {
                $s = implode('',$v);
                if ($s === '25521625522401674707370') {
                    $this->info['mozJPEG'] = true;
                }
            }
        }

        return $ext;
    }
    
    //是否为mozJPEG
    public function isMozJpeg() {
        return !empty($this->info['mozJPEG']);
    }
    
    public function __call($method, $args) {
        if ($method == 'blurImage') {
            imagefilter($this->dst_im, IMG_FILTER_GAUSSIAN_BLUR);
        }
        return $this;
    }

}
