<?php

include_once(dirname(__FILE__).'/../../Base/Model.php');

class Realm extends Model
{
  var $Id;
  var $Data;
  var $Task;
  
  function __construct($System, $Id)
  {
    parent::__construct($System);
    $this->Task = new Task($System);
    $this->Id = $Id;
    $DbResult = $this->Database->query('SELECT * FROM `Realm` WHERE `Id`='.$Id);
    if($DbResult->num_rows > 0)
    {
      $this->Data = $DbResult->fetch_assoc();
      $DbResult = $this->Database->query('SELECT * FROM `Database` WHERE `Id`='.$this->Data['Database']);
      if($DbResult->num_rows > 0) $this->Data['Database'] = $DbResult->fetch_assoc();
        else $this->Data['Database'] = array('Emulator' => 0);
      $DbResult = $this->Database->query('SELECT * FROM `Emulator` WHERE `Id`='.$this->Data['Database']['Emulator']);
      if($DbResult->num_rows > 0) $this->Data['Database']['Emulator'] = $DbResult->fetch_assoc();
        else $this->Data['Database']['Emulator'] = array('Client' => 0);
      $DbResult = $this->Database->query('SELECT * FROM `Client` WHERE `Id`='.$this->Data['Database']['Emulator']['Client']);
      if($DbResult->num_rows > 0) $this->Data['Database']['Emulator']['Client'] = $DbResult->fetch_assoc();
        else $this->Data['Database']['Emulator']['Client'] = array();
    }
  } 

  function GetState()
  {    
    $State = array();
    $State['WorlddPortState'] = $this->System->NetworkPortState('localhost', $this->Data['NetworkPortWorldd']);
    $State['Online'] = $State['WorlddPortState'];
    $State['Uptime'] = $this->Uptime();
    $DbResult = $this->Database->query('SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = "realm'.$this->Id.'_characters"');
    $DbRow = $DbResult->fetch_row();
    if($DbRow[0] > 0)
    {
      $DbResult = $this->Database->query('SELECT COUNT(*) FROM realm'.$this->Id.'_characters.characters AS T WHERE T.online = 1');
      $DbRow = $DbResult->fetch_row();
      $State['CharacterOnlineCount'] = $DbRow[0];
      $DbResult = $this->Database->query('SELECT COUNT(*) FROM realm'.$this->Id.'_characters.characters AS T');
      $DbRow = $DbResult->fetch_row();
      $State['CharacterCount'] = $DbRow[0];
    } else
    {
      $State['CharacterOnlineCount'] = 0;
      $State['CharacterCount'] = 0;
    }
    return($State);
  }  
  
  function CreateDatabase()
  {
    $this->Database->query('CREATE DATABASE `realm'.$this->Id.'_mangos`');
    $this->Database->query('CREATE DATABASE `realm'.$this->Id.'_characters`');
    $this->Database->query('CREATE DATABASE `realm'.$this->Id.'_scriptdev2`');
    $this->Database->query('GRANT ALL PRIVILEGES ON `realm'.$this->Id.'\_mangos` . * TO "server'.$this->Data['Server'].'"@"localhost" WITH GRANT OPTION');
    $this->Database->query('GRANT ALL PRIVILEGES ON `realm'.$this->Id.'\_characters` . * TO "server'.$this->Data['Server'].'"@"localhost" WITH GRANT OPTION');
    $this->Database->query('GRANT ALL PRIVILEGES ON `realm'.$this->Id.'\_scriptdev2` . * TO "server'.$this->Data['Server'].'"@"localhost" WITH GRANT OPTION');
  }
  
  function DeleteDatabase()
  {
    $this->Database->query('DROP DATABASE `realm'.$this->Id.'_mangos`');
    $this->Database->query('DROP DATABASE `realm'.$this->Id.'_characters`');
    $this->Database->query('DROP DATABASE `realm'.$this->Id.'_scriptdev2`');
  }

  function ImportDatabase($Delete = false)
  {
    $this->Lock();
    $CommandList = array(
      'php www/shell.php RealmLock '.$this->Id,
    );
    
    // Empty all tables
    if($Delete == true) 
    {
      $CommandList = array_merge($CommandList, array(
        'mysql --silent --skip-column-names -u server'.$this->Data['Server'].' -pserver'.$this->Data['Server'].' realm'.$this->Id.'_mangos -e "show tables" | gawk \'{print "drop table " $1 ";"}\' | mysql -u server'.$this->Data['Server'].' -pserver'.$this->Data['Server'].' realm'.$this->Id.'_mangos',
        'mysql --silent --skip-column-names -u server'.$this->Data['Server'].' -pserver'.$this->Data['Server'].' realm'.$this->Id.'_characters -e "show tables" | gawk \'{print "drop table " $1 ";"}\' | mysql -u server'.$this->Data['Server'].' -pserver'.$this->Data['Server'].' realm'.$this->Id.'_characters',
      'mysql --silent --skip-column-names -u server'.$this->Data['Server'].' -pserver'.$this->Data['Server'].' realm'.$this->Id.'_scriptdev2 -e "show tables" | gawk \'{print "drop table " $1 ";"}\' | mysql -u server'.$this->Data['Server'].' -pserver'.$this->Data['Server'].' realm'.$this->Id.'_scriptdev2',
      ));
    }
    
    // Lookup nearest database with full import
    $DbResult = $this->Database->query('SELECT * FROM `Database` WHERE (`Emulator` <> 0) AND (`Revision` <= '.$this->Data['Database']['Revision'].') AND (`SourceFileName` <> "") ORDER BY `Revision` DESC');
    $Database = $DbResult->fetch_assoc();
    
    $CommandList = array_merge($CommandList, array(
      'mysql --user=server'.$this->Data['Server'].' --password=server'.$this->Data['Server'].' realm'.$this->Id.'_mangos < database/'.$Database['Id'].'/'.$Database['SourceFileName'],
      'mysql --user=server'.$this->Data['Server'].' --password=server'.$this->Data['Server'].' realm'.$this->Id.'_scriptdev2 < emulator/'.$Database['Emulator'].'/source/src/bindings/ScriptDev2/sql/scriptdev2_create_structure.sql',
      'mysql --user=server'.$this->Data['Server'].' --password=server'.$this->Data['Server'].' realm'.$this->Id.'_scriptdev2 < emulator/'.$Database['Emulator'].'/source/src/bindings/ScriptDev2/sql/scriptdev2_script_full.sql',
      'mysql --user=server'.$this->Data['Server'].' --password=server'.$this->Data['Server'].' realm'.$this->Id.'_mangos < emulator/'.$Database['Emulator'].'/source/src/bindings/ScriptDev2/sql/mangos_scriptname_full.sql',
      'mysql --user=server'.$this->Data['Server'].' --password=server'.$this->Data['Server'].' realm'.$this->Id.'_characters < emulator/'.$Database['Emulator'].'/source/sql/characters.sql'));
    if($Database['ACIDSourceFileName'] != '')
      $CommandList[] = 'mysql --user=server'.$this->Data['Server'].' --password=server'.$this->Data['Server'].' realm'.$this->Id.'_mangos < database/'.$Database['Id'].'/'.$Database['ACIDSourceFileName'];
    $CommandList[] = 'php www/shell.php RealmDatabaseChange '.$this->Id.' '.$Database['Id'];
    $commandList[] = 'php www/shell.php RealmUnLock '.$this->Id;
    $this->Task->Add('Inicializace databáze', $CommandList);
    
    if($Database['Id'] != $this->Data['Database']['Id']) 
    {
      $NewDatabaseId = $this->Data['Database']['Id'];
      $this->Data['Database']['Id'] = $Database['Id'];
      $this->Update($NewDatabaseId, false, false);
    }
    return('Úloha načtení nové databáze zařazena do fronty.');
  } 

  function Start()
  {
    $this->Lock();
    $this->SaveConfiguration();
    $this->Task->Add('Start světa', array(
      'php www/shell.php RealmLock '.$this->Id,
      'realm/'.$this->Id.'/bin/start.sh',
      'php www/shell.php RealmUnLock '.$this->Id,
    ));
    return('Požadavek na start světa zařazen.');
  }
  
  function Stop()
  {
    $this->Lock();
    $this->Task->Add('Zastavení světa', array(
      'php www/shell.php RealmLock '.$this->Id,
      'realm/'.$this->Id.'/bin/stop.sh',
      'php www/shell.php RealmUnLock '.$this->Id,
    ));
    return('Požadavek na zastavení světa zařazen.');
  }

  function UpdateRealmlist()
  {
    $Server = new Server($this->System, $this->Data['Server']);
    $Server->UpdateRealmList();
  }
  
  function SaveConfiguration()
  {
    $this->SetupConfigurationFiles();
    $this->UpdateRealmlist();
    $this->UpdateScripts();
  }

  function UpdateScripts()
  {
    $RealmBinDir = '../realm/'.$this->Id.'/bin/';
    if(!file_exists($RealmBinDir)) mkdir($RealmBinDir, 0777, true);    
    
    // GDB script
    $ScriptFileName = '../realm/'.$this->Id.'/bin/mangos.gdb';
    $Content = array
    (
      'run -c realm/'.$this->Id.'/etc/mangosd.conf',
      'info thread',
    );
    for($I = 1; $I < $this->Config['MangosWorlddThreadCountMax']; $I++)
      $Content[] = 'thread apply '.$I.' bt full';
    file_put_contents($ScriptFileName, implode("\n", $Content));
    chmod($ScriptFileName, 0666);    

    // Start script
    $ScriptFileName = '../realm/'.$this->Id.'/bin/start.sh';
    $Content = array
    (
      '#!/bin/sh',
      'if [ -z `ps -ef | grep \'SCREEN -A -m -d -S realm'.$this->Id.'-worldd\' | grep -v grep | awk \'{print $2}\'` ]',
      'then',
      'screen -A -m -d -S realm'.$this->Id.'-worldd realm/'.$this->Id.'/bin/worldd_restarter.sh',
      'fi',
    );
    file_put_contents($ScriptFileName, implode("\n", $Content));
    chmod($ScriptFileName, 0777);    

    $ScriptFileName = '../realm/'.$this->Id.'/bin/worldd_restarter.sh';
    $Content = array
    (
      '#!/bin/sh',
      'while [ 1=1 ] ; do',
      'gdb emulator/'.$this->Data['Database']['Emulator']['Id'].'/bin/mangos-worldd -x realm/'.$this->Id.'/bin/mangos.gdb --batch >>realm/'.$this->Id.'/log/mangos-worldd.log 2>>realm/'.$this->Id.'/log/mangos-worldd.err',
      'php www/shell.php ServerProcessLog '.$this->Id.' >>realm/'.$this->Id.'/log/mangos_debug.log 2>>realm/'.$this->Id.'/log/mangos_debug.err',
      'sleep 3',
      'done',
    );
    file_put_contents($ScriptFileName, implode("\n", $Content));
    chmod($ScriptFileName, 0777);    

    // Stop script
    $ScriptFileName = '../realm/'.$this->Id.'/bin/stop.sh';
    $Content = array
    (
      '#!/bin/sh',
      'ps -ef | grep \'SCREEN -A -m -d -S realm'.$this->Id.'-worldd\' | grep -v grep | awk \'{print $2}\' | xargs -i kill {}',
    );
    file_put_contents($ScriptFileName, implode("\n", $Content));
    chmod($ScriptFileName, 0777);    
  }
  
  function SetupConfigurationFiles()
  {
    $EmulatorEtcDir = '../emulator/'.$this->Data['Database']['Emulator']['Id'].'/etc/';
    $RealmEtcDir = '../realm/'.$this->Id.'/etc/';
    $RealmLogDir = '../realm/'.$this->Id.'/log/';
    if(!file_exists($RealmEtcDir)) mkdir($RealmEtcDir, 0777, true);    
    if(!file_exists($RealmLogDir)) mkdir($RealmLogDir, 0777, true);    
    
    // mangosd.conf
    if(!file_exists($RealmEtcDir.'mangosd.conf')) 
      file_put_contents($RealmEtcDir.'mangosd.conf', file_get_contents($EmulatorEtcDir.'mangosd.conf.dist'));
    $EmulatorConfig = new MangosConfigurationFile($this->System);
    $EmulatorConfig->Load($RealmEtcDir.'mangosd.conf');
    $EmulatorConfig->ParameterList['RealmID'] = $this->Data['Id'];
    $EmulatorConfig->ParameterList['LoginDatabaseInfo'] = 'localhost;3306;server'.$this->Data['Server'].';server'.$this->Data['Server'].';server'.$this->Data['Server'].'_realmd';
    $EmulatorConfig->ParameterList['WorldDatabaseInfo'] = 'localhost;3306;server'.$this->Data['Server'].';server'.$this->Data['Server'].';realm'.$this->Id.'_mangos';
    $EmulatorConfig->ParameterList['CharacterDatabaseInfo'] = 'localhost;3306;server'.$this->Data['Server'].';server'.$this->Data['Server'].';realm'.$this->Id.'_characters';
    $EmulatorConfig->ParameterList['ScriptDev2DatabaseInfo'] = 'localhost;3306;server'.$this->Data['Server'].';server'.$this->Data['Server'].';realm'.$this->Id.'_scriptdev2';
    $EmulatorConfig->ParameterList['WorldServerPort'] = $this->Data['NetworkPortWorldd'];
    $EmulatorConfig->ParameterList['DataDir'] = 'wowclient/'.$this->Data['Database']['Emulator']['Client']['Version'];
    $EmulatorConfig->ParameterList['LogsDir'] = 'realm/'.$this->Id.'/log';
    $EmulatorConfig->ParameterList['LogLevel'] = 1;
    $EmulatorConfig->ParameterList['LogSQL'] = 0;
    $EmulatorConfig->Save($RealmEtcDir.'mangosd.conf');
      
    // scriptdev2.conf
    $EmulatorConfig = new MangosConfigurationFile($this->System);
    $EmulatorConfig->Load($EmulatorEtcDir.'scriptdev2.conf');
    $EmulatorConfig->ParameterList['ScriptDev2DatabaseInfo'] = 'localhost;3306;server'.$this->Data['Server'].';server'.$this->Data['Server'].';realm'.$this->Id.'_scriptdev2';
    $EmulatorConfig->Save($RealmEtcDir.'scriptdev2.conf');  
  }

  function Update($DatabaseId, $DoBackup = true, $DoStop = true)
  {
    $this->Lock();
    $Output = '';
    
    // Stop server before update
    if($DoStop)
    {
      $Output .= $this->Stop();
    }
    
    // Backup current 
    if($DoBackup)
    {
      $Backup = new Backup($this->System, 0);
      $Output .= '<br />'.$Backup->Create($this->Id);
    }
    
    // Do update
    $Commands = array(
      'php www/shell.php RealmLock '.$this->Id,
    );
    $DbResult = $this->Database->query('SELECT `Revision` FROM `Database` WHERE `Id` = '.$this->Data['Database']['Id']);
    $DbRow = $DbResult->fetch_assoc();
    $DatabaseRevisionStart = $DbRow['Revision'];
    $DbResult = $this->Database->query('SELECT `Revision` FROM `Database` WHERE `Id` = '.$DatabaseId);
    $DbRow = $DbResult->fetch_assoc();
    $DatabaseRevisionEnd = $DbRow['Revision'];
    $DbResult = $this->Database->query('SELECT * FROM `Database` WHERE (`Revision` > '.$DatabaseRevisionStart.') AND (`Revision` <= '.$DatabaseRevisionEnd.') ORDER BY `Revision`');
    while($DbRow = $DbResult->fetch_assoc())
    {
      $Updates = explode("\n", $DbRow['Update']);
      foreach($Updates as $Update)
      if($Update != '')
      {
        $Parts = explode('|', $Update); 
        if($Parts[0] != 'realmd')
          $Command = 'mysql --user=server'.$this->Data['Server'].' --password=server'.$this->Data['Server'].' realm'.$this->Id.'_'.$Parts[0].' < database/'.$DbRow['Id'].'/'.$Parts[1];
        $Commands[] = $Command;
      }     
    }
    $Commands = array_merge($Commands, array(
      'php www/shell.php RealmDatabaseChange '.$this->Id.' '.$DatabaseId,
      'php www/shell.php RealmUnLock '.$this->Id,
    ));

    $this->Task->Add('Aktualizace databáze', $Commands);
    $Output .= '<br />Úloha aktualizace serveru byla přidána do fronty.';
    return($Output);
  }
  
  function Lock()
  {
    $this->Database->update('Realm', 'Id='.$this->Id, array('Lock' => 1));
  }
  
  function UnLock()
  {
    $this->Database->update('Realm', 'Id='.$this->Id, array('Lock' => 0));
  }
  
  function ChangeDatabaseId($Id)
  {
    $this->Database->update('Realm', 'Id='.$this->Id, array('Database' => $Id));
    $this->SaveConfiguration();
  }
  
  function GetUsedMemory()
  {
    $Output = array();
    if(isset($this->Data['Database']['Emulator']['Id']))
      exec('ps aux|grep "emulator/'.$this->Data['Database']['Emulator']['Id'].'/bin/mangos-worldd -c realm/'.$this->Id.'/etc/mangosd.conf"| grep -v grep', $Output);
    if(count($Output) > 0)
    {
      while(strpos($Output[0], '  ') > 0) $Output[0] = str_replace('  ', ' ', $Output[0]);
      $Parts = explode(' ', $Output[0]);
      return($Parts[4]);
    } else return(0);
  } 
  
  function ProcessLog()
  {
    $File = fopen('../realm/'.$this->Id.'/log/mangos-worldd.log', 'r');
    while(true)
    {
      $Readers = array($File);
      if(stream_select($Readers, $Writers=null, $Except=null, 0, 15) < 1)
      {
        continue;
      } else
      {
        // read data from the fifo
        $Data = fread($File, 1024);
        echo($Data);
        $this->Database->insert('RealmLog', array('Time' => 'NOW()', 'Realm' => $this->Id, 'Text' => $Data));
      }         
      sleep(1);
    }
    fclose($File);
  }
  
  function GetUser()
  {
    $DbResult = $this->Database->select('Server', 'User', 'Id='.$this->Data['Server']);
    $DbRow = $DbResult->fetch_assoc();
    return($DbRow['User']);
  }
  
  function UpdateState()
  {
    $State = $this->GetState();
    $this->Database->update('Realm', 'Id='.$this->Id, array(
      'Online' => $State['Online'], 
      'CharacterOnlineCount' => $State['CharacterOnlineCount'],
      'CharacterCount' => $State['CharacterCount'],  
    ));
  }
  
  function UpdateStateAll()
  {
    $DbResult = $this->Database->select('Realm', 'Id');
    while($DbRow = $DbResult->fetch_assoc())
    {
      $Realm = new Realm($this->System, $DbRow['Id']);    
      $Realm->UpdateState();
    }    
  } 
  
  function BackupCharacter($AccountId)
  {
  }
  
  function Uptime()
  {
    $DbResult = $this->Database->query('SELECT uptime FROM server'.$this->Data['Server'].'_realmd.uptime WHERE realmid='.$this->Id.' ORDER BY starttime DESC LIMIT 1');
    $DbRow = $DbResult->fetch_assoc();
    return($DbRow['uptime']);
  }
}
