<?php 

namespace App\Services;

use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use App\Services\WaterMark;
use Image;
use App\Models\Setting;

// 上传管理
class UploadManager
{
    private $maxFileAge = 600;

	private $config;

    // 当前错误
    private $error = '';

    private $thumb = '';

    protected $path = 'data/upload';

    // 允许上传的图片格式
    protected $imgExtAccepted = ['jpg', 'jpe', 'jpeg', 'png', 'gif'];

    // 允许上传的文件格式
    protected $extAccepted = ['jpg', 'jpe', 'jpeg', 'png', 'gif', 'zip', 'rar', 'doc', 'docx', 'ppt', 'psd', 'txt', 'mp4'];

    // 上传状态信息
    protected $stateInfo;

    // 文件相关信息
    protected $data;

    private $stateMap = [
        "SUCCESS", //上传成功标记，在UEditor中内不可改变，否则flash判断会出错
        "文件大小超出 upload_max_filesize 限制",
        "文件大小超出 MAX_FILE_SIZE 限制",
        "文件未被完整上传",
        "没有文件被上传",
        "上传文件为空",
        "ERROR_TMP_FILE" => "临时文件错误",
        "ERROR_TMP_FILE_NOT_FOUND" => "找不到临时文件",
        "ERROR_SIZE_EXCEED" => "文件大小超出网站限制",
        "ERROR_TYPE_NOT_ALLOWED" => "文件类型不允许",
        "ERROR_CREATE_DIR" => "目录创建失败",
        "ERROR_DIR_NOT_WRITEABLE" => "目录没有写权限",
        "ERROR_FILE_MOVE" => "文件保存时出错",
        "ERROR_FILE_NOT_FOUND" => "找不到上传文件",
        "ERROR_WRITE_CONTENT" => "写入文件内容错误",
        "ERROR_UNKNOWN" => "未知错误",
        "ERROR_DEAD_LINK" => "链接不可用",
        "ERROR_HTTP_LINK" => "链接不是http链接",
        "ERROR_HTTP_CONTENTTYPE" => "链接contentType不正确",
        "INVALID_URL" => "非法 URL",
        "INVALID_IP" => "非法 IP"
    ];

    public function __construct($config = array())
    {
    	if (!empty($config)) {
    		$this->config = $config;
    	}
    }

    public function upload(Request $request)
    {
       $this->putFile($request);

       return $this->getUploadFileInfo();
    }

    // 水印
    public function watermark($path, $target = '')
    {
        $img = with(new WaterMark())->make($path);

        if ($target) {
            // 生成水印图片
            return $img->save($target, 100);
        }

        // 输出到浏览器
        return $img->response($img->extension, 100);
    }

    /**
     * 上传文件到服务器
     *
     * @param $upToken    上传凭证
     * @param $key        上传文件名
     * @param $data       上传二进制流
     * @param $mime       上传数据的mimeType
     * @param $checkCrc   是否校验crc32
     *
     * @return array    包含已上传文件的信息
     */
    public function putFile(Request $request, $name = 'file')
    {
        // v($request);
		if ($request->hasFile($name)) {
		    $file = $request->file($name);
			if ($file->isValid()) {
                $this->extension = $file->getClientOriginalExtension();
                $this->filesize  = $file->getSize();
                if(empty($this->extension)){
                    $chunk        = $request->chunk;
                    $chunks       = $request->chunks;
                    $originalName = $request->name;
                    $originalName = preg_replace('/[^\w\._]+/', '', $originalName);
                    $this->extension = substr($originalName,strrpos($originalName, '.')+1);
                    $filePath = $this->getStoragePath().'/'.$originalName;

                    // $this->filename  = $this->makeFilename().'.'.$this->extension;
                    $this->name         = $this->makeFilename();
                    $this->filename     = $this->name.'.'.$this->extension;
                    $this->directory    = $this->path.'/'.$this->getDirectory();
                    $this->pathname     = $this->getFullPath($this->directory);
                    $this->relativePath = $this->getRelativePath();
                    $this->mime         = $file->getMimeType();

                    $this->removeOldData($filePath);
                    $this->appendData($filePath, $file);

                    if ($chunk == $chunks - 1) {
                        $this->allowType = ($request->input('type') == 1) ? $this->imgExtAccepted : $this->extAccepted;

                        // 检查是否不允许的文件格式
                        if (!$this->checkType()) {
                            $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
                            return;
                        }
                        $file = new UploadedFile($filePath, $originalName, 'blob', sizeof($filePath), UPLOAD_ERR_OK, true);

                        $this->handler($file);

                        @unlink($filePath);
                    }
                }else{
                    $this->name      = $this->makeFilename();
                    $this->filename  = $this->name.'.'.$this->extension;
                    $this->directory = $this->path.'/'.$this->getDirectory();
                    $this->pathname  = $this->getFullPath($this->directory);
                    $this->mime      = $file->getMimeType();
                    $this->relativePath = $this->getRelativePath();

                    $this->allowType = ($request->input('type') == 1) ? $this->imgExtAccepted : $this->extAccepted;

                    // 检查是否不允许的文件格式
                    if (!$this->checkType()) {
                        $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
                        return;
                    }

                    //创建目录失败
                    if (!file_exists($this->pathname) && !mkdir($this->pathname, 0777, true)) {
                        $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
                        return;
                    } else if (!is_writeable($this->pathname)) {
                        $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
                        return;
                    }

                    // 文件上传
                    if ($file->isValid()) {
                        $file->move($this->pathname, $this->filename);   
                        $this->stateInfo = $this->stateMap[0];

                        // 创建缩略图
                        $this->thumb = '';
                        if ($request->input('thumb') == 1) {
                            $setting    = Setting::where('lang', request()->cookie('lang', 'cn'))->lists('value', 'name');
                            $size       = $setting['thumb_item'];
                            $thumbSize  = trim(str_replace('，', ',', $size), ',');
                            $thumbArray = explode(',', $thumbSize);
                            foreach ($thumbArray as $key => $size) {
                                list($width, $height) = explode('x', $size);
                                // $this->thumbs = $this->directory ."{$size}_".$this->filename;
                                $this->thumbs = $this->directory ."thumb_".$this->filename;
                                Image::make($this->relativePath)->fit($width, $height)->save($this->thumbs, 100);
                            }
                            // $this->thumb = $this->relativePath."_{$thumbSize}.jpg";
                            // Image::make($this->relativePath)->fit($width, $height, null, 'top')->save($this->thumbs, 100);
                            $this->thumb = $this->directory ."{$thumbArray[0]}_".$this->filename;
                        }  

                    } else {
                        $this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE");
                    }
                }

                return $this;
			}
		}
    }

    protected function handler(UploadedFile $file)
    {
        // if (!file_exists($this->pathname))
        // @mkdir($targetDir);
        $result = $file->move($this->pathname, $this->filename);
        $result && $this->setFinish(1);

        return $result;
    }

    public function getStoragePath()
    {
        $path = storage_path('upload');

        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }

        return $path;
    }

    public function removeOldData($filePath)
    {
        if (file_exists($filePath) && filemtime($filePath) < time() - $this->maxFileAge) {
            @unlink($filePath);
        }
    } 

    private function appendData($filePathPartial, UploadedFile $file)
    {
        if (!$out = @fopen($filePathPartial, 'ab')) {
            die("Failed to open input stream.");
        }
        if (!$in = @fopen($_FILES['file']['tmp_name'], 'rb')) {
            die("Failed to open input stream.");
        }
        while ($buff = fread($in, 4096)) {
            fwrite($out, $buff);
        }
        @fclose($out);
        @fclose($in);
    }

    // 百度编辑器处理文件上传
    public function ueditor(Request $request)
    {
        $action = $request->input('action');

        if ($action == 'config') {
            return $this->getUeditorConfig();
        }

        $this->ueditorUpload($request, $action);

        return $this->getUeditorFileInfo();
    }

    // 编辑器配置项
    private function getUeditorConfig()
    {
        return json_decode(preg_replace("/\/\*[\s\S]+?\*\//", "", file_get_contents($_SERVER['DOCUMENT_ROOT']."/data/assets/js/ueditor/config.json")), true);
    }

    // 编辑器文件上传
    private function ueditorUpload($request, $action)
    {
        $config = $this->getUeditorConfig();
        $this->allowType = ($action == 'image') ? $this->imgExtAccepted : $this->extAccepted;

        $fieldName = 'file';

        if ($request->hasFile($fieldName)) {
            $file = $request->file($fieldName);

            $this->extension    = $file->getClientOriginalExtension();
            $this->originalname = $file->getClientOriginalName();
            $this->filesize     = $file ->getClientSize();

            $this->directory = $this->path.'/'.$this->getDirectory();
            $this->pathname  = $this->getFullPath($this->directory);
            $this->filename  = $this->makeFilename().'.'.$this->extension;
            $this->fullname  = $this->getFullName();

            // 检查是否不允许的文件格式
            if (!$this->checkType()) {
                $this->stateInfo = $this->getStateInfo("ERROR_TYPE_NOT_ALLOWED");
                return;
            }

            //创建目录失败
            if (!file_exists($this->pathname) && !mkdir($this->pathname, 0777, true)) {
                $this->stateInfo = $this->getStateInfo("ERROR_CREATE_DIR");
                return;
            } else if (!is_writeable($this->pathname)) {
                $this->stateInfo = $this->getStateInfo("ERROR_DIR_NOT_WRITEABLE");
                return;
            }

            // 文件上传
            if ($file->isValid()) {
                $file->move($this->pathname, $this->filename);   
                $this->stateInfo = $this->stateMap[0];
            } else {
                $this->stateInfo = $this->getStateInfo("ERROR_FILE_MOVE");
            }
        }
    }

    // 生成新的文件名称
	protected function makeFilename()
	{
        return substr(md5(time().rand(1, 10000000000)), 0, 10).strtolower(str_random(6));
	}

    // 从给定的部分路径得到的完整的磁盘路径
    protected function getFullPath($path)
    {
        return base_path() . '/' .$path;
    }

    /**
     * 获取文件完整路径
     * @return string
     */
    private function getFullName()
    {
        return url($this->directory . $this->filename);
    }

    private function getRelativePath()
    {
        return $this->directory . $this->filename;
    }

    // 上传目录
    protected function getDirectory()
    {
    	return date('Y').'/'.date('m').'/';
    }

    /**
     * 文件类型检测
     * @return bool
     */
    private function checkType()
    {
        return in_array($this->extension, $this->allowType);
    }

     /**
     * 获取当前上传成功文件的各项信息 - 标准文件上传
     * @return array
     */
    public function getUploadFileInfo()
    {
        return [
            'state'       => $this->stateInfo,
            'path'        => $this->relativePath,
            'thumb'       => $this->thumb,
            'extension'   => $this->extension,
            'filesize'    => $this->filesize,
            'mime'        => $this->mime,
            'filename'    => $this->name
        ];
    }  

    /**
     * 获取当前上传成功文件的各项信息 - 百度编辑器
     * @return array
     */
    public function getUeditorFileInfo()
    {
        return [
            "state"     => $this->stateInfo,
            "url"       => $this->fullname,
            "title"     => $this->originalname,
            "original"  => $this->originalname,
            "type"      => $this->extension,
            "size"      => $this->filesize
        ];
    }

    /**
     * 上传错误检查
     * @param $errCode
     * @return string
     */
    private function getStateInfo($errCode)
    {
        return !$this->stateMap[$errCode] ? $this->stateMap["ERROR_UNKNOWN"] : $this->stateMap[$errCode];
    }

    // 上传错误处理
    private function getErrorMessage($file)
    {
        static $errors = [
            UPLOAD_ERR_INI_SIZE   => '您上传文件过大',
            UPLOAD_ERR_FORM_SIZE  => '您上传文件过大',
            UPLOAD_ERR_PARTIAL    => '文件只有部分被上传',
            UPLOAD_ERR_NO_FILE    => '没有文件被上传',
            UPLOAD_ERR_CANT_WRITE => '文件无法写入磁盘',
            UPLOAD_ERR_NO_TMP_DIR => '临时目录丢失',
            UPLOAD_ERR_EXTENSION  => 'PHP扩展限制了上传',
        ];

        $errorCode = $file->getError();

        $maxFilesize = $errorCode === UPLOAD_ERR_INI_SIZE ? $file->getMaxFilesize() / 1024 : 0;
        $message = isset($errors[$errorCode]) ? $errors[$errorCode] : '文件 "%s" 上传失败.';

        return sprintf($message, $file->getClientOriginalName(), $maxFilesize);
    }

    public function setFinish($value)
    {
        $this->finish = $value;
    }

}