<?php
namespace common\helpers;

use Yii;
use yii\helpers\Url;
use yii\web\Response;

/**
 * 验证码扩展类
 * @author   Leo
 * @copyright  Copyright (c) 2016 (http://www.LeoCode.net)
 */
class CaptchaAction extends \yii\captcha\CaptchaAction
{

    const CAPTCHA_CODE = 'ak:captcha_code_%s';

    /**
     * GD库方式渲染
     * @param string $code
     * @return string
     */
    protected function renderImageByGD($code)
    {
        $this->fontFile = Yii::getAlias('@common/helpers/font/DroidSansMono.ttf');
        $image = imagecreatetruecolor($this->width, $this->height);
        $backColor = imagecolorallocate(
            $image,
            (int)($this->backColor % 0x1000000 / 0x10000),
            (int)($this->backColor % 0x10000 / 0x100),
            $this->backColor % 0x100
        );
        imagefilledrectangle($image, 0, 0, $this->width - 1, $this->height - 1, $backColor);
        imagecolordeallocate($image, $backColor);
        $this->transparent = true;
        if ($this->transparent) {
            imagecolortransparent($image, $backColor);
        }

        $foreColor = imagecolorallocate(
            $image,
            (int)($this->foreColor % 0x1000000 / 0x10000),
            (int)($this->foreColor % 0x10000 / 0x100),
            $this->foreColor % 0x100
        );

        $length = strlen($code);
        $box = imagettfbbox(30, 0, $this->fontFile, $code);
        $w = $box[4] - $box[0] + $this->offset * ($length - 1);
        $h = $box[1] - $box[5];
        $scale = min(($this->width - $this->padding * 2) / $w, ($this->height - $this->padding * 2) / $h);
        $x = 10;
        $y = round($this->height * 27 / 40);

        // 添加彩色乱字符
        $chartDictionary = 'abcdefghjkmnopqrstuvwxyzABCDEFGHJKMNOPQRSTUVWXYZ2345679';
        for ($i = 0; $i < 50; $i++) {
            $chartPosition = rand(0, strlen($chartDictionary));
            $chartContent = substr($chartDictionary, $chartPosition, 1);
            $chartColor = imagecolorallocate($image, mt_rand(150, 255), mt_rand(150, 255), mt_rand(150, 255));
            $chartAngle = rand(-10, 10);
            imagettftext($image, 10, $chartAngle, rand(0, ($this->width - $this->padding * 2) - 10), rand(0, ($this->height - $this->padding * 2) - 10), $chartColor, $this->fontFile, $chartContent);
        }

        // 设置文字
        for ($i = 0; $i < $length; ++$i) {
            $fontSize = (int)(rand(26, 32) * $scale * 0.8);
            $angle = rand(-10, 10);
            $letter = $code[$i];
            $box = imagettftext($image, $fontSize, $angle, $x, $y, $foreColor, $this->fontFile, $letter);
            $x = $box[2] + $this->offset;
        }

        // 验证码添加线条
        for ($i = 0; $i < 10; $i++) {
            $lineColor = imagecolorallocate($image, rand(0, 255), rand(0, 255), rand(0, 255));
            imageline($image, rand(0, 2), rand(0, ($this->width - $this->height * 2) - 2), rand(0, ($this->width - $this->padding * 2) - 2), rand(0, ($this->width - $this->height * 2) - 2), $lineColor);
        }

        // 验证码添加噪点
        for ($i = 0; $i < 300; $i++) {
            //设置点的颜色
            $pointColor = imagecolorallocate($image, rand(0, 255), rand(0, 255), rand(0, 255));
            imagesetpixel($image, rand(0, ($this->width - $this->padding * 2)), rand(0, ($this->width - $this->height * 2) - 10), $pointColor);
        }

        imagecolordeallocate($image, $foreColor);

        ob_start();
        imagepng($image);
        imagedestroy($image);

        return ob_get_clean();
    }

    /**
     * Runs the action.
     */
    public function run()
    {
        if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) !== null) {
            // AJAX request for regenerating code
            $code = $this->getVerifyCode(true);
            Yii::$app->response->format = Response::FORMAT_JSON;
            return [
                'hash1' => $this->generateValidationHash($code),
                'hash2' => $this->generateValidationHash(strtolower($code)),
                // we add a random 'v' parameter so that FireFox can refresh the image
                // when src attribute of image tag is changed
                'url' => Url::to([$this->id, 'v' => uniqid()]),
            ];
        } else {
            $this->setHttpHeaders();
            Yii::$app->response->format = Response::FORMAT_RAW;
            return $this->renderImage($this->getVerifyCode(true));
        }
    }

    /**
     * @param bool $regenerate
     * @return int|mixed|string|null
     */
    public function getVerifyCode($regenerate = false)
    {
        if ($this->fixedVerifyCode !== null) {
            return $this->fixedVerifyCode;
        }
//        $name = $this->getSessionKey();
        $pageId = \Yii::$app->request->get('page_id');
        if( !$pageId ) {
            throw new \Exception('no page id');
        }

        $cacheRedis = Yii::$app->cache;
        $key = sprintf(self::CAPTCHA_CODE, $pageId);
        if( !$regenerate ) {
            $code = $cacheRedis->get($key);
            if ( $code ) return $code;
        }

        $code = $this->generateVerifyCode();
        $cacheRedis->set($key, $code, 300);

        return $code;
    }

    public function validate($input, $caseSensitive)
    {
        $code = $this->getVerifyCode();
        $valid = $caseSensitive ? ($input === $code) : strcasecmp($input, $code) === 0;
        if ( $valid ) {
            $this->getVerifyCode(true);
        }

        return $valid;
    }
}