<?php

if (!defined('XOOPS_ROOT_PATH')) exit();

require_once XOOPS_TRUST_PATH . '/libs/xoonips/class/core/BeanFactory.class.php';
require_once XOOPS_TRUST_PATH . '/libs/xoonips/class/Enum.class.php';
require_once USER_TRUST_PATH . '/admin/class/installer/UserInstallUtils.class.php';
require_once USER_TRUST_PATH . '/class/UserUtils.class.php';

/**
 * User_Updater
**/
class User_Updater
{
    /**
     * @brief   Legacy_ModuleInstallLog
    **/
    public $mLog = null;

    /**
     * @brief   string[]
    **/
    private $_mMileStone = array();

    /**
     * @brief   XoopsModule
    **/
    private $_mCurrentXoopsModule = null;

    /**
     * @brief   XoopsModule
    **/
    private $_mTargetXoopsModule = null;

    /**
     * @brief   int
    **/
    private $_mCurrentVersion = 0;

    /**
     * @brief   int
    **/
    private $_mTargetVersion = 0;

    /**
     * @brief   bool
    **/
    private $_mForceMode = false;

    /**
     *
     * @brief  object
     */
    public $db = null;


    /**
     * __construct
     * 
     * @param   void
     * 
     * @return  void
    **/
    public function __construct()
    {
        $this->mLog = new Legacy_ModuleInstallLog();
        $this->db   = &Database::getInstance();
    }

    /**
     * setForceMode
     * 
     * @param   bool  $isForceMode
     * 
     * @return  void
    **/
    public function setForceMode(/*** bool ***/ $isForceMode)
    {
        $this->_mForceMode = $isForceMode;
    }

    /**
     * setCurrentXoopsModule
     * 
     * @param   XoopsModule  &$module
     * 
     * @return  void
    **/
    public function setCurrentXoopsModule(/*** XoopsModule ***/ &$module)
    {
        $moduleHandler =& UserUtils::getXoopsHandler('module');
        $cloneModule =& $moduleHandler->create();
    
        $cloneModule->unsetNew();
        $cloneModule->set('mid',$module->get('mid'));
        $cloneModule->set('name',$module->get('name'));
        $cloneModule->set('version',$module->get('version'));
        $cloneModule->set('last_update',$module->get('last_update'));
        $cloneModule->set('weight',$module->get('weight'));
        $cloneModule->set('isactive',$module->get('isactive'));
        $cloneModule->set('dirname',$module->get('dirname'));
        $cloneModule->set('trust_dirname',$module->get('trust_dirname'));
        $cloneModule->set('hasmain',$module->get('hasmain'));
        $cloneModule->set('hasadmin',$module->get('hasadmin'));
        $cloneModule->set('hasconfig',$module->get('hasconfig'));
    
        $this->_mCurrentXoopsModule =& $cloneModule;
        $this->_mCurrentVersion = $cloneModule->get('version');
    }

    /**
     * setTargetXoopsModule
     * 
     * @param   XoopsModule  &$module
     * 
     * @return  void
    **/
    public function setTargetXoopsModule(/*** XoopsModule ***/ &$module)
    {
        $this->_mTargetXoopsModule =& $module;
        $this->_mTargetVersion = $this->getTargetPhase();
    }

    /**
     * getCurrentVersion
     * 
     * @param   void
     * 
     * @return  int
    **/
    public function getCurrentVersion()
    {
        return intval($this->_mCurrentVersion);
    }

    /**
     * getTargetPhase
     * 
     * @param   void
     * 
     * @return  int
    **/
    public function getTargetPhase()
    {
        ksort($this->_mMileStone);
    
        foreach($this->_mMileStone as $tVer => $tMethod)
        {
            if($tVer >= $this->getCurrentVersion())
            {
                return intval($tVer);
            }
        }
    
        return $this->_mTargetXoopsModule->get('version');
    }

    /**
     * hasUpgradeMethod
     * 
     * @param   void
     * 
     * @return  bool
    **/
    public function hasUpgradeMethod()
    {
        ksort($this->_mMileStone);
    
        foreach($this->_mMileStone as $tVer => $tMethod)
        {
            if($tVer >= $this->getCurrentVersion() && is_callable(array($this,$tMethod)))
            {
                return true;
            }
        }
    
        return false;
    }

    /**
     * isLatestUpgrade
     * 
     * @param   void
     * 
     * @return  bool
    **/
    public function isLatestUpgrade()
    {
        return ($this->_mTargetXoopsModule->get('version') == $this->getTargetPhase());
    }

    /**
     * _updateModuleTemplates
     * 
     * @param   void
     * 
     * @return  void
    **/
    private function _updateModuleTemplates()
    {
        User_InstallUtils::uninstallAllOfModuleTemplates($this->_mTargetXoopsModule,$this->mLog);
        User_InstallUtils::installAllOfModuleTemplates($this->_mTargetXoopsModule,$this->mLog);
        User_InstallUtils::installCommonTemplates($this->_mTargetXoopsModule,$this->mLog);
    }

    /**
     * _updateBlocks
     * 
     * @param   void
     * 
     * @return  void
    **/
    private function _updateBlocks()
    {
        User_InstallUtils::smartUpdateAllOfBlocks($this->_mTargetXoopsModule,$this->mLog);
    }

    /**
     * _updatePreferences
     * 
     * @param   void
     * 
     * @return  void
    **/
    private function _updatePreferences()
    {
        User_InstallUtils::smartUpdateAllOfConfigs($this->_mTargetXoopsModule,$this->mLog);
    }

    /**
     * executeUpgrade
     * 
     * @param   void
     * 
     * @return  bool
    **/
    public function executeUpgrade()
    {
        return ($this->hasUpgradeMethod() ?
            $this->_callUpgradeMethod() :
            $this->executeAutomaticUpgrade());
    }

    /**
     * _callUpgradeMethod
     * 
     * @param   void
     * 
     * @return  bool
    **/
    private function _callUpgradeMethod()
    {
        ksort($this->_mMileStone);
    
        foreach($this->_mMileStone as $tVer => $tMethod)
        {
            if($tVer >= $this->getCurrentVersion() && is_callable(array($this,$tMethod)))
            {
                return $this->$tMethod();
            }
        }
    
        return false;
    }
    
    /**
     * Does NG XooNIps table exist?
     * 
     * Now check only user_field_detail.
     * 
     * @return int 1:Exist,0:Not Exist,-1:DB error
     */
    private function is_ng_xoonips() {
      $tbl = $this->db->prefix('user_field_detail');
      $sql = "SHOW TABLES LIKE '{$tbl}'";
      $result = $this->db->queryF($sql);
      if($result === FALSE){
        return -1;
      }
      $array = mysql_fetch_array( $result ,MYSQL_ASSOC);
      if($array === FALSE){
        return 0;
      }
      return 1;
    }

 
    /**
     * Table create 
     */
    private function create_modify_table() {
      $module = $this->_mCurrentXoopsModule;
      $log = $this->mLog;
      $rc = $this->is_ng_xoonips();
      if($rc === 0){
        $rc = User_InstallUtils::installSQLAutomatically($module, $log);
        if($rc === false){
          return false;
        }
        $rc = User_InstallUtils::installDataAutomatically($module, $log);        
        if($rc === false){
          return false;
        }
      }else if($rc === -1){
        $log->addReport(XCube_Utils::formatString(_MI_USER_INSTALL_MSG_SQL_ERROR, 'show user_field_detail'));
      }
      #true do not exist
      return true;
    }

    /**
     * executeAutomaticUpgrade
     * 
     * @param   void
     * 
     * @return  bool
    **/
    public function executeAutomaticUpgrade()
    {
        $this->mLog->addReport(_MI_USER_INSTALL_MSG_UPDATE_STARTED);
    
        $this->_updateModuleTemplates();
        if(!$this->_mForceMode && $this->mLog->hasError())
        {
            $this->_processReport();
            return false;
        }
    
        $this->_updateBlocks();
        if(!$this->_mForceMode && $this->mLog->hasError())
        {
            $this->_processReport();
            return false;
        }
    
        $this->_updatePreferences();
        if(!$this->_mForceMode && $this->mLog->hasError())
        {
            $this->_processReport();
            return false;
        }
    
        $this->saveXoopsModule($this->_mTargetXoopsModule);
        if(!$this->_mForceMode && $this->mLog->hasError())
        {
            $this->_processReport();
            return false;
        }

        $this->create_modify_table();
        $this->_processReport();
    
        return true;
    }

	function _processScript()
	{
		$installScript = trim($this->_mTargetXoopsModule->getInfo('onUpdate'));
		if ($installScript != false) {
			require_once USER_TRUST_PATH . "/" . $installScript;
			$funcName = 'xoops_module_update_' . $this->_mTargetXoopsModule->get('dirname');
			if (function_exists($funcName)) {
				if (!call_user_func($funcName, $this->_mTargetXoopsModule, $this->getCurrentVersion(), new XCube_Ref($this->mLog))) {
					$this->mLog->addError("Failed to execute " . $funcName);
				}
			}
		}
	}
    /**
     * saveXoopsModule
     * 
     * @param   XoopsModule  &$module
     * 
     * @return  void
    **/
    public function saveXoopsModule(/*** XoopsModule ***/ &$module)
    {
        $moduleHandler =& UserUtils::getXoopsHandler('module');
    
        if($moduleHandler->insert($module))
        {
            $this->mLog->addReport(_MI_USER_INSTALL_MSG_UPDATE_FINISHED);
        }
        else
        {
            $this->mLog->addError(_MI_USER_INSTALL_ERROR_UPDATE_FINISHED);
        }
    }

    /**
     * _processReport
     * 
     * @param   void
     * 
     * @return  void
    **/
    private function _processReport()
    {
        if(!$this->mLog->hasError())
        {
            $this->mLog->add(
                XCube_Utils::formatString(
                    _MI_USER_INSTALL_MSG_MODULE_UPDATED,
                    $this->_mCurrentXoopsModule->get('name')
                )
            );
        }
        else
        {
            $this->mLog->add(
                XCube_Utils::formatString(
                    _MI_USER_INSTALL_ERROR_MODULE_UPDATED,
                    $this->_mCurrentXoopsModule->get('name')
                )
            );
        }
    }
}


