<?php

abstract class UpdateMod {
  protected $ArrArgs = array();
  public $ObjSystemMod = null;
  public $VarUpdateFileName = null;
  public $VarUpdateDirTmpName = null;
  const TMPDIRUPDATE = '/../tmp/';
  public $VarUpdateFile = null;
  static protected $ArrVersion = null;

  public function __construct(Array $ArrArgs, SystemMod $ObjSystemMod) {
    $this->ArrArgs = $ArrArgs;
    $this->ObjSystemMod = $ObjSystemMod;
//    $this->ArrVersion = $this->getVersionCurr();
    $this->VarUpdateFileName = $this->getUpdateFileName();
    $this->VarUpdateDirTmpName = $_SERVER['DOCUMENT_ROOT'] . self::TMPDIRUPDATE;
    $this->VarUpdateFile = $this->VarUpdateDirTmpName . $this->VarUpdateFileName;
    
  }
  
  static function getUpdater(Array $ArrArgs, SystemMod $ObjSystemMod){
    $VarClassname = @$ArrArgs['UrlMethod'].__CLASS__;
    if (false === isset($ArrArgs['UrlMethod']) || false === class_exists($VarClassname)){
      $VarClassname = 'Default'.__CLASS__;
    }
    return new $VarClassname($ArrArgs, $ObjSystemMod);
  }
  
  abstract function UpdateFileIsset();
  
  abstract function UpdateFileGet();

  function getUpdateFileName(){
    if('' != $this->getVersionCurr()){
      return 'supporti_'. $this->ArrArgs['UrlType'] .'_for_version_'. str_replace('.', '_', $this->getVersionCurr()) .'.zip';
    }
    else{
      return '';
    }
  }
  
  function getVersionCurr($VarIn = ''){
    if('' != $VarIn){
      $VarType = $VarIn;
    }
    else{
      $VarType = $this->ArrArgs['UrlType'];
    }
    if (false === isset(self::$ArrVersion[$VarType])) {
      self::$ArrVersion[$VarType] = $this->ObjSystemMod->GetFromDb('SqlGetValue', 'GetOne', $VarType .'Update');
    }
    return self::$ArrVersion[$VarType];
  }
  
  function setVersionCurr($VarVersion, $VarIn = ''){
    if('' != $VarIn){
      $VarType = $VarIn;
    }
    else{
      $VarType = $this->ArrArgs['UrlType'];
    }
    $VarResult = $this->ObjSystemMod->Edit(array('system_key' => $VarType .'Update', 'system_value' => $VarVersion));
    if(true === is_numeric($VarResult)){
      self::$ArrVersion[$VarType] = $this->getVersionCurr();
      return TRUE;
    }
    else{
      return FALSE;
    }
//    return self::$ArrVersion[$VarType];
  }
}

class DefaultUpdateMod extends UpdateMod{
  const UPDATEXMLFILE = 'update.xml';
  const REPORTXMLFILE = 'report.xml';
  const UPDATEBFOLDER = 'Backup';
  public $UpdateExtractFolder = '';
  public $UpdateBackupFolder = '';
  
  
  public function __construct(Array $ArrArgs, SystemMod $ObjSystemMod) {
    parent::__construct($ArrArgs, $ObjSystemMod);
    $this->UpdateExtractFolder = $this->UpdateExtractFolderGet($this->VarUpdateFileName);
    $this->UpdateBackupFolder = $this->UpdateExtractFolder . self::UPDATEBFOLDER ."/";
  }
  
  function UpdateExtractFolderGet($VarFile){
    return $this->VarUpdateDirTmpName . substr($VarFile, 0, strrpos($VarFile,'.')) . date("-Y_m_d_H_i_s") .'/';
  }
  
  function UpdateFileIsset(){
    if (true === is_file($this->VarUpdateFile)) {
      return TRUE;
    }
    else{
      return FALSE;
    }
  }
  
  function UpdateFileGet(){}
  
  function ZipExtract($VarFile){
    $zip = new ZipArchive;
    if (TRUE === $zip->open($VarFile)) {
//      $this->UpdateExtractFolder = $this->ZipExtractFolderGet($this->VarUpdateFileName);
      $zip->extractTo($this->UpdateExtractFolder);
      $zip->close();
      return TRUE;
    } 
    else {
      return FALSE;
    }
  }
  
  function UpdateExec(){
    if (true === $this->UpdateFileIsset()){
      if (true === $this->ZipExtract($this->VarUpdateFile)){
        if(false === is_dir($this->UpdateBackupFolder)){
          mkdir($this->UpdateBackupFolder, 0755);
        }
        $VarFileXml = $this->UpdateExtractFolder . self::UPDATEXMLFILE;
        $VarXmlContent = new SimpleXMLElement($VarFileXml, NULL, TRUE);
        $ObjCommand = $this->UpdateExecInit($VarXmlContent);
        return $ObjCommand;
      }
      else{
        return array('error' => $this->ObjSystemMod->ArrMsSp->Sp_LangMessage['error']['ErrCannotOpenZipFile']);
      }
    }
    else{
      return array('error' => $this->ObjSystemMod->ArrMsSp->Sp_LangMessage['error']['ErrNoFile']);
    }
  }
  
  function UpdateExecDependsOn(SimpleXMLElement $VarXmlContent) {
    $VarDependsOnFlag = TRUE;
    foreach ($VarXmlContent->head->DependsOn->children() as $keyDependsOn => $valDependsOn) {
//      echo $keyDependsOn. ": ". $valDependsOn .": ". $this->getVersionCurr($keyDependsOn). "<br />";
      if('' != $valDependsOn && $valDependsOn != $this->getVersionCurr($keyDependsOn)){
        $VarDependsOnFlag = FALSE;
      }
    }
    return $VarDependsOnFlag;
  }
  
  function UpdateExecInit(SimpleXMLElement $VarXmlContent) {
    if(TRUE === $this->UpdateExecDependsOn($VarXmlContent)){
      $ArrResultCommands = array();
      $VarResultCommandTotal = TRUE;
      foreach ($VarXmlContent->body->children() as $keyTypes => $ObjCommands) {
//        echo "keyTypes: ". $keyTypes ."<br />";
        foreach ($ObjCommands->Commands->children() as $keyCommands => $valueCommands) {
          //echo "keyCommands: ". var_dump($valueCommands->children()->getName()) ."<br />";
          $VarCommand = $valueCommands->children()->getName();
  //        var_dump($valueCommands->children()->$VarCommand);// ."<br />";
          $ObjUpdateCommandRegistry = UpdateCommandRegistry::getUpdateCommandRegistry($keyTypes, $VarCommand, $this);
          $VarResultCommand = $ObjUpdateCommandRegistry->Execute($valueCommands->children()->$VarCommand);
          $ArrResultCommands[$keyTypes][] = $VarResultFlag = array($VarCommand => $VarResultCommand);
          if(FALSE === $VarResultCommand){
            $VarResultCommandTotal = FALSE;
          }
        }
      }
      if(TRUE === $VarResultCommandTotal){
        $this->setVersionCurr($VarXmlContent->head->Version);
      }
      else{
        $VarResultCommandTotal = array('error' => $this->ObjSystemMod->ArrMsSp->Sp_LangMessage['error']['ErrUpdateFalse']);
      }
      $this->UpdateReportSave($ArrResultCommands);
      return $VarResultCommandTotal;
    }
    else{
      return array('error' => $this->ObjSystemMod->ArrMsSp->Sp_LangMessage['warning']['WarnNoUpdate']);
    }
  }
  
  
  function UpdateReportSave(Array $Args) {
    $ReportXml = new SimpleXMLElement("<?xml version=\"1.0\"?><ReportInfo></ReportInfo>");
    self::array_to_xml($Args,$ReportXml);

    $VarFileXml = $this->UpdateExtractFolder . self::REPORTXMLFILE;
    $ReportXml->asXML($VarFileXml);
  }
  
  static function array_to_xml($student_info, &$xml_student_info) {
      foreach($student_info as $key => $value) {
        if(is_array($value)) {
          if(!is_numeric($key)){
              $subnode = $xml_student_info->addChild("$key");
              self::array_to_xml($value, $subnode);
          }
          else{
              self::array_to_xml($value, $xml_student_info);
          }
        }
        else {
          $xml_student_info->addChild("$key","$value");
        }
      }
   }
}

class CdUpdateMod extends UpdateMod{
  function UpdateFileIsset(){
    if (true === is_file($this->ArrArgs['RestorePoFile']['tmp_name']) && $this->VarUpdateFileName == $this->ArrArgs['RestorePoFile']['name']) {
      return TRUE;
    }
    else{
      return FALSE;
    }
  }
  
  function UpdateFileGet(){
    if (move_uploaded_file($this->ArrArgs['RestorePoFile']['tmp_name'], $this->VarUpdateFile)){
      return TRUE;
    }
    else{
//      echo "wefwef".$this->VarUpdateFile;
      return FALSE;
    }
  }
}

class NetUpdateMod extends UpdateMod{
  const URLUPDATE = 'http://supporti.ru/files/install/update/';
  
  public function __construct(Array $ArrArgs, SystemMod $ObjSystemMod) {
    parent::__construct($ArrArgs, $ObjSystemMod);
    $this->VarUpdateUrlFileName = self::URLUPDATE . $this->VarUpdateFileName;
  }
  
  function UpdateFileIsset(){
    $ArrAnswer = get_headers($this->VarUpdateUrlFileName);
    if ('HTTP/1.0 200 OK' == $ArrAnswer['0'] || 'HTTP/1.1 200 OK' == $ArrAnswer['0']) {
      return TRUE;
    }
    else{
      return FALSE;
    }
  }
  
  function UpdateFileGet(){
    if (copy($this->VarUpdateUrlFileName, $this->VarUpdateFile)) {
      return TRUE;
    }
    else{
      return FALSE;
    }
  }
}

class UpdateCommandRegistry{
  protected static $ArrCommand;
  
  public function getUpdateCommandRegistry($VarType = '', $VarCommand = '', $ObjUpdateMod){
//    if (false === isset(self::$ArrDbConnection[$VarConnectionName]) || self::$ArrDbConnection[$VarConnectionName] instanceof MDB2) {
    if (false === isset(self::$ArrCommand[$VarType][$VarCommand])) {
      $VarClassName = 'Command'. $VarType . $VarCommand;
      self::$ArrCommand[$VarType][$VarCommand] = new $VarClassName($ObjUpdateMod);
    }
    return self::$ArrCommand[$VarType][$VarCommand];
  }
}


abstract class Command{
  protected $ObjUpdateMod;

  public function __construct($ObjUpdateMod){
    $this->ObjUpdateMod = $ObjUpdateMod;
  }
  
  public abstract function Execute($Args);
//  public abstract function UnExecute();
  protected function cp_r( $path, $dest ){
    define('DS', DIRECTORY_SEPARATOR);
    if( is_dir($path) ){
      @mkdir( $dest, 0755);
      $objects = scandir($path);
      if( sizeof($objects) > 0 ){
        foreach( $objects as $file ){
          if( $file == "." || $file == ".." )
            continue;
          // go on
          if( is_dir( $path.DS.$file ) ){
            self::cp_r( $path.DS.$file, $dest.DS.$file );
          }
          else{
            $VarResult = copy( $path.DS.$file, $dest.DS.$file );
            if(false === $VarResult){
              return FALSE;
            }
          }
        }
      }
      return true;
    }
    elseif( is_file($path) ){
      return copy($path, $dest);
    }
    else{
      return false;
    }
  }
  
  protected function rrmdir($dir) {
    if(true === is_dir($dir)){
      foreach(glob($dir . '/*') as $file) {
        if(true === is_dir($file)){
          self::rrmdir($file);
        }
        else{
          unlink($file);
        }
      }
      $VarResult = rmdir($dir);
    }
    else{
      $VarResult = unlink($dir);
    }
    return $VarResult;
  }
}
?>