<?php

/**
 * 模块管理函数库文件
 *
 * @author banyanCheung <banyan@ibos.com.cn>
 * @link http://www.ibos.com.cn/
 * @copyright Copyright &copy; 2012-2013 IBOS Inc
 */
/**
 * 模块管理函数库类，提供安装模块，检测模块，协助模块一系列操作功能的实现
 * @package application.core.utils
 * @version $Id: module.php -1   $
 * @author banyanCheung <banyan@ibos.com.cn>
 */

namespace application\core\utils;

use application\core\model\Module as ModuleModel;
use application\modules\role\utils\Auth;
use CException;
use CJSON;

class Module
{

    /**
     * 模块文件夹别名
     */
    const MODULE_ALIAS = 'modules';

    /**
     * 模块安装文件夹别名
     */
    const INSTALL_PATH_ALIAS = 'install';

    /**
     * 模块卸载文件夹别名
     */
    const UNINSTALL_PATH_ALIAS = 'uninstall';

    /**
     * 路径分隔常量
     */
    const DS = DIRECTORY_SEPARATOR;

    /**
     * 核心模块
     * @var array
     */
    private static $_coreModule = array(
        'main',
        'user',
        'department',
        'position',
        'message',
        'dashboard',
        'role',
        'mobile',
    );

    /**
     * 核心依赖模块，相比于核心模块的不可关闭与不可卸载来说，它能关闭但不能卸载
     * 因为这种类型的模块在代码与视图层面上与系统核心模块有耦合性
     * @var array
     */
    private static $_sysDependModule = array(
        'weibo'
    );

    /**
     * 检查某个模块是否可用
     * @param string $moduleName 模块名
     * @return boolean 是否可用
     */
    public static function getIsEnabled($moduleName)
    {
        static $modules = array();
        if (empty($modules)) {
            $modules = Ibos::app()->getEnabledModule();
        }
        return isset($modules[$moduleName]);
    }

    /**
     * 获得核心模块数组
     * @return array
     */
    public static function getCoreModule()
    {
        return self::$_coreModule;
    }

    /**
     * 获得核心依赖模块数组
     * @return array
     */
    public static function getDependModule()
    {
        return self::$_sysDependModule;
    }

    /**
     * 执行模块安装
     * @param string $moduleName 模块名
     * @throws CException 检查安装文件夹时的异常
     * @return boolean 安装成功与否
     */
    public static function install($moduleName)
    {
        defined('IN_MODULE_ACTION') or define('IN_MODULE_ACTION', true);
        if (!defined('INSTALL_PAGE')) {
            $checkError = self::check($moduleName);
            if (!empty($checkError)) {
                throw new CException($checkError);
            }
        }
        $installPath = self::getInstallPath($moduleName);
        // 安装模块模型(如果有)
        $modelSqlFile = $installPath . 'model.sql';
        if (file_exists($modelSqlFile)) {
            $modelSql = file_get_contents($modelSqlFile);
            self::executeSql($modelSql);
        }
        /**
         * 执行额外的sql语句
         */
        $sqlFiles = glob($installPath . '*.sql');
        if (!empty($sqlFiles)) {
            foreach ($sqlFiles as $sqlFile) {
                if (file_exists($sqlFile) && $sqlFile != $installPath . 'model.sql') {
                    $modelSql = file_get_contents($sqlFile);
                    self::executeSql($modelSql);
                }
            }
        }

        // 处理模块配置，写入数据
        $config = require $installPath . 'config.php';
        // 是否有模块图标文件,有的话写入数据库标识避免以后的引用判断
        $icon = self::getModulePath() . $moduleName . '/static/image/icon.png';
        if (is_file($icon)) {
            $config['param']['icon'] = 1;
        } else {
            $config['param']['icon'] = 0;
        }
        // 是否有模块所属分类
        if (!isset($config['param']['category'])) {
            $config['param']['category'] = '';
        }
        // 是否有首页显示
        if (isset($config['param']['indexShow']) && isset($config['param']['indexShow']['link'])) {
            $config['param']['url'] = $config['param']['indexShow']['link'];
        } else {
            $config['param']['url'] = '';
        }
        $configs = CJSON::encode($config);
        $record = array(
            'module' => $moduleName,
            'name' => $config['param']['name'],
            'url' => $config['param']['url'],
            'category' => $config['param']['category'],
            'version' => $config['param']['version'],
            'description' => $config['param']['description'],
            'icon' => $config['param']['icon'],
            'config' => $configs,
            'installdate' => TIMESTAMP
        );
        if (in_array($moduleName, self::getCoreModule())) {
            $record['iscore'] = 1;
        } elseif (in_array($moduleName, self::getDependModule())) {
            $record['iscore'] = 2;
        } else {
            $record['iscore'] = 0;
        }
        $insertStatus = ModuleModel::model()->add($record);
        if (!defined('INSTALL_PAGE')) {
            Cache::rm('module');
            if ($insertStatus && isset($config['authorization'])) {
                self::updateAuthorization($config['authorization'], $moduleName, $config['param']['category']);
            }
            $extentionScript = $installPath . 'extention.php';
            // 执行模块扩展脚本(如果有)
            if (file_exists($extentionScript)) {
                include_once $extentionScript;
            }
        }
        return $insertStatus;
    }

    /**
     * 执行模块卸载
     * @param string $moduleName 模块名
     * @return boolean
     */
    public static function uninstall($moduleName)
    {
        defined('IN_MODULE_ACTION') or define('IN_MODULE_ACTION', true);
        $record = ModuleModel::model()->fetchByPk($moduleName);
        if (!empty($record)) {
            ModuleModel::model()->deleteByPk($moduleName);
            Cache::rm('module');
        }
        $uninstallPath = self::getUninstallPath($moduleName);
        $extentionScript = $uninstallPath . 'extention.php';
        $modelSqlFile = $uninstallPath . 'model.sql';
        // 卸载模块模型(如果有)
        if (file_exists($modelSqlFile)) {
            $modelSql = file_get_contents($modelSqlFile);
            self::executeSql($modelSql);
        }
        // 执行模块扩展脚本(如果有)
        if (is_file($extentionScript)) {
            include_once $extentionScript;
        }
        return true;
    }

    /**
     * 获取所有模块目录
     * @return array
     */
    public static function getModuleDirs()
    {
        $modulePath = self::getModulePath();
        $dirs = (array) glob($modulePath . '*');
        $moduleDirs = array();
        foreach ($dirs as $dir) {
            if (is_dir($dir)) {
                $d = basename($dir);
                //排除名字带_bak的目录
                if (substr($d, -4) != '_bak') {
                    $moduleDirs[] = $d;
                }
            }
        }
        return $moduleDirs;
    }

    /**
     * 获取模块文件夹真实路径
     * @return string
     */
    public static function getModulePath()
    {
        static $path = null;
        if (!$path) {
            $path = Ibos::getPathOfAlias('application') . self::DS . self::MODULE_ALIAS . self::DS;
        }
        return $path;
    }

    /**
     * 获取模块安装文件夹路径
     * @param string $module 模块名
     * @return string
     */
    public static function getInstallPath($module)
    {
        return self::getModulePath() . $module . self::DS . self::INSTALL_PATH_ALIAS . self::DS;
    }

    /**
     * 获取模块卸载文件夹路径
     * @param string $module 模块名
     * @return string
     */
    public static function getUninstallPath($module)
    {
        return self::getModulePath() . $module . self::DS . self::UNINSTALL_PATH_ALIAS . self::DS;
    }

    /**
     * 过滤掉已安装模块,返回未安装模块名
     * @param array $installedModule 已安装模块
     * @param array $moduleDirs 所有模块的文件夹数组
     * @return array
     */
    public static function filterInstalledModule(array $installedModule, array $moduleDirs)
    {
        $dirs = array();
        foreach ($moduleDirs as $index => $moduleName) {
            if (array_key_exists($moduleName, $installedModule)) {
                continue;
            } else {
                $dirs[] = $moduleName;
            }
        }
        return $dirs;
    }

    public static function getNotInstallModule()
    {
        $moduleDirs = self::getModuleDirs();
        if (!empty($moduleDirs)) {
            $moduleDirs = self::filterInstalledModule(ModuleModel::model()->fetchAllSortByPk('module'), $moduleDirs);
        }
        return $moduleDirs;
    }

    /**
     * 初始化模块配置文件 - 参数部分
     * @param string $moduleName 模块名称
     * @return array
     */
    public static function initModuleParameter($moduleName)
    {
        defined('IN_MODULE_ACTION') or define('IN_MODULE_ACTION', true);
        $param = array();
        $installPath = self::getInstallPath($moduleName);
        if (is_dir($installPath)) {
            $file = $installPath . 'config.php';
            if (is_file($file) && is_readable($file)) {
                $config = include_once $file;
            }
            if (isset($config) && is_array($config)) {
                $param = (array) $config['param'];
                // 处理模块ICON
                $icon = self::getModulePath() . $moduleName . '/static/image/icon.png';
                if (is_file($icon)) {
                    $param['icon'] = 1;
                } else {
                    $param['icon'] = 0;
                }
                // ------------------------------
            }
        }
        return $param;
    }

    /**
     * 初始化多个模块配置文件 - 参数部分,用于列表
     * @param array $moduleDirs
     * @return array
     */
    public static function initModuleParameters(array $moduleDirs)
    {
        $modules = array();
        foreach ($moduleDirs as $index => $moduleName) {
            $param = self::initModuleParameter($moduleName);
            if (!empty($param)) {
                $modules[$moduleName] = $param;
            }
        }
        return $modules;
    }

    /**
     * 更新模块配置文件
     * @param mixed $module 要更新的模块名，为空更新全部，可单个模块字符串也可数组格式
     * @param boolean $updateAuth 是否更新授权信息
     * @return boolean
     */
    public static function updateConfig($module = '', $updateAuth = true)
    {
        static $execute = false;
        if (!$execute) {
            defined('IN_MODULE_ACTION') or define('IN_MODULE_ACTION', true);
            $updateList = empty($module) ? array() : (is_array($module) ? $module : explode(',', $module));
            $modules = array();
            $installedModule = ModuleModel::model()->fetchAllEnabledModule();
            if (!$updateList) {
                foreach ($installedModule as $module) {
                    $modules[] = $module['module'];
                }
            } else {
                $modules = $updateList;
            }
            foreach ($modules as $name) {
                $installPath = self::getInstallPath($name);
                $file = $installPath . 'config.php';
                if (is_file($file) && is_readable($file)) {
                    $config = include_once $file;
                    if (isset($config) && is_array($config) && array_key_exists($name, $installedModule)) {
                        $icon = self::getModulePath() . $name . '/static/image/icon.png';
                        if (is_file($icon)) {
                            $config['param']['icon'] = 1;
                        } else {
                            $config['param']['icon'] = 0;
                        }
                        if (!isset($config['param']['category'])) {
                            $config['param']['category'] = '';
                        }
                        $data = array(
                            'updatedate' => time(),
                            'config' => json_encode($config),
                            'icon' => $config['param']['icon'],
                            'name' => $config['param']['name'],
                            'category' => $config['param']['category'],
                            'version' => $config['param']['version'],
                            'description' => $config['param']['description'],
                        );
                        ModuleModel::model()->modify($name, $data);
                        if (isset($config['authorization']) && $updateAuth) {
                            self::updateAuthorization($config['authorization'], $name, $config['param']['category']);
                        }
                    }
                }
            }
            Cache::rm('module');
            $execute = true;
        }
        return $execute;
    }

    /**
     * 更新授权认证项目
     * @param array $authItem 配置文件中的授权节点数组
     * @param string $moduleName 对应的模块名字
     * @return void
     */
    public static function updateAuthorization($authItem, $moduleName, $category)
    {
        return Auth::updateAuthorization($authItem, $moduleName, $category);
    }

    /**
     * 检查安装所需条件
     * @param string $moduleName 模块名
     * @return boolean 检查通过与否
     */
    private static function check($moduleName)
    {
        $error = '';
        // 检查是否已安装
        $record = ModuleModel::model()->findByPk($moduleName);
        if (!empty($record)) {
            $error = Ibos::lang('This module has been installed', 'error');
            return $error;
        }
        // 检查模块安装目录
        $installPath = self::getInstallPath($moduleName);
        if (!is_dir($installPath)) {
            $error = Ibos::lang('Install dir does not exists', 'error');
            return $error;
        }
        // 模块配置文件
        if (!file_exists($installPath . 'config.php')) {
            $error = Ibos::lang('Module config missing', 'error');
            return $error;
        }
        // 配置文件格式，目前只是粗略匹配
        $configFile = $installPath . 'config.php';
        $config = (array) include_once $configFile;
        $configFormatCorrect = isset($config['param']) && isset($config['config']);
        if (!$configFormatCorrect) {
            $error = Ibos::lang('Module config format error', 'error');
            return $error;
        }
        return $error;
    }

    /**
     * 执行mysql.sql文件，创建数据表等
     * @param string $sql sql语句
     */
    public static function executeSql($sql)
    {
        $sqls = StringUtil::splitSql($sql);
        $command = Ibos::app()->db->createCommand();
        if (is_array($sqls)) {
            foreach ($sqls as $sql) {
                if (trim($sql) != '') {
                    $command->setText($sql)->execute();
                }
            }
        } else {
            $command->setText($sqls)->execute();
        }
        return true;
    }

    public static function getModuleName($module)
    {
        static $allModulesParam = null;
        if (null === $allModulesParam) {
            $allModules = self::getModuleDirs();
            $allModulesParam = self::initModuleParameters($allModules);
        }
        $moduleName = $allModulesParam[$module]['name'];
        return $moduleName;
    }

    /**
     * 判断是否安装某个模块
     * @param string $module 模块名
     * @return boolean
     */
    public static function getIsInstall($module)
    {
        $rs = Ibos::app()->db->createCommand()
            ->select('module')
            ->from('{{module}}')
            ->where("`module` = '{$module}'")
            ->queryScalar();
        return !!$rs;
    }

}
