<?php

include_once(dirname(__FILE__).'/../Packages/Common/Common.php');
if (file_exists(dirname(__FILE__).'/../Config/Config.php'))
  include_once(dirname(__FILE__).'/../Config/Config.php');
include_once(dirname(__FILE__).'/../Application/Core.php');
include_once(dirname(__FILE__).'/../Application/View.php');
include_once(dirname(__FILE__).'/../Application/Version.php');
include_once(dirname(__FILE__).'/../Application/DefaultConfig.php');
include_once(dirname(__FILE__).'/../Application/UpdateTrace.php');
include_once(dirname(__FILE__).'/PageEdit.php');

// Back compatibility, will be removed
if (isset($InitSystem) and $InitSystem)
{
  $System = new Core();
  $System->DoNotShowPage = true;
  $System->Run();
}

class TempPage extends Page
{
  function Show(): string
  {
    global $TempPageContent;
    return $TempPageContent;
  }
}

function ShowPageClass($Page)
{
  global $System;

  $System->Pages['temporary-page'] = get_class($Page);
  $_SERVER['REDIRECT_QUERY_STRING'] = 'temporary-page';
  $System->PathItems = ProcessURL();
  $System->ShowPage();
}

function ShowPage(string $Content): void
{
  global $TempPageContent, $System;

  $TempPage = new TempPage($System);
  $System->Pages['temporary-page'] = 'TempPage';
  $_SERVER['REDIRECT_QUERY_STRING'] = 'temporary-page';
  $TempPageContent = $Content;
  $System->PathItems = ProcessURL();
  $System->ShowPage();
}

function GetMicrotime(): float
{
  list($Usec, $Sec) = explode(' ', microtime());
  return (float)$Usec + (float)$Sec;
}

$UnitNames = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB');

function HumanSize(float $Value): string
{
  global $UnitNames;

  $UnitIndex = 0;
  while ($Value > 1024)
  {
    $Value = round($Value / 1024, 3);
    $UnitIndex++;
  }
  return $Value.' '.$UnitNames[$UnitIndex];
}

function GetQueryStringArray(string $QueryString): array
{
  $Result = array();
  $Parts = explode('&', $QueryString);
  foreach ($Parts as $Part)
  {
    if ($Part != '')
    {
      if (!strpos($Part, '=')) $Part .= '=';
      $Item = explode('=', $Part);
      $Result[$Item[0]] = $Item[1];
    }
  }
  return $Result;
}

function SetQueryStringArray(array $QueryStringArray): string
{
  $Parts = array();
  foreach ($QueryStringArray as $Index => $Item)
  {
    $Parts[] = $Index.'='.$Item;
  }
  return implode('&amp;', $Parts);
}

function utf2ascii(string $text): string
{
  $return = Str_Replace(
    Array("á","č","ď","é","ě","í","ľ","ň","ó","ř","š","ť","ú","ů","ý","ž","Á","Č","Ď","É","Ě","Í","Ľ","Ň","Ó","Ř","Š","Ť","Ú","Ů","Ý","Ž") ,
    Array("a","c","d","e","e","i","l","n","o","r","s","t","u","u","y","z","A","C","D","E","E","I","L","N","O","R","S","T","U","U","Y","Z") ,
    $text);
  //$return = Str_Replace(Array(" ", "_"), "-", $return); //nahradí mezery a podtržítka pomlčkami
  //$return = Str_Replace(Array("(",")",".","!",",","\"","'"), "", $return); //odstraní ().!,"'
  //$return = StrToLower($return); // velká písmena nahradí malými.
  return $return;
}

function GetMonthYears(int $Days): string
{
  $Month = floor($Days / 30);
  $Year = floor($Month / 12);
  $Days = floor($Days - $Month * 30);
  $Month = $Month - $Year * 12;
  return $Year.'r '.$Month.'m '.$Days.'d';
}

function GetTranslateGoogle($System, string $text, bool $withouttitle = false)
{
//  $text = 'Balthule\'s letter is dire. This Cult of the Dark Strand is a thorn in my side that must be removed. I have been dealing with some of the Dark Strand scum northeast of here at Ordil\'Aran. One of their number possesses a soul gem that I believe holds the secret to the cult\'s power.$b$bBring it to me, and I will be able to decipher the secrets held within.';
 // $text = htmlspecialchars($text);

  $text = str_replace('$B','$B ', $text);
  $text = urlencode($text);
 // $text = strtolower($text);
//  $text = str_replace('&','',$text);
//  $text = str_replace(' ','%20',$text);
  $User = ModuleUser::Cast($System->GetModule('User'))->User;
  $DbResult = $System->Database->select('Language', '`Code`', '`Id`='.$User->Language);
  $DbRow = $DbResult->fetch_assoc();
  $lang = $DbRow['Code'];
  $url = 'https://translate.google.cz/?sl=en&tl='.$lang.'&text='.$text;

  error_reporting(E_ALL ^ E_WARNING);
  if (($handle = @fopen($url, "r")) === FALSE) return false;

  $data = stream_get_contents($handle);
  $data = substr($data, strpos($data, 'result_box'));
  $data = substr($data, strpos($data, '>') + 1);
  $data = substr($data, 0, strpos($data, '</div>'));

  if ($withouttitle)
  while ((strpos($data, '>') !== false and (strpos($data, '<') !== false)) and (strpos($data, '>') > strpos($data, '<')))
  {
    $partbefore = substr($data, 0, strpos($data, '<'));
    //  echo $partbefore;
    $partafter = substr($data, strpos($data, '>') + 1);
    // echo $partafter;
    $data = $partbefore.' '.$partafter;
  }

  $Encoding = new Encoding();
  $data = $Encoding->ToUTF8($data);
  //$data = utf8_encode($data);

  $data = str_replace('$ ', '$',$data);
  $data = str_replace('$b $b', '$b$b', $data);
  $data = str_replace('| ', '|', $data);

  $data = strip_tags($data);

  return $data;
}

function GetPageList(int $TotalCount): array
{
  global $System;

  $QueryItems = GetQueryStringArray($_SERVER['QUERY_STRING']);

  $ItemPerPage = $System->Config['Web']['ItemsPerPage'];
  $Around = round($System->Config['Web']['VisiblePagingItems'] / 2);
  $Result = '';
  $PageCount = floor($TotalCount / $ItemPerPage) + 1;

  if (!array_key_exists('Page', $_SESSION)) $_SESSION['Page'] = 0;
  if (array_key_exists('page', $_GET)) $_SESSION['Page'] = $_GET['page'] * 1;
  if ($_SESSION['Page'] < 0) $_SESSION['Page'] = 0;
  if ($_SESSION['Page'] >= $PageCount) $_SESSION['Page'] = $PageCount - 1;
  $CurrentPage = $_SESSION['Page'];

  $Result .= 'Počet položek: <strong>'.$TotalCount.'</strong> &nbsp; Stránky: ';

  $Result = '';
  if ($PageCount > 1)
  {
    if ($CurrentPage > 0)
    {
      $QueryItems['page'] = 0;
      $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&lt;&lt;</a> ';
      $QueryItems['page'] = ($CurrentPage - 1);
      $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&lt;</a> ';
    }
    $PagesMax = $PageCount - 1;
    $PagesMin = 0;
    if ($PagesMax > ($CurrentPage + $Around)) $PagesMax = $CurrentPage + $Around;
    if ($PagesMin < ($CurrentPage - $Around))
    {
      $Result.= ' ... ';
      $PagesMin = $CurrentPage - $Around;
    }
    for ($i = $PagesMin; $i <= $PagesMax; $i++)
    {
      if ($i == $CurrentPage) $Result.= '<strong>'.($i + 1).'</strong> ';
      else {
       $QueryItems['page'] = $i;
       $Result .= '<a href="?'.SetQueryStringArray($QueryItems).'">'.($i + 1).'</a> ';
      }
    }
    if ($PagesMax < ($PageCount - 1)) $Result .= ' ... ';
    if ($CurrentPage < ($PageCount - 1))
    {
      $QueryItems['page'] = ($CurrentPage + 1);
      $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&gt;</a> ';
      $QueryItems['page'] = ($PageCount - 1);
      $Result.= '<a href="?'.SetQueryStringArray($QueryItems).'">&gt;&gt;</a>';
    }
  }
  $Result = '<div style="text-align: center">'.$Result.'</div>';
  return array('SQLLimit' => ' LIMIT '.$CurrentPage * $ItemPerPage.', '.$ItemPerPage,
    'Page' => $CurrentPage,
    'Output' => $Result,
  );
}

$OrderDirSQL = array('ASC', 'DESC');
$OrderArrowImage = array('sort_asc.png', 'sort_desc.png');

function GetOrderTableHeader(array $Columns, string $DefaultColumn, int $DefaultOrder = 0): array
{
  global $OrderDirSQL, $OrderArrowImage, $System;

  if (array_key_exists('OrderCol', $_GET)) $_SESSION['OrderCol'] = $_GET['OrderCol'];
  if (array_key_exists('OrderDir', $_GET) and (array_key_exists($_GET['OrderDir'], $OrderArrowImage)))
    $_SESSION['OrderDir'] = $_GET['OrderDir'];
  if (!array_key_exists('OrderCol', $_SESSION)) $_SESSION['OrderCol'] = $DefaultColumn;
  if (!array_key_exists('OrderDir', $_SESSION)) $_SESSION['OrderDir'] = $DefaultOrder;

  // Check OrderCol
  $Found = false;
  foreach ($Columns as $Column)
  {
    if ($Column['Name'] == $_SESSION['OrderCol'])
    {
      $Found = true;
      break;
    }
  }
  if ($Found == false)
  {
    $_SESSION['OrderCol'] = $DefaultColumn;
    $_SESSION['OrderDir'] = $DefaultOrder;
  }
  // Check OrderDir
  if (($_SESSION['OrderDir'] != 0) and ($_SESSION['OrderDir'] != 1)) $_SESSION['OrderDir'] = 0;

  $Result = '';
  $QueryItems = GetQueryStringArray($_SERVER['QUERY_STRING']);
  foreach ($Columns as $Index => $Column)
  {
    $QueryItems['OrderCol'] = $Column['Name'];
    $QueryItems['OrderDir'] = 1 - $_SESSION['OrderDir'];
    if ($Column['Name'] == $_SESSION['OrderCol']) $ArrowImage = '<img style="vertical-align: middle; border: 0px;" src="'.
      $System->Link('/images/'.$OrderArrowImage[$_SESSION['OrderDir']]).'" alt="order arrow"/>';
      else $ArrowImage = '';
    if ($Column['Name'] == '') $Result .= '<th>'.$Column['Title'].'</th>';
      else $Result .= '<th><a href="?'.SetQueryStringArray($QueryItems).'">'.$Column['Title'].$ArrowImage.'</a></th>';
  }
  return array(
    'SQL' => ' ORDER BY `'.$_SESSION['OrderCol'].'` '.$OrderDirSQL[$_SESSION['OrderDir']],
    'Output' => '<tr>'.$Result.'</tr>',
    'Column' => $_SESSION['OrderCol'],
    'Direction' => $_SESSION['OrderDir'],
  );
}

function ClientVersionSelection(string $Selected): string
{
  global $System;

  $Output = '<select name="ClientVersion">';
  $DbResult = $System->Database->select('ClientVersion', '`Id`, `Version`', '`Imported` = 1');
  $Output .= '<option value=""';
  if ($Selected == '')
    $Output .= ' selected="selected"';
  $Output .= '>'.T('None').'</option>';
  while ($ClientVersion = $DbResult->fetch_assoc())
  {
    $Output .= '<option value="'.$ClientVersion['Id'].'"';
    if ($Selected == $ClientVersion['Id'])
      $Output .= ' selected="selected"';
    $Output .= '>'.$ClientVersion['Version'].'</option>';
  }
  $Output .= '</select>';
  return $Output;
}

function GetLanguageList(): array
{
  global $System;

  $Result = array();
  $DbResult = $System->Database->query('SELECT * FROM `Language` WHERE `Enabled` = 1');
  while ($DbRow = $DbResult->fetch_assoc())
    $Result[$DbRow['Id']] = $DbRow;
  return $Result;
}

$Moderators = array('Překladatel', 'Moderátor', 'Administrátor');

function HumanDate(?string $SQLDateTime): string
{
  if (($SQLDateTime == NULL) or ($SQLDateTime == '')) return '&nbsp;';
  $DateTimeParts = explode(' ', $SQLDateTime);
  if ($DateTimeParts[0] != '0000-00-00')
  {
    $DateParts = explode('-', $DateTimeParts[0]);
    return ($DateParts[2] * 1).'.'.($DateParts[1] * 1).'.'.($DateParts[0] * 1);
  } else return '&nbsp;';
}

function HumanDateTime(?string $SQLDateTime): string
{
  if (($SQLDateTime == NULL) or ($SQLDateTime == '')) return '&nbsp;';
  $DateTimeParts = explode(' ', $SQLDateTime);
  if ($DateTimeParts[0] != '0000-00-00' and $SQLDateTime <> '')
  {
    $DateParts = explode('-', $DateTimeParts[0]);
    $Output = ($DateParts[2] * 1).'.'.($DateParts[1] * 1).'.'.($DateParts[0] * 1);
  } else $Output = '&nbsp;';
  if (count($DateTimeParts) > 1)
  if ($DateTimeParts[1] != '00:00:00')
  {
    $TimeParts = explode(':', $DateTimeParts[1]);
    $Output .= ' '.($TimeParts[0] * 1).':'.($TimeParts[1] * 1).':'.($TimeParts[2] * 1);
  };
  return $Output;
}

function FollowingTran($TextID, $Table, $GroupId, $Prev = false)
{
  global $System, $Config;

  if ($Prev)
    $sql = 'SELECT `ID` FROM `'.$Table.'` AS `item` WHERE '.
      '(`Language` = '.$Config['OriginalLanguage'].') AND NOT EXISTS(SELECT `entry` '.
      'FROM `'.$Table.'` AS `sub` WHERE (`sub`.`Language` <> '.$Config['OriginalLanguage'].') '.
      'AND (`sub`.`entry` = `item`.`entry`)) AND (`ID` < '.$TextID.') ORDER BY `ID` DESC LIMIT 1';
    else $sql = 'SELECT `ID` FROM `'.$Table.'` AS `item` WHERE '.
      '(`Language` = '.$Config['OriginalLanguage'].') AND NOT EXISTS(SELECT `entry` '.
      'FROM `'.$Table.'` AS `sub` WHERE (`sub`.`Language` <> '.$Config['OriginalLanguage'].') '.
      'AND (`sub`.`entry` = `item`.`entry`)) AND `ID` > '.$TextID.' ORDER BY `ID` LIMIT 1';

  $DbResult = $System->Database->query($sql);
  $Next = $DbResult->fetch_assoc();
  if ($Next)
  {
    if ($Prev) $Output = '<a href="form.php?group='.$GroupId.'&amp;ID='.$Next['ID'].'">Předcházející '.$Next['ID'].'</a> ';
    else $Output = '<a href="form.php?group='.$GroupId.'&amp;ID='.$Next['ID'].'">Následující '.$Next['ID'].'</a> ';
      return 'form.php?group='.$GroupId.'&amp;ID='.$Next['ID'];
  }
}

function GetBuildNumber(string $Version): int
{
  global $System, $BuildNumbers;

  if (!isset($BuildNumbers)) $BuildNumbers = array();
  if (!array_key_exists($Version, $BuildNumbers))
  {
    $DbResult = $System->Database->select('ClientVersion', 'BuildNumber', '`Version` = "'.$Version.'"');
    if ($DbResult->num_rows == 1)
    {
      $DbRow = $DbResult->fetch_assoc();
      $BuildNumbers[$Version] = $DbRow['BuildNumber'];
    } else return 0;
  }
  return $BuildNumbers[$Version];
}

// TODO: Client version build number should not be used in internal references
function GetVersionWOW(int $BuildNumber): string
{
  global $System, $VersionsWOW;

  if (!isset($VersionsWOW)) $VersionsWOW = array();
  if (!array_key_exists($BuildNumber, $VersionsWOW))
  {
    $DbResult = $System->Database->select('ClientVersion', 'Version', '`BuildNumber` = "'.$BuildNumber.'"');
    if ($DbResult->num_rows == 1)
    {
      $Version = $DbResult->fetch_assoc();
      $VersionsWOW[$BuildNumber] = $Version['Version'];
    } else return '';
  }
  return $VersionsWOW[$BuildNumber];
}

// TODO: Client version build number should not be used in internal references
function GetVersionWOWId($BuildNumber)
{
  global $System, $VersionsWOWId;

  if (isset($VersionsWOWId[$BuildNumber]) == false)
  {
    $sql = 'SELECT `Id` FROM `ClientVersion` WHERE `BuildNumber` = "'.$BuildNumber.'"';
    $DbResult = $System->Database->query($sql);
    $Version = $DbResult->fetch_assoc();
    $VersionsWOWId[$BuildNumber] = $Version['Id'];
  }
  return $VersionsWOWId[$BuildNumber];
}

function LoadGroupIdParameter()
{
  global $System;
  $TranslationTree = ModuleTranslation::Cast($System->ModuleManager->GetModule('Translation'))->GetTranslationTree();

  $GroupId = 0;
  if (TryGetUrlParameterInt('group', $GroupId))
  {  
    if (isset($TranslationTree[$GroupId]) == false) ErrorMessage('Překladová skupina dle zadaného Id neexistuje.');
    return $GroupId;
  }
  ErrorMessage('Group not valid.');
}

function LoadCommandLineParameters(): void
{
  if (!array_key_exists('REMOTE_ADDR', $_SERVER) && is_array($_SERVER['argv']))
  {
    foreach ($_SERVER['argv'] as $Parameter)
    {
      if (strpos($Parameter, '=') !== false)
      {
        $Index = substr($Parameter, 0, strpos($Parameter, '='));
        $Parameter = substr($Parameter, strpos($Parameter, '=') + 1);
        //echo($Index.'  ----  '.$Parameter);
        $_GET[$Index] = $Parameter;
      }
    }
  }
}

function ShowTabs(array $Tabs): string
{
  $QueryItems = GetQueryStringArray($_SERVER['QUERY_STRING']);

  if (array_key_exists('Tab', $_GET)) $_SESSION['Tab'] = $_GET['Tab'];
  if (!array_key_exists('Tab', $_SESSION)) $_SESSION['Tab'] = 0;
  if (($_SESSION['Tab'] < 0) or ($_SESSION['Tab'] > (count($Tabs) - 1))) $_SESSION['Tab'] = 0;
  $Output = '<div id="header">'.
    '<ul>';
  foreach ($Tabs as $Index => $Tab)
  {
    $QueryItems['Tab'] = $Index;
    if ($Index == $_SESSION['Tab']) $Selected = ' id="selected"';
      else $Selected = '';
    $Output .= '<li'.$Selected.'><a href="?'.SetQueryStringArray($QueryItems).'">'.$Tab.'</a></li>';
  }
  $Output .= '</ul></div>';
  return $Output;
}

function CheckBox($Name, $Checked = false, $Id = '', $Class = '', $Disabled = false)
{
  if ($Id) $Id = ' id="'.$Id.'"'; else $Id = '';
  if ($Class) $Class = ' class="'.$Class.'"'; else $Class = '';
  if ($Checked) $Checked = ' checked="checked"'; else $Checked = '';
  if ($Disabled) $Disabled = ' disabled="disabled"'; else $Disabled = '';
  return '<input type="checkbox" value="checked" name="'.$Name.'"'.$Checked.$Disabled.$Id.$Class.' />';
}

function RadioButton($Name, $Value, $Checked = false, $OnClick = '', $Disabled = false)
{
  if ($Checked) $Checked = ' checked="checked"'; else $Checked = '';
  if ($OnClick != '') $OnClick = ' onclick="'.$OnClick.'"'; else $OnClick = '';
  if ($Disabled) $Disabled = ' disabled="disabled"'; else $Disabled = '';
  return '<input type="radio" name="'.$Name.'" value="'.$Value.'"'.$Checked.$Disabled.$OnClick.'/>';
}

function SelectOption($Name, $Text, $Selected = false)
{
  if ($Selected) $Selected = ' selected="selected"'; else $Selected = '';
  return '<option value="'.$Name.'"'.$Selected.'>'.$Text.'</option>';
}

function DeleteDirectory(string $dirname): bool
{
  if (is_dir($dirname))
  {
    $dir_handle = opendir($dirname);
    if (!$dir_handle) return false;
    while ($file = readdir($dir_handle))
    {
      if (($file != '.') and ($file != '..'))
      {
        if (!is_dir($dirname.'/'.$file)) unlink($dirname.'/'.$file);
          else DeleteDirectory($dirname.'/'.$file);
      }
    }
    closedir($dir_handle);
    rmdir($dirname);
  }
  return true;
}

function ErrorMessage(string $Text): void
{
  ShowPage($Text);
  die();
}

function GetIDbyName(string $Table)
{
  global $System;

  $TranslationTree = ModuleTranslation::Cast($System->ModuleManager->GetModule('Translation'))->GetTranslationTree();

  foreach ($TranslationTree as $TableID => $Value)
  {
    if ($Value['TablePrefix'] == $Table) return $TableID;
  }
}

function GetTranslatNamesArray(): array
{
  $TablesColumn = array
  (
    'TextGameObject' => 'Name',
    'TextCreature' => 'name',
    'TextTransport' => 'Name',
    'TextAreaTriggerTeleport' => 'Name',
    'TextAreaTriggerTavern' => 'Name',
    'TextArea' => 'Name',
    'TextAreaPOI' => 'Name',
    'TextCharacterClass' => 'Name',
    'TextCharacterRace' => 'Name1',
    'TextItemSubClass' => 'Name',
    'TextItemSubClass' => 'Name2',
    'TextCreatureType' => 'Name',
    'TextItem' => 'Name',
    'Dictionary' => 'Text',
  );
  return $TablesColumn;
}

function GetTranslatNames($Text, $mode, $TablesColumn, $FirstBig = True)
{
  global $System, $Config;

 /* $TablesID = array('gameobject' => 5,
  'creature' => 6,
  'item' => 4,
  'transports' => 'Name',
  'areatrigger_teleport' => 'Name',
  'areatrigger_tavern' => 'Name',); */
  $buff = array();

  // change chars by we want to separate
  $Text = str_replace('$B$B', ' ', $Text);
  $Text = str_replace('$b$b', ' ', $Text);
  $Text = str_replace('$G', ' ', $Text);
  $Text = str_replace('$I', ' ', $Text);
  $Text = str_replace('$N', ' ', $Text);
  $Text = str_replace('$R', ' ', $Text);
  $Text = str_replace('$g', ' ', $Text);
  $Text = str_replace('$i', ' ', $Text);
  $Text = str_replace('$n', ' ', $Text);
  $Text = str_replace('$r', ' ', $Text);
  $Text = str_replace(':', ' ', $Text);
  $Text = str_replace(';', ' ', $Text);
  $Text = str_replace('!', ' ', $Text);
  $Text = str_replace('?', ' ', $Text);
  $Text = str_replace('.', ' ', $Text);
  $Text = str_replace(',', ' ', $Text);
  $Text = str_replace('\'s', ' ', $Text);
  $Text = str_replace('<', ' ', $Text);
  $Text = str_replace('>', ' ', $Text);
  $ArrStr = explode(' ', $Text);
  $sqlall = '';

  foreach ($TablesColumn as $Table => $Column)
  {
    $orderinby = ' ORDER BY ID DESC ';
    $sql = 'SELECT `ID`, (SELECT CONCAT(\''.GetIDbyName($Table).'\' )) AS `GroupId`,`'.
      $Column.'` AS Orig, (SELECT `'.$Column.'` FROM `'.$Table.'` AS `T` WHERE '.
      '(`O`.`Entry` = `T`.`Entry`) AND (`Language` <> '.$Config['OriginalLanguage'].') '.
      $orderinby.' LIMIT 1) AS `Tran` FROM `'.$Table.'` AS `O` WHERE ';
    $groupby = ' GROUP BY `'.$Column.'` ';

    $where = '(`Language` = '.$Config['OriginalLanguage'].') ';
    if ($mode == 1)
    {
      $where .= ' AND EXISTS(SELECT 1 FROM `'.$Table.
      '` AS `Sub` WHERE (`Sub`.`Language` <> '.$Config['OriginalLanguage'].
      ') AND (`Sub`.`Entry` = `O`.`Entry`))';
    }
    if ($mode == 2)
    {
      $where .= ' AND NOT EXISTS(SELECT 1 FROM `'.$Table.
      '` AS `Sub` WHERE (`Sub`.`Language` <> '.$Config['OriginalLanguage'].
      ') AND (`Sub`.`Entry` = `O`.`Entry`))';
    }
    $where .= ' AND (';
    if (array_search('the', $ArrStr))
    {
      $where .= '(`O`.`'.$Column.'` LIKE "The %") OR ';
    }

    $SqlOK = false;
    if (count($ArrStr) > 0)
    {
      for ($i = 0; $i < count($ArrStr); $i++)
      {
        // find word only if is 3 characters and more, and if starts by upper char, and search only once
        if ((strlen($ArrStr[$i]) > 3) or ( ((count($ArrStr) < 6) or ($i == 0)) and (strlen($ArrStr[$i]) > 0) ) )  //length
          if ((!$FirstBig) or (ctype_upper(substr($ArrStr[$i], 0, 1))) or (count($ArrStr) < 6) )  //first big
            if (array_search($ArrStr[$i], $ArrStr) == $i)
            {  // first in array
              $where .= '(`O`.`'.$Column.'` LIKE "'.addslashes($ArrStr[$i]).'%") OR ';
              $SqlOK = true;
            }
      }
      $where = substr($where, 0, strlen($where) - 4);
      $where .= ')';
    }
    if ($SqlOK)
    {
        //$sql.$where.$groupby.$orderby
//          $buff[] = array($Line['ID'], GetIDbyName($Table), $Line['Orig'], $Line['Tran']);
      if ($sqlall <> '')
      {
        $sqlall .= ' UNION ALL ( '.$sql.$where.$groupby.' )';
      } else {
        $sqlall .= ' ( '.$sql.$where.$groupby.' )';
      }
    }
  }
  if ($SqlOK)
  {
    $orderby = ' ORDER BY LENGTH(Orig) DESC ';
    // echo $sqlall. $orderby;
    $DbResult = $System->Database->query($sqlall.$orderby);
    //  echo ($sql.'|'.$where.'|'.$groupby);
    while ($Line = $DbResult->fetch_assoc())
    {
      $buff[] = array($Line['ID'], $Line['GroupId'], $Line['Orig'], $Line['Tran']);
    }
  }
  return $buff;
}

function ProgressBar($Width, $Percent, $Text = '')
{
  $Pixels = $Width * ($Percent / 100);
  if ($Pixels > $Width) $Pixels = $Width;
  if ($Text == '') $Text = $Percent;

  return '<div class="progressbar" style="width: '.$Width.'px">'.
    '<div class="bar" style="width: '.$Pixels.'px;"></div>'.
    '<div class="text" style="width: '.$Width.'px">'.$Text.'</div>'.
    '</div>';
}

function GetLevelMinMax(int $XP): array
{
  $IndexLevel = 100;

  if ($XP > 0) $Level = floor(sqrt($XP / $IndexLevel));
    else $Level = 0;
  $MinXP = $Level * $Level * $IndexLevel;
  $MaxXP = ($Level + 1) * ($Level + 1) * $IndexLevel;
  $MaxXP = $MaxXP - $MinXP;
  $XP = $XP - $MinXP;
  return array('Level' => $Level, 'XP' => $XP, 'MaxXP' => $MaxXP);
}

function GetParameter(string $Name, string $Default = '', bool $Numeric = false, bool $Session = false): string
{
  $Result = $Default;
  if (array_key_exists($Name, $_GET)) $Result = $_GET[$Name];
  else if (array_key_exists($Name, $_POST)) $Result = $_POST[$Name];
  else if ($Session and array_key_exists($Name, $_SESSION)) $Result = $_SESSION[$Name];
  if ($Numeric and !is_numeric($Result)) $Result = $Default;
  if ($Session) $_SESSION[$Name] = $Result;
  return $Result;
}

function MakeActiveLinks(string $Content): string
{
  $Content = htmlspecialchars($Content);
  $Content = str_replace("\n", ' <br/>', $Content);
  $Content = str_replace("\r", '', $Content);

  $Result = '';
  $I = 0;
  while ((strpos($Content, 'http://') !== false) or (strpos($Content, 'https://') !== false))
  {
    if (strpos($Content, 'http://') !== false) $I = strpos($Content, 'http://');
    if (strpos($Content, 'https://') !== false) $I = strpos($Content, 'https://');
    $Result .= substr($Content, 0, $I);
    $Content = substr($Content, $I);
    $SpacePos = strpos($Content, ' ');
    if ($SpacePos !== false) $URL = substr($Content, 0, strpos($Content, ' '));
      else $URL = substr($Content, 0);

    $Result .= '<a href="'.$URL.'">'.$URL.'</a>';
    $Content = substr($Content, strlen($URL));
  }
  $Result .= $Content;
  return $Result;
}

define('MESSAGE_WARNING', 0);
define('MESSAGE_CRITICAL', 1);
define('MESSAGE_INFORMATION', 2);

function ShowMessage(string $Text, int $Type = MESSAGE_INFORMATION)
{
  global $System;

  $IconName = array(
    MESSAGE_INFORMATION => 'information',
    MESSAGE_WARNING => 'warning',
    MESSAGE_CRITICAL => 'critical'
  );
  $BackgroundColor = array(
    MESSAGE_INFORMATION => '#e0e0ff',
    MESSAGE_WARNING => '#ffffe0',
    MESSAGE_CRITICAL => '#ffe0e0'
  );

  return '<div class="message" style="background-color: '.$BackgroundColor[$Type].
    ';"><table><tr><td class="icon"><img src="'.
    $System->Link('/images/message/'.$IconName[$Type].'.png').'" alt="'.
    $IconName[$Type].'"><td>'.$Text.'</td></tr></table></div>';
}

function ProcessURL(): array
{
  if (array_key_exists('REDIRECT_QUERY_STRING', $_SERVER))
    $PathString = $_SERVER['REDIRECT_QUERY_STRING'];
  else $PathString = '';
  if (substr($PathString, -1, 1) == '/') $PathString = substr($PathString, 0, -1);
  $PathItems = explode('/', $PathString);
  if (strpos(GetRequestURI(), '?') !== false)
    $_SERVER['QUERY_STRING'] = substr(GetRequestURI(), strpos(GetRequestURI(), '?') + 1);
  else $_SERVER['QUERY_STRING'] = '';
  parse_str($_SERVER['QUERY_STRING'], $_GET);
  // SQL injection hack protection
  foreach ($_GET as $Index => $Item) $_GET[$Index] = addslashes($_GET[$Index]);
  return $PathItems;
}

function WriteLanguages($Selected)
{
  global $System;

  $Output = '<select name="Language">';
  $DbResult = $System->Database->select('Language', '`Id`, `Name`', '`Enabled` = 1');
  while ($Language = $DbResult->fetch_assoc())
  {
    $Output .= '<option value="'.$Language['Id'].'"';
    if ($Selected == $Language['Id'])
      $Output .= ' selected="selected"';
    $Output .= '>'.T($Language['Name']).'</option>';
  }
  $Output .= '</select>';
  return $Output;
}

function GetClientProxyAddresses()
{
  if (array_key_exists('HTTP_X_FORWARDED_FOR',$_SERVER)) $IP = $_SERVER['HTTP_X_FORWARDED_FOR'];
    else $IP = array();
}

function GetRemoteAddress()
{
  if (array_key_exists('REMOTE_ADDR', $_SERVER)) $IP = $_SERVER['REMOTE_ADDR'];
    else $IP = '';
  return $IP;
}

function GetRequestURI()
{
  if (array_key_exists('REQUEST_URI', $_SERVER)) return $_SERVER['REQUEST_URI'];
    else return $_SERVER['PHP_SELF'];
}

function ShowBBcodes($text)
{
    // NOTE : I had to update this sample code with below line to prevent obvious attacks as pointed out by many users.
    // Always ensure that user inputs are scanned and filtered properly.
    $text  = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');

    // BBcode array
    $find = array(
      '~\[b\](.*?)\[/b\]~s',
      '~\[i\](.*?)\[/i\]~s',
      '~\[u\](.*?)\[/u\]~s',
      '~\[quote\](.*?)\[/quote\]~s',
      '~\[size=(.*?)\](.*?)\[/size\]~s',
      '~\[color=(.*?)\](.*?)\[/color\]~s',
      '~\[url\]((?:ftp|https?)://.*?)\[/url\]~s',
      '~\[img\](https?://.*?\.(?:jpg|jpeg|gif|png|bmp))\[/img\]~s'
    );

    // HTML tags to replace BBcode
    $replace = array(
      '<b>$1</b>',
      '<i>$1</i>',
      '<span style="text-decoration:underline;">$1</span>',
      '<pre>$1</'.'pre>',
      '<span style="font-size:$1px;">$2</span>',
      '<span style="color:$1;">$2</span>',
      '<a href="$1">$1</a>',
      '<img src="$1" alt="" />'
    );

    // Replacing the BBcodes with corresponding HTML tags
    return preg_replace($find, $replace, $text);
}

function NormalizePath(string $Path): string
{
  $Segments = explode('/', $Path);
  $Result = array();
  for ($I = 0; $I < count($Segments); $I++)
  {
    $Segment = $Segments[$I];
    if (($Segment == '.') || ((strlen($Segment) == 0) && ($I > 0) && ($I < count($Segments) - 1))) 
    {
      continue;
    }
    if ($Segment == '..') 
    {
      array_pop($Result);
    } else 
    {
      array_push($Result, $Segment);
    }
  }
  return implode('/', $Result);
}

function TryGetUrlParameterInt(string $Name, int &$Value): bool
{
  if (array_key_exists($Name, $_GET))
  {
    if (is_numeric($_GET[$Name]))
    {
      $Value = $_GET[$Name] * 1;
      return true;
    }
    return false;
  }
  return false;
}