<?php

namespace think;

use think\Loader;

/**
 * 插件基类
 * Class Addons
 * @package think\addons
 */
abstract class Addons
{
    /**
     * 视图实例对象
     * @var view
     * @access protected
     */
    protected $view = null;
    // 当前插件标识
    protected $name;
    // 当前错误信息
    protected $error;
    public $addons_path = '';
    public $config_file = '';
    // 插件配置
    protected $addon_config;
    //插件信息
    protected $addon_info;

    /**
     * 架构函数
     * @access public
     */
    public function __construct()
    {
        // 获取当前插件目录
        $this->addons_path = \think\facade\Env::get('addons_path') . $this->getName() . DIRECTORY_SEPARATOR;

        // 读取当前插件配置信息
        if (is_file($this->addons_path . 'config.php')) {
            $this->config_file = $this->addons_path . 'config.php';
        }
        $this->addon_config = "addon_{$this->name}_config";
        $this->name = $this->getName();
        $this->addon_info = "addon_{$this->name}_info";

        // 控制器初始化
        if (method_exists($this, '_initialize')) {
            $this->_initialize();
        }
    }

    /**
     * [setAddonViewPath 设置插件目录]
     */
    protected function setAddonViewPath()
    {
        $config = ['view_path' => $this->addons_path];
        $this->request = Container::get('request');
        $this->app = Container::get('app');
        // 初始化视图模型
        $this->view = Container::get('view')->init(
            array_merge($this->app['config']->get('template.'), $config)
        );
    }

    /**
     * 获取插件信息
     * @return array
     */
    final public function getInfo()
    {
        $info = config($this->addon_info);
        if ($info) {
            return $info;
        }

        // 文件属性
        $info = [];
        // 文件配置
        $info_file = $this->addons_path . 'info.ini';
        if (is_file($info_file)) {
            $_info = parse_ini_file($info_file, true, INI_SCANNER_TYPED) ?: [];
            $info = array_merge($_info, $info);
        }
        $info['name'] = $this->name;
        config($this->addon_info, $info);

        return isset($info) ? $info : [];
    }

    /**
     * 获取插件的默认配置信息
     * @return array|mixed|null
     */
    final public function getConfig()
    {
        $config = config($this->addon_config);
        if ($config) {
            return $config;
        }
        $temp_arr = $this->getFullConfig();

        foreach ($temp_arr as $key => $value) {
            $config[$key] = $value['value'];
            if ($value['type'] == 'checkbox' || $value['type'] == 'images') {
                $config[$key] = explode(',', $value['value']);
            } else {
                $config[$key] = $value['value'];
            }
        }
        unset($temp_arr);
        config($this->addon_config, $config);

        return $config;
    }

    /**
     * 设置配置数据
     * @param $name 配置项
     * @param array $value
     * @return array
     */
    final public function setConfig($name, $value = [])
    {
        $config = $this->getConfig($this->name);
        $config [$name] = $value;
        config($this->addon_config, $config);
        return $config;
    }

    /**
     * 保存基础配置信息
     * @param $name 配置项
     * @param array $value
     * @return boolean
     * @throws Exception
     */
    final function saveInfo($name, $value)
    {
        $file = $this->addons_path . 'info.ini';
        $array = $this->getInfo();
        $array[$name] = $value;

        $res = array();
        foreach ($array as $key => $val) {
            if (is_array($val)) {
                $res[] = "[$key]";
                foreach ($val as $skey => $sval)
                    $res[] = "$skey = " . (is_numeric($sval) ? $sval : $sval);
            } else
                $res[] = "$key = " . (is_numeric($val) ? $val : $val);
        }
        if ($handle = fopen($file, 'w')) {
            fwrite($handle, implode("\n", $res) . "\n");
            fclose($handle);
            //清空当前配置缓存
            config($this->addon_info, null);
        } else {
            throw new Exception("文件没有写入权限");
        }
        return true;
    }

    /**
     * 设置插件信息数据
     * @param $name 配置项
     * @param array $value
     * @return array
     */
    final public function setInfo($name, $value)
    {
        $info = $this->getInfo($this->name);
        $info[$name] = $value;
        config($this->addon_info, $info);
        return $info;
    }

    /**
     * 获取完整配置列表
     * @return array
     */
    final public function getFullConfig()
    {
        $fullConfigArr = [];
        $config_file = $this->addons_path . 'config.php';
        if (is_file($config_file)) {
            $fullConfigArr = include $config_file;
        }
        return $fullConfigArr;
    }


    /**
     * 写入配置文件
     * @param string $name 配置项
     * @param array $config 配置数据
     */
    final function saveConfig($name, $config = '')
    {
        $fullconfig = $this->getFullConfig();
        if (is_array($name)) {
            foreach ($name as $key => $item) {
                $fullconfig[$key]['value'] = $item;
            }
        } else {
            if (isset($fullconfig[$name])) {
                $fullconfig[$name]['value'] = $config;
            }
        }
        // 写入配置文件
        $this->saveFullConfig($fullconfig);
        //清除配置缓存
        config($this->addon_config, null);
        return true;
    }

    /**
     * 写入配置文件
     * @param array $array
     * @return boolean
     * @throws Exception
     */
    final function saveFullConfig($array)
    {
        $file = $this->addons_path . 'config.php';
        if ($handle = fopen($file, 'w')) {
            fwrite($handle, "<?php\n\n" . "return " . var_export($array, TRUE) . ";\n");
            fclose($handle);
        } else {
            throw new Exception("文件没有写入权限");
        }
        return true;
    }

    /**
     * 获取当前模块名
     * @return string
     */
    final public function getName()
    {
        $data = explode('\\', get_class($this));
        return Loader::parseName(array_pop($data));
    }

    /**
     * 检查配置信息是否完整
     * @return bool
     */
    final public function checkInfo()
    {
        $info_check_keys = ['name', 'title', 'description', 'status', 'author', 'version'];
        foreach ($info_check_keys as $value) {
            if (!array_key_exists($value, $this->info)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 加载模板和页面输出 可以返回输出内容
     * @access public
     * @param string $template 模板文件名或者内容
     * @param array $vars 模板输出变量
     * @param array $replace 替换内容
     * @param array $config 模板参数
     * @return mixed
     * @throws \Exception
     */
    public function fetch($template = '', $vars = [], $replace = [], $config = [])
    {
        $this->setAddonViewPath();
        if (!is_file($template)) {
            $template = '/' . $template;
        }

        // 关闭模板布局
        $this->view->engine->layout(false);

        echo $this->view->fetch($template, $vars, $replace, $config);
    }

    /**
     * 渲染内容输出
     * @access public
     * @param string $content 内容
     * @param array $vars 模板输出变量
     * @param array $replace 替换内容
     * @param array $config 模板参数
     * @return mixed
     */
    public function display($content, $vars = [], $replace = [], $config = [])
    {
        $this->setAddonViewPath();
        // 关闭模板布局
        $this->view->engine->layout(false);

        echo $this->view->display($content, $vars, $replace, $config);
    }

    /**
     * 渲染内容输出
     * @access public
     * @param string $content 内容
     * @param array $vars 模板输出变量
     * @return mixed
     */
    public function show($content, $vars = [])
    {
        $this->setAddonViewPath();
        // 关闭模板布局
        $this->view->engine->layout(false);

        echo $this->view->fetch($content, $vars, [], [], true);
    }

    /**
     * 模板变量赋值
     * @access protected
     * @param mixed $name 要显示的模板变量
     * @param mixed $value 变量的值
     * @return void
     */
    public function assign($name, $value = '')
    {
        $this->setAddonViewPath();
        $this->view->assign($name, $value);
    }

    /**
     * 获取当前错误信息
     * @return mixed
     */
    public function getError()
    {
        return $this->error;
    }

    //必须实现安装
    abstract public function install();

    //必须卸载插件方法
    abstract public function uninstall();
}

