<?php

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

class Server extends Model
{
  var $Id;
  var $Server;
  var $Task;
  
  function __construct($System, $Id)
  {
    parent::__construct($System);
    $this->Task = new Task($System);
    $this->Id = $Id;
    $DbResult = $this->Database->query('SELECT * FROM `Server` WHERE `Id`='.$Id);
    if($DbResult->num_rows > 0)
    {
      $this->Server = $DbResult->fetch_assoc();
      $DbResult = $this->Database->query('SELECT * FROM `Database` WHERE `Id`='.$this->Server['Database']);
      if($DbResult->num_rows > 0) $this->Server['Database'] = $DbResult->fetch_assoc();
        else $this->Server['Database'] = array('Emulator' => 0);
      $DbResult = $this->Database->query('SELECT * FROM `Emulator` WHERE `Id`='.$this->Server['Database']['Emulator']);
      if($DbResult->num_rows > 0) $this->Server['Database']['Emulator'] = $DbResult->fetch_assoc();
        else $this->Server['Database']['Emulator'] = array('Client' => 0);
      $DbResult = $this->Database->query('SELECT * FROM `Client` WHERE `Id`='.$this->Server['Database']['Emulator']['Client']);
      if($DbResult->num_rows > 0) $this->Server['Database']['Emulator']['Client'] = $DbResult->fetch_assoc();
        else $this->Server['Database']['Emulator']['Client'] = array();
    }
  } 
  
  function CreateDatabase()
  {
    $this->Database->query('CREATE DATABASE `server'.$this->Id.'_realmd`');
    $this->Database->query('CREATE USER "server'.$this->Id.'"@"localhost" IDENTIFIED BY "server'.$this->Id.'"');
    $this->Database->query('GRANT ALL PRIVILEGES ON `server'.$this->Id.'\_realmd` . * TO "server'.$this->Id.'"@"localhost" WITH GRANT OPTION');
  }
  
  function DeleteDatabase()
  {
    $this->Database->query('DROP DATABASE `server'.$this->Id.'_realmd`');
    $this->Database->query('DROP USER "server'.$this->Id.'"@"localhost"');
  }
  
  function Start()
  {
    $this->Lock();
    $this->SaveConfiguration();
    $this->Task->Add('Start emulátoru', array(
      'php www/shell.php ServerLock '.$this->Id,
      'server/'.$this->Id.'/bin/start.sh',
      'php www/shell.php ServerUnLock '.$this->Id,
    ));
    return('Požadavek na start serveru zařazen.');
  }
  
  function Stop()
  {
    $this->Lock();
    $this->Task->Add('Zastavení emulátoru', array(
      'php www/shell.php ServerLock '.$this->Id,
      'server/'.$this->Id.'/bin/stop.sh',
      'php www/shell.php ServerUnLock '.$this->Id,
    ));
    return('Požadavek na zastavení serveru zařazen.');
  }
   
  function GetState()
  {
    $State = array();
    $State['RealmdPortState'] = $this->System->NetworkPortState('localhost', $this->Server['NetworkPortRealmd']);
    $State['Online'] = $State['RealmdPortState'];
    $DbResult = $this->Database->query('SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = "server'.$this->Id.'_realmd"');
    $DbRow = $DbResult->fetch_row();
    if($DbRow[0] > 0)
    {
      $State['AccountCount'] = $DbRow[0];
      $DbResult = $this->Database->query('SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = "server'.$this->Id.'_realmd" AND TABLE_NAME = "uptime"');
      $DbRow = $DbResult->fetch_row();
      if($DbRow[0] > 0)
      {
        $DbResult = $this->Database->query('SELECT uptime FROM server'.$this->Id.'_realmd.uptime AS T ORDER BY T.startstring DESC');
        $DbRow = $DbResult->fetch_assoc();
        $State['Uptime'] = $DbRow['uptime'];
      } else $State['Uptime'] = 0;
    } else
    {
      $State['AccountCount'] = 0;
      $State['Uptime'] = 0;
    }
    $State['UsedMemory'] = $this->GetUsedMemory();
    return($State);
  }
  
  function UpdateRealmlist()
  {
    $this->Database->query('TRUNCATE TABLE server'.$this->Id.'_realmd.realmlist');
    $DbResult = $this->Database->select('Realm', '*', 'Server = '.$this->Id);
    while($Realm = $DbResult->fetch_assoc())
    {
      $this->Database->insert('server'.$this->Id.'_realmd`.`realmlist', 
        array('id' => $Realm['Id'], 'name' => addslashes($Realm['Name']), 
        'address' => $this->Config['Web']['Host'], 'port' => $Realm['NetworkPortWorldd'], 
        'icon' => 0, 'timezone' => 1, 'color' => 0));
    }
  }
  
  function SaveConfiguration()
  {
    $this->SetupConfigurationFiles();
    $this->UpdateRealmlist();
    $this->UpdateScripts();
  }
  
  function UpdateScripts()
  {
    $ServerBinDir = '../server/'.$this->Id.'/bin/';
    if(!file_exists($ServerBinDir)) mkdir($ServerBinDir, 0777, true);       

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

    // Stop script
    $ScriptFileName = '../server/'.$this->Id.'/bin/stop.sh';
    $Content = array
    (
      '#!/bin/sh',
      'ps -ef | grep \'SCREEN -A -m -d -S server'.$this->Id.'-realmd\' | grep -v grep | awk \'{print $2}\' | xargs -i kill {}',
    );
    file_put_contents($ScriptFileName, implode("\n", $Content));
    chmod($ScriptFileName, 0777);    
  }
  
  function SetupConfigurationFiles()
  {
    $EmulatorEtcDir = '../emulator/'.$this->Server['Database']['Emulator']['Id'].'/etc/';
    $ServerEtcDir = '../server/'.$this->Id.'/etc/';
    $ServerLogDir = '../server/'.$this->Id.'/log/';
    if(!file_exists($ServerEtcDir)) mkdir($ServerEtcDir, 0777, true);    
    if(!file_exists($ServerLogDir)) mkdir($ServerLogDir, 0777, true);    
    
    // realmd.conf
    $EmulatorConfig = new MangosConfigurationFile($this->System);
    $EmulatorConfig->Load($EmulatorEtcDir.'realmd.conf.dist');
    $EmulatorConfig->ParameterList['LoginDatabaseInfo'] = 'localhost;3306;server'.$this->Id.';server'.$this->Id.';server'.$this->Id.'_realmd';
    $EmulatorConfig->ParameterList['RealmServerPort'] = $this->Server['NetworkPortRealmd'];
    $EmulatorConfig->ParameterList['LogsDir'] = 'server/'.$this->Id.'/log';
    $EmulatorConfig->Save($ServerEtcDir.'realmd.conf');
  }
  
  function UpdateState()
  {
    $State = $this->GetState();
    $this->Database->update('Server', 'Id='.$this->Id, array(
      'Online' => $State['Online'], 
      'AccountCount' => $State['AccountCount'],
    ));
  } 
  
  function UpdateStateAll()
  {
    $DbResult = $this->Database->select('Server', 'Id');
    while($DbRow = $DbResult->fetch_assoc())
    {
      $Server = new Server($this->System, $DbRow['Id']);
      $Server->UpdateState();
    }    
  } 
  
  function NewAccount($Name, $Password, $Password2, $Email, $Expansion)
  {
    $Output = '';
    if(($Password == '') or ($Password2 == '') or ($Name == '') or ($Email == '')) $Output = 'Vyplňte správně všechny údaje.';
    else if($Password != $Password2) $Output = 'Hesla si neodpovídají.';
    else 
    {
      $Name = strtoupper($Name);
      $DbResult = $this->Database->query('SELECT Id FROM server'.$this->Id.'_realmd.account WHERE username="'.$Name.'"');
      if($DbResult->num_rows > 0) $Output = 'Účet se zadaným jménem již existuje.';
      else
      {
        $Password = sha1($Name.':'.strtoupper($Password));
        $this->Database->query('INSERT INTO `server'.$this->Id.'_realmd`.`account` (`username`, `sha_pass_hash`, `email`, `joindate`, `expansion`) VALUES ("'.$Name.'", "'.$Password.'", "'.$Email.'", NOW(), '.$Expansion.')');
        $Output = 'Nový účet vytvořen.';
      }
    }
    return($Output);
  }
  
  function ImportDatabase($Delete = false)
  {
    $this->Lock();
    $CommandList = array(
      'php www/shell.php ServerLock '.$this->Id,
    );
    if($Delete == true) 
    {
      $CommandList = array_merge($CommandList, array(
      'mysql --silent --skip-column-names -u server'.$this->Id.' -pserver'.$this->Id.' server'.$this->Id.'_realmd -e "show tables" | gawk \'{print "drop table " $1 ";"}\' | mysql -u server'.$this->Id.' -pserver'.$this->Id.' server'.$this->Id.'_realmd',
      ));
    }
    // Lookup nearest database with full import
    $DbResult = $this->Database->query('SELECT * FROM `Database` WHERE (`Emulator` <> 0) AND (`Revision` <= '.$this->Server['Database']['Revision'].') AND (`SourceFileName` <> "") ORDER BY `Revision` DESC');
    $Database = $DbResult->fetch_assoc();
    
    $CommandList = array_merge($CommandList, array(
      'mysql --user=server'.$this->Id.' --password=server'.$this->Id.' server'.$this->Id.'_realmd < emulator/'.$Database['Emulator']['Id'].'/share/mangos/sql/realmd.sql',
      'php www/shell.php ServerDatabaseChange '.$this->Id.' '.$Database['Id'],
      'php www/shell.php ServerUnLock '.$this->Id,
    ));    
    $this->Task->Add('Inicializace databáze', $CommandList);
    
    if($Database['Id'] != $this->Server['Database']['Id']) 
    {
      $NewDatabaseId = $this->Server['Database']['Id'];
      $this->Server['Database']['Id'] = $Database['Id'];
      $this->Update($NewDatabaseId, false, false);
    }
    return('Úloha načtení nové databáze zařazena do fronty.');
  } 

  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 ServerLock '.$this->Id,
    );
    $DbResult = $this->Database->query('SELECT `Revision` FROM `Database` WHERE `Id` = '.$this->Server['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->Id.' --password=server'.$this->Id.' server'.$this->Id.'_'.$Parts[0].' < database/'.$DbRow['Id'].'/'.$Parts[1];
        $Commands[] = $Command;
      }     
    }
    $Commands = array_merge($Commands, array(
      'php www/shell.php ServerDatabaseChange '.$this->Id.' '.$DatabaseId,
      'php www/shell.php ServerUnLock '.$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('Server', 'Id='.$this->Id, array('Lock' => 1));
  }
  
  function UnLock()
  {
    $this->Database->update('Server', 'Id='.$this->Id, array('Lock' => 0));
  }
  
  function ChangeDatabaseId($Id)
  {
    $this->Database->update('Server', 'Id='.$this->Id, array('Database' => $Id));
    $this->SaveConfiguration();
  }
  
  function GetUsedMemory()
  {
    $UsedMemory = 0;
    $DbResult = $this->Database->select('Realm', 'Id', 'Server='.$this->Id);
    while($DbRow = $DbResult->fetch_assoc())
    {
      $Realm = new Realm($this->System, $DbRow['Id']);
      $UsedMemory += $Realm->GetUsedMemory();
    }
    return($UsedMemory);
  }  

  function GetPatchList()
  {
    // http://us.version.worldofwarcraft.com/update/PatchSequenceFile.txt
    $Output = array('[WoW]', 'CurrentBuild='.$this->Server['Database']['Emulator']['Client']['BuildNumber']);
    $DbResult = $this->Database->query('SELECT * FROM Client WHERE BuildNumber <= '.$this->Server['Database']['Emulator']['Client']['BuildNumber'].' AND Patch != "" ORDER BY BuildNumber');
    while($DbRow = $DbResult->fetch_assoc())
    {
      $Output[] = $DbRow['BuildNumber'].'='.$DbRow['Patch'];
    }
    return(implode("\n", $Output));
  } 
  
  function RealmCount()
  {
    $DbResult = $this->Database->query('SELECT COUNT(*) FROM Realm WHERE Server='.$this->Id);
    $DbRow = $DbResult->fetch_row();
    return($DbRow[0]);
  }  
  
  function UpdateRealmlistAccountCount()
  {
    $this->Database->query('TRUNCATE TABLE server'.$this->Id.'_realmd.realmcharacters');
    $DbResult = $this->Database->select('Realm', '*', 'Server = '.$this->Id);
    while($Realm = $DbResult->fetch_assoc())
    {
      $this->Database->query('INSERT INTO server'.$this->Id.'_realmd.realmcharacters (SELECT '.$Realm['Id'].' AS realmid, server'.$this->Id.'_realmd.account.id AS acctid, (SELECT COUNT(*) FROM realm'.$Realm['Id'].'_characters.characters WHERE realm'.$Realm['Id'].'_characters.characters.account = server'.$this->Id.'_realmd.account.id) AS numchars FROM server'.$this->Id.'_realmd.account)');
    }
  }
}
