<?php

class Core extends System
{
  public array $Config;
  public array $PathItems;
  public array $Menu;
  public bool $DoNotShowPage;
  public array $OnPageNotFound;
  public LocaleManager $LocaleManager;
  public array $Bars;
  public array $PageHeaders;
  public string $BaseURL;
  public array $RSSChannels;
  public BaseView $BaseView;
  public array $LinkLocaleExceptions;

  function __construct()
  {
    parent::__construct();
    $this->Config = array();
    $this->Menu = array();
    $this->RSSChannels = array();
    $this->DoNotShowPage = false;
    $this->OnPageNotFound = array();
    $this->Pages = array();
    $this->Bars = array();
    $this->PageHeaders = array();
    $this->BaseURL = '';
  }

  function Init()
  {
    global $GlobalLocaleManager, $Config;

    $this->Config = $Config;
    if (isset($this->Config['Web']['Timezone']))
      date_default_timezone_set($this->Config['Web']['Timezone']);
    mb_internal_encoding("UTF-8");

    if (isset($this->Config['Database']))
    {
      $this->Database->Connect($this->Config['Database']['Host'],
        $this->Config['Database']['User'], $this->Config['Database']['Password'],
        $this->Config['Database']['Database']);
      $this->Database->charset($this->Config['Database']['Charset']);
      $this->Database->ShowSQLQuery = $this->Config['Web']['ShowSQLQuery'];
      $this->Database->ShowSQLError = $this->Config['Web']['ShowSQLError'];
      $this->Database->LogSQLQuery = $this->Config['Web']['LogSQLQuery'];
    }

    $this->LocaleManager = new LocaleManager($this);
    $this->LocaleManager->Dir = dirname(dirname(__FILE__)).'/locale';
    $this->LocaleManager->Available = array(
      'cs' => array('Code' => 'cs', 'Title' => 'Česky'),
      'en' => array('Code' => 'en', 'Title' => 'English'),
    );
    $GlobalLocaleManager = $this->LocaleManager;
    $this->LinkLocaleExceptions = array('images', 'style', 'tmp', 'files', 'aowow',
      'banners'
    );

    if (GetRemoteAddress() != '')
    {
      $this->BaseURL = $_SERVER["CONTEXT_PREFIX"];
      if (substr($this->BaseURL, -1, 1) == '/') $this->BaseURL = substr($this->BaseURL, 0, -1);
    }
    $this->PathItems = ProcessURL();

    // Detect interface locale
    if (isset($this->Config['Web']['Locale']))
      $this->LocaleManager->DefaultLangCode = $this->Config['Web']['Locale'];
    $this->LocaleManager->LangCode = $this->LocaleManager->DefaultLangCode;
    if (count($this->PathItems) > 0)
    {
      $NewLangCode = $this->PathItems[0];
      if (array_key_exists($NewLangCode, $this->LocaleManager->Available)) {
        array_shift($this->PathItems);
        $this->LocaleManager->LangCode = $NewLangCode;
      }
    }
    if (array_key_exists($this->LocaleManager->LangCode, $this->LocaleManager->Available))
      $this->LocaleManager->LoadLocale($this->LocaleManager->LangCode);

    $this->Menu = array
    (
      /*     array(
       'Title' => T('Files'),
       'Hint' => 'Stahování různých pomocných souborů a programů',
       'Link' => $this->Link('/download.php'),
       'Permission' => LICENCE_ANONYMOUS,
       'Icon' => '',
      ),

      array(
        'Title' => T('IRC chat'),
        'Hint' => 'IRC chat pro překladatele',
        'Link' => 'http://embed.mibbit.com/?server=game.zdechov.net%3A6667&amp;channel=%23wowpreklad&amp;forcePrompt=true&amp;charset=utf-8',
        'Permission' => LICENCE_ANONYMOUS,
        'Icon' => '',
      ),
      */
    );
  }

  static function Cast(System $System): Core
  {
    if ($System instanceof Core)
    {
      return $System;
    }
    throw new Exception('Expected Core type but '.gettype($System));
  }

  function Run(): void
  {
    global $ScriptStartTime, $StopAfterUpdateManager,
      $UpdateManager;

    $ScriptStartTime = GetMicrotime();
    //if (GetRemoteAddress() != '')
      session_start();

    // SQL injection hack protection
    foreach ($_POST as $Index => $Item)
    {
      if (is_array($_POST[$Index]))
        foreach ($_POST[$Index] as $Index2 => $Item2) $_POST[$Index][$Index2] = addslashes($Item2);
      else $_POST[$Index] = addslashes($_POST[$Index]);
    }
    foreach ($_GET as $Index => $Item) $_GET[$Index] = addslashes($_GET[$Index]);

    $this->RegisterPageBar('Top');
    $this->RegisterPageBar('Left');
    $this->RegisterPageBar('Right');
    $this->Init();

    $this->StartModules();

    $this->BaseView = new BaseView($this);
    if ($this->DoNotShowPage == false)
    {
      $this->ShowPage();
    }
  }

  function StartModules(): void
  {
    $ModuleSetup = $this->ModuleManager->LoadModule(dirname(__FILE__).'/../Packages/Common/Modules/Setup.php');
    ModuleSetup::Cast($ModuleSetup)->UpdateManager->VersionTable = 'DbVersion';
    $ModuleSetup->Install();
    $ModuleSetup->Start();
    $this->ModuleManager->LoadModules();
    $this->ModuleManager->LoadModule(dirname(__FILE__).'/../Packages/Common/Modules/ModuleManager.php');
    if (file_exists($this->ModuleManager->FileName)) $this->ModuleManager->LoadState();
    if (ModuleSetup::Cast($ModuleSetup)->CheckState())
    {
      $this->ModuleManager->StartAll(array(ModuleCondition::Enabled));
    }
  }

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

  function Link(string $Target): string
  {
    if (substr($Target, 0, strlen($this->BaseURL)) == $this->BaseURL)
      $Remaining = substr($Target, strlen($this->BaseURL));
      else $Remaining = $Target;
    $TargetParts = explode('/', $Remaining);
    if ((count($TargetParts) > 0) and ($TargetParts[0] == ''))
      array_splice($TargetParts, 0, 1);
    if (count($TargetParts) > 0)
    {
      if (in_array($TargetParts[0], $this->LinkLocaleExceptions))
      {
        $Result = $this->BaseURL.$Target;
      } else $Result = $this->LinkLocale($Target);
    } else $Result = $this->LinkLocale($Target);
    return $Result;
  }

  function TranslateURL($URL, $Locale)
  {
    // Try translate URL directory parts from current locale to target locale
    $Remaining = $URL;
    $RemainingParts = explode('?', $Remaining);
    $Directory = $RemainingParts[0];
    if (count($RemainingParts) > 1)
    {
      $Params = $RemainingParts[1];
    } else {
      $Params = '';
    }
    $TargetLocaleManager = new LocaleManager($this);
    $TargetLocaleManager->Dir = $this->LocaleManager->Dir;
    $TargetLocaleManager->Available = $this->LocaleManager->Available;
    $TargetLocaleManager->LoadLocale($Locale);

    $DirectoryParts = explode('/', $Directory);
    foreach ($DirectoryParts as $Index => $Item)
    {
      $NewText = $TargetLocaleManager->CurrentLocale->Texts->Translate($Item, 'URL');
      $DirectoryParts[$Index] = $NewText;
    }
    $Directory = implode('/', $DirectoryParts);
    $Remaining = $Directory;
    if ($Params != '') $Remaining .= '?'.$Params;

    return $Remaining;
  }

  function TranslateReverseURL($URL, $Locale)
  {
    // Try translate URL directory parts from current locale to target locale
    $Remaining = $URL;
    $RemainingParts = explode('?', $Remaining);
    $Directory = $RemainingParts[0];
    if (count($RemainingParts) > 1)
    {
      $Params = $RemainingParts[1];
    } else {
      $Params = '';
    }
    $TargetLocaleManager = new LocaleManager($this);
    $TargetLocaleManager->Dir = $this->LocaleManager->Dir;
    $TargetLocaleManager->Available = $this->LocaleManager->Available;
    $TargetLocaleManager->LoadLocale($Locale);

    $DirectoryParts = explode('/', $Directory);
    foreach ($DirectoryParts as $Index => $Item)
    {
      $NewText = $TargetLocaleManager->CurrentLocale->Texts->TranslateReverse($Item, 'URL');
      $DirectoryParts[$Index] = $NewText;
    }
    $Directory = implode('/', $DirectoryParts);
    $Remaining = $Directory;
    if ($Params != '') $Remaining .= '?'.$Params;

    return $Remaining;
  }

  function LinkLocale($Target, $Locale = '')
  {
    if ($Locale == '') $Locale = $this->LocaleManager->LangCode;

    $Target = $this->TranslateURL($Target, $Locale);

    if ($Locale == $this->LocaleManager->DefaultLangCode)
      return $this->BaseURL.$Target;
    return $this->BaseURL.'/'.$Locale.$Target;
  }

  function RegisterMenuItem($MenuItem, $Pos = NULL)
  {
    if (is_null($Pos)) $this->Menu[] = $MenuItem;
      else {
        array_splice($this->Menu, $Pos, 0, array($MenuItem));
      }
  }

  function SearchPage($PathItems, $Pages)
  {
    if (count($PathItems) == 0) $PathItems = array('');
    $PathItem = $PathItems[0];
    $PathItem = $this->LocaleManager->CurrentLocale->Texts->TranslateReverse($PathItem, 'URL');

    if (array_key_exists($PathItem, $Pages))
    {
      if (is_array($Pages[$PathItem]))
      {
        array_shift($PathItems);
        return $this->SearchPage($PathItems, $Pages[$PathItem]);
      } else
      {
        if (count($PathItems) == 1) return $Pages[$PathItem];
          else return ''; // Unexpected subpages
      }
    } else return '';
  }

  function PageNotFound()
  {
    // Send correct HTTP status code to signal unknown page
    if (array_key_exists('SERVER_PROTOCOL', $_SERVER))
      Header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
    if (array_key_exists('HTTP_REFERER', $_SERVER)) $Referer = ' Referer: '.$_SERVER['HTTP_REFERER'];
      else $Referer = '';
    if (isset($this->ModuleManager->Modules['Log']))
      $this->ModuleManager->Modules['Log']->WriteLog('Stránka "'.
        implode('/', $this->PathItems).'" nenalezena'.$Referer, LOG_TYPE_PAGE_NOT_FOUND);
    return ShowMessage(sprintf(T('Page "%s" not found'), implode('/', $this->PathItems)), MESSAGE_CRITICAL);
  }

  function ShowPage()
  {
    $Output = '';

    /* @var $Page Page */
    $ClassName = $this->SearchPage($this->PathItems, $this->Pages);
    if (($ClassName != '') and (class_exists($ClassName)))
    {
      $Page = new $ClassName($this);
      $Output = $Page->GetOutput();
      $this->BaseView->Title = $Page->Title;
      if ($Page->RawPage == false) $Output = $this->BaseView->ShowPage($Output);
    } else {
      $Output2 = '';
      if ((count($this->OnPageNotFound) == 2)
        and method_exists($this->OnPageNotFound[0], $this->OnPageNotFound[1]))
        $Output2 = call_user_func_array($this->OnPageNotFound, array());
     if ($Output2 != '') $Output .= $this->BaseView->ShowPage($Output2);
      else {
        $Output = $this->PageNotFound();
        $this->BaseView->Title = T('Page not found');
        $Output = $this->BaseView->ShowPage($Output);
      }
    }
    echo($Output);
  }

  function RegisterPageBar($Name)
  {
    $this->Bars[$Name] = array();
  }

  function UnregisterPageBar($Name)
  {
    unset($this->Bars[$Name]);
  }

  function RegisterPageBarItem($BarName, $ItemName, $Callback)
  {
    $this->Bars[$BarName][$ItemName] = $Callback;
  }

  function RegisterPageHeader($Name, $Callback)
  {
    $this->PageHeaders[$Name] = $Callback;
  }

  function HumanDate($Time)
  {
    return date('j.n.Y', $Time);
  }
}
