<?php

namespace xmvc;

/**
 * 控制器的返回值类
 *
 */
class ReturnClass
{
    protected $innerCode = R_OK;
    protected $innerData = null;
    protected $innerMsg = null;
    protected $innerExt = null;
    protected $isChecked = false; // 是否进行过合法性检查，不管是否合法
    protected $cacheTime = 0; // 浏览器缓存多少秒
    public $header = null;  // 用于模板的<head></head>中的变量
    public $footer = null;  // 用于模板的footer.tpl.php中的变量


    public function __get($var)
    {
        if ($var == 'isOK') {
            return $this->isOK();
        } elseif ($var == 'isNO') {
            return $this->isNO();
        } elseif (substr($var, 0, 5) != 'inner') {
            if (!$this->isChecked) {
                $this->check();
            }
            $innerKey = 'inner' . ucfirst($var);
            if (isset($innerKey)) {
                return $this->{$innerKey};
            } else {
                throw new \Exception("未知的属性名: " . ucfirst($var), 1);
            }
        }
    }

    public function isOK(): bool
    {
        if (!$this->isChecked) {
            $this->check();
        }
        return $this->innerCode & R_OK;
    }

    public function isNO(): bool
    {
        if (!$this->isChecked) {
            $this->check();
        }
        return $this->innerCode & R_NO;
    }

    public function info(string $msg): self
    {
        $this->innerCode = R_OK | R_INFO;
        $this->innerMsg = $msg;
        return $this;
    }

    public function warning(string $msg): self
    {
        $this->innerCode = $this->innerCode | R_WARNING;
        $this->innerMsg = $msg;
        return $this;
    }

    public function error(string $msg): self
    {
        $this->innerCode = R_NO | R_ERROR;
        $this->innerMsg = $msg;
        return $this;
    }

    public function code(int $code): self
    {
        $this->innerCode = $code;
        return $this;
    }

    /**
     * 返回的数据, 如果需要调用模板,$data数组中不要出现 server,header,footer的键,它们被占用
     *
     * @param mixed $data
     * @return self
     */
    public function data($data): self
    {
        $this->innerData = $data;
        return $this;
    }

    /** 返回消息的文本 */
    public function msg(string $msg): self
    {
        $this->innerMsg = $msg;
        return $this;
    }

    /** 返回自定义扩展信息 */
    public function ext(array $ext): self
    {
        $this->innerExt = $ext;
        return $this;
    }

    /**
     * 返回值检测
     * code 值为常量 R_OK, R_NO, R_INFO, R_WARNING, R_ERROR 或者是它们的集合 ,
     * code 值必须包括有 R_OK 或者 R_NO 这两个的一个,
     * code 值如果有 R_INFO, R_WARNING, R_ERROR, 则必须显性的调用 msg() 方法,
     * 如果同时包括 R_OK 和 R_NO 将只保留 R_NO,
     * code 值如果同时包括 R_OK 和 R_ERROR, 则 R_OK 移除, 并加上 R_NO
     * 如果有调用 msg(), 并且没有指定 R_INFO, R_WARNING, R_ERROR 中的一个,则 R_OK 默认 R_OK|R_INFO, R_NO 默认 R_NO|R_ERROR
     * 如果 code 有 R_NO 与 R_WARNING, R_ERROR 中的一个,将会创建错误跟踪信息
     * ext 扩展值，用于自定义特殊业务逻辑
     * @param [type] $ext
     * @return self
     */
    public function check(): self
    {
        $this->isChecked = true;
        if (!($this->innerCode & (R_OK | R_NO))) {
            throw new \Exception('code 值必须包括有 R_OK 或者 R_NO 这两个的一个');
        }
        if (($this->innerCode & (R_INFO | R_WARNING | R_ERROR))  && is_null($this->innerMsg)) {
            throw new \Exception('code 值有 R_INFO, R_WARNING, R_ERROR, 则必须显性的调用 msg() 方法');
        }
        if (($this->innerCode & (R_OK | R_NO)) === (R_OK | R_NO)) {
            $this->innerCode &= ~R_OK;
        }
        if (($this->innerCode & (R_OK | R_ERROR)) === (R_OK | R_ERROR)) {
            $this->innerCode &= ~R_OK;
            $this->innerCode |= R_NO;
        }
        if (!($this->innerCode & (R_INFO | R_WARNING | R_ERROR)) && !is_null($this->innerMsg)) {
            $this->innerCode  |= ($this->innerCode  & R_OK) ? R_INFO : R_ERROR;
        }
        return $this;
    }

    /**
     * 用于模板文件的<head></head>之间的变量处理
     *
     * @return self
     */
    public function header(array $params): self
    {
        if (is_null($this->header)) {
            $this->header = new \stdClass();
        }
        foreach ($params as $key => $value) {
            $this->header->$key = $value;
        }
        return $this;
    }

    /**
     * 用于模板的footer.tpl.php中的变量
     *
     * @return self
     */
    public function footer(array $params): self
    {
        if (is_null($this->footer)) {
            $this->footer = new \stdClass();
        }
        foreach ($params as $key => $value) {
            $this->footer->$key = $value;
        }
        return $this;
    }

    public function array(): array
    {
        if (!$this->isChecked) {
            $this->check();
        }
        return [
            'code' => $this->innerCode,
            'data' => $this->innerData,
            'msg'  => $this->innerMsg,
            'ext'  => $this->innerExt,
        ];
    }

    /**  浏览器缓存多少秒, 默认 5 分钟 */
    public function cache(int $sec = 300): self
    {
        $this->cacheTime = $sec;
        return $this;
    }

    public function output(int $httpCode = 200)
    {
        ob_clean();
        http_response_code($httpCode);
        if (!IS_CLI && !headers_sent()) {
            header('Content-Type: application/json; charset=utf-8');
            if ($this->cacheTime) {
                $expires = gmdate('D, d M Y H:i:s', time() + $this->cacheTime) . ' GMT'; // 缓存时间
                header("Cache-Control: max-age={$this->cacheTime}, public");
                header('Pragma: public');
                header('ETag: "ver-' . SVER . '"');
                header('Expires: ' . $expires);
            }
        }
        echo json_encode($this->array(), JSON_UNESCAPED_UNICODE);
        ob_end_flush();
    }
}
