<?php

include_once('Database.php');
include_once('Config.php');
include_once('Global.php');
include_once('Run.php');
include_once('Packages/Common/Base.php');
include_once('Packages/Common/Locale.php');
include_once('Packages/Common/PrefixMultiplier.php');

session_start();

class Application extends System
{
  var $NoFullPage = false;

  function Run()
  {
  }
}

class MyApplication extends Application
{
  var $Config;
  var $LeaderboardURL;
  var $LocaleManager;
  var $BaseURL;
  var $LinkLocaleExceptions;
  var $Title;
  var $LapLength;
  var $MoneyKm;
  var $MaxRunnerSpeed;
  var $MinRunnerSpeed;

  function __construct()
  {
    //$this->LeaderboardURL = 'https://registrace.teribear.cz/Leaderboard';
    $this->LeaderboardURL = 'https://leaderboard.teribear.cz/';
    $this->BaseURL = '';
    $this->LinkLocaleExceptions = array();
    $this->Title = '';
    $this->LapLength = 0; // km
    $this->MoneyKm = 0; // Kč
    $this->MaxRunnerSpeed = 20; // km/hour
    $this->MinRunnerSpeed = 2; // km/hour
  }

  function GetLatestYear()
  {
    $Year = 0;
    $DbResult = $this->Database->query('SELECT DISTINCT(Year) AS Year FROM `Runner` ORDER BY Year DESC');
    if ($DbResult->num_rows > 0)
    {
      $DbRow = $DbResult->fetch_assoc();
      $Year = $DbRow['Year'];
    }
    return $Year;
  }

  function YearList($Path, $SelectedYear, $Table = 'Runner', $Where = '1')
  {
    $Output = T('Year').': ';
    $DbResult = $this->Database->query('SELECT DISTINCT(Year) AS Year FROM `'.$Table.'` WHERE '.$Where.' ORDER BY Year ASC');
    while ($DbRow = $DbResult->fetch_assoc())
    {
      $Year = $DbRow['Year'];
      $Item = '<a href="'.$this->Link($Path.$Year.'/').'">'.$Year.'</a>';
      if ($SelectedYear == $Year) $Item = '<strong>'.$Item.'</strong>';
      $Output .= $Item.' ';
    }
    return $Output;
  }

  function ItemsYearList($Path, $SelectedId, $Table = 'Runner', $Where = '1')
  {
    $Output = T('Year').': ';
    $DbResult = $this->Database->query('SELECT T1.Id AS Id, T2.Year AS Year FROM (SELECT DISTINCT(Id) AS Id FROM `'.$Table.'` WHERE '.$Where.' ORDER BY Year ASC) AS T1 '.
      'LEFT JOIN '.$Table.' AS T2 ON T1.Id=T2.Id');
    while ($DbRow = $DbResult->fetch_assoc())
    {
      $Item = '<a href="'.$this->Link($Path.$DbRow['Id'].'/').'">'.$DbRow['Year'].'</a>';
      if ($SelectedId == $DbRow['Id']) $Item = '<strong>'.$Item.'</strong>';
      $Output .= $Item.' ';
    }
    return $Output;
  }

  function ShowMenu()
  {
    $Output = '<div>'.
      '<a href="'.$this->Link('/').'">'.T('Summary').'</a> '.
      '<a href="'.$this->Link('/runners/').'">'.T('Runners').'</a> '.
      '<a href="'.$this->Link('/teams/').'">'.T('Teams').'</a> '.
      '<a href="'.$this->Link('/families/').'">'.T('Families').'</a> '.
      $this->ShowLocaleSelector().
      '</div>';
    return $Output;
  }

  function ShowLocaleSelector()
  {
    //$Output .= ' <form action="?setlocale" method="get">';
    $Output = ' <select onchange="window.location=this.value">';
    foreach ($this->LocaleManager->Available as $Locale)
    {
      $Remaining = substr($_SERVER["REQUEST_URI"], strlen($this->BaseURL));
      if (substr($Remaining, 1, strlen($Locale['Code'].'/')) == $this->LocaleManager->LangCode.'/')
        $Remaining = substr($Remaining, strlen('/'.$Locale['Code']));
      if ($Locale['Code'] == $this->LocaleManager->CurrentLocale->Texts->Code) $Selected = ' selected="selected"';
        else $Selected = '';
      $Remaining = $this->TranslateReverseURL($Remaining, $this->LocaleManager->LangCode);

      $URL = $this->LinkLocale($Remaining, $Locale['Code']);
      $Output .= '<option title="" value="'.$URL.'"'.$Selected.' onchange="this.form.submit()">'.$Locale['Title'].'</option>';
    }
    $Output .= '</select><noscript><span><input type="submit" value="Submit"/></span></noscript>';

    return $Output;
  }

  function ProcessURL()
  {
    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 (array_key_exists('REQUEST_URI', $_SERVER) and (strpos($_SERVER['REQUEST_URI'], '?') !== false))
      $_SERVER['QUERY_STRING'] = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], '?') + 1);
    else $_SERVER['QUERY_STRING'] = '';
    parse_str($_SERVER['QUERY_STRING'], $_GET);
    return $PathItems;
  }

  // Query: text from name
  // Page: index of page, one page is 30 items
  // Category: '', all, men, women, kids, families, teams
  // TeamId: id of team
  // Count: number of items per page, default 30
  function QueryRunners($Category, $Page = 0, $TeamId = null, $Query = null, $StartNumber = null, $Count = null)
  {
      $URL = $this->LeaderboardURL.'/Home/GetRunners?category='.$Category;
      if ($Page != 0) $URL .= '&page='.$Page;
      if ($TeamId != null) $URL .= '&teamId='.$TeamId;
      if ($Count != null) $URL .= '&count='.$Count;
      if ($Query != null) $URL .= '&query='.$Query;
      if ($StartNumber != null) $URL .= '&startNumber='.$StartNumber;
      $Content = file_get_contents($URL);
      if (($Content !== false) and !empty($Content))
      {
        $JSON = json_decode($Content, true);
      } else
      {
        $JSON = null;
        echo('Cannot load data from remote server ('.$URL.').<br/>'."\n");
      }
      return $JSON;
  }

  function QueryRunnersAll($Category = 'all', $Count = 0)
  {
    $ItemsPerPage = 30;
    $MaxCount = 250 * 30;
    if ($Count > 0) $MaxCount = $Count;
    $Result = array();
    $I = 0;
    while (true)
    {
      $Page = $I * floor($MaxCount / $ItemsPerPage);
      $JSON = $this->QueryRunners($Category, $Page, null, null, null, $MaxCount);
      if ($JSON != null)
      {
        foreach ($JSON['items'] as $Item)
        {
          // Use RegistrationNumber as ChipNumber.
          $Item['ChipNumber'] = $Item['RegistrationNumber'] * 1;
          unset($Item['RegistrationNumber']);
          if ($Item['Name'] != null)
          {
            // Normalize names
            $Item['Name'] = trim($Item['Name']);
            while (strpos($Item['Name'], '  ') !== false) 
            {
              $Item['Name'] = str_replace('  ', ' ', $Item['Name']);
            }
          }
          $Result[] = $Item;
        }
        if (($JSON['last'] == 'true') or (count($JSON) == 0)) break;
        if ($I > 30) break; // Safe limit if last would not work
        $I++;
      } else break;
    }
    return $Result;
  }

  function ShowEmpty()
  {
    $Output = '';
    
    $this->Database->query('DELETE FROM RunnerStat');
    $this->Database->query('DELETE FROM Runner');
    $this->Database->query('DELETE FROM TeamStat');
    $this->Database->query('DELETE FROM Team');
    $this->Database->query('DELETE FROM Import');
    
    // Find duplicates
    /*
    // ALTER TABLE `RunnerStat` ADD `Prev` INT NULL AFTER `Money`;
    // UPDATE RunnerStat AS A SET Prev = (SELECT Id FROM RunnerStat AS B WHERE A.Runner = B.Runner AND B.Id < A.Id ORDER BY Id DESC LIMIT 1)
    // SELECT * FROM `RunnerStat` AS A LEFT JOIN RunnerStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance

    $Table = 'Runner';
    $TableStat = $Table.'Stat';
    //$Queries = array();
    $Output .= 'DELETE FROM RunnerStat WHERE Id IN (';
    $DbResult = $this->Database->query('SELECT A.Id FROM `RunnerStat` AS A LEFT JOIN RunnerStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance');
    while ($Item = $DbResult->fetch_assoc())
    {
      $Output .= $Item[Id].', ';
    }
    $Output .= ');<br/>';

    */

  /*
    // ALTER TABLE `TeamStat` ADD `Prev` INT NULL AFTER `Money`;
    // UPDATE TeamStat AS A SET Prev = (SELECT Id FROM TeamStat AS B WHERE A.Team = B.Team AND B.Id < A.Id ORDER BY Id DESC LIMIT 1)
    // SELECT * FROM `TeamStat` AS A LEFT JOIN TeamStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance

    $Table = 'Team';
    $TableStat = $Table.'Stat';
    //$Queries = array();
    $Output .= 'DELETE FROM TeamStat WHERE Id IN (';
    $DbResult = $this->Database->query('SELECT A.Id FROM `TeamStat` AS A LEFT JOIN TeamStat AS B ON B.ID=A.Prev WHERE A.Distance=B.Distance');
    while ($Item = $DbResult->fetch_assoc())
    {
      $Output .= $Item[Id].', ';
    }
    $Output .= ');<br/>';
    */
/*
    $I = 0;
    $DbResult2 = $this->Database->query('SELECT * FROM Runner WHERE Year=2020');
    while ($Runner = $DbResult2->fetch_assoc())
    {
      $Output .= $Runner['Id'].' '.$Runner['Name'].'<br/>';
      $Where = '('.$TableStat.'.Runner='.$Runner['Id'].')';
      $DbResult = $this->Database->query('SELECT *'.
        ', (SELECT '.$TableStat.'.Distance - B.Distance FROM '.$TableStat.' AS B WHERE (B.Id < '.$TableStat.'.Id) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Id DESC LIMIT 1) AS Length'.
        ' FROM '.$TableStat.
        ' WHERE '.$Where);
      while ($Item = $DbResult->fetch_assoc())
      {
        if (($Item['Length'] != null) and ($Item['Length'] == 0))
        {
          $Output .= ' '.$Item['Id'].' '.$Item['Time'].' '.$Item['Distance'].' '.$Item['Length'].'<br/>';
          $Queries[] = 'DELETE FROM RunnerStat WHERE Id='.$Item[Id];
        }
      }
      flush();
      $I++;
      if ($I > 500) break;
    }
    echo('START TRANSACTION;<br/>');
    foreach ($Queries as $Query)
    {
      echo($Query.";<br/>");
    }
    echo('COMMIT;<br/>');
*/
    return $Output;
  }

  function Sync($Items, $Time)
  {
    $Year = date("Y", $Time);

    // Load all runners
    $DbResult = $this->Database->query('SELECT MAX(Id) AS Id FROM Runner');
    $DbRow = $DbResult->fetch_assoc();
    $NextRunnerId = $DbRow['Id'] + 1;

    $Runners = array();
    $DbResult = $this->Database->query('SELECT Runner.Id, Runner.ChipNumber, Runner.Team, '.
      '(SELECT RunnerStat.Distance FROM RunnerStat WHERE (RunnerStat.Runner = Runner.Id) ORDER BY RunnerStat.Id DESC LIMIT 1) AS Distance '.
      'FROM Runner WHERE Year='.$Year);
    while ($DbRow = $DbResult->fetch_assoc())
    {
      $Runners[$DbRow['ChipNumber']] = $DbRow;
    }

    // Load all teams
    $DbResult = $this->Database->query('SELECT MAX(Id) AS Id FROM Team');
    $DbRow = $DbResult->fetch_assoc();
    $NextTeamId = $DbRow['Id'] + 1;

    $Teams = array();
    $DbResult = $this->Database->query('SELECT Team.Id, Team.WebId, Team.Name, '.
      '(SELECT TeamStat.Distance FROM TeamStat WHERE (TeamStat.Team = Team.Id) ORDER BY TeamStat.Id DESC LIMIT 1) AS Distance '.
      'FROM Team WHERE Year='.$Year);
    while ($DbRow = $DbResult->fetch_assoc())
    {
      $Teams[$DbRow['WebId']] = $DbRow;
    }

    $Queries = array();
    foreach ($Items as $Item)
    {
      if (($Item['Type'] == 'child') or ($Item['Type'] == 'woman') or ($Item['Type'] == 'man'))
      {
        if (!array_key_exists($Item['ChipNumber'], $Runners))
        {
          if ($Item['TeamId'] == null)
          {
            $TeamId = null;
          } else
          {
            if (!array_key_exists($Item['TeamId'], $Teams))
            {
              $TeamId = $NextTeamId;
              $Queries[] = $this->Database->GetInsert('Team', array(
                'Id' => $TeamId,
                'Name' => '',
                'WebId' => $Item['TeamId'],
                'Year' => $Year,
                'IsFamily' => 0,
              ));
              $Teams[$Item['TeamId']] = array('Id' => $TeamId, 'Distance' => -1);
              $NextTeamId++;
            } else
              $TeamId = $Teams[$Item['TeamId']]['Id'];
          }

          $Gender = 0;
          if ($Item['Type'] == 'man') $Gender = 1;
          if ($Item['Type'] == 'woman') $Gender = 2;
          if ($Item['Type'] == 'child') $Gender = 3;
          $RunnerId = $NextRunnerId;
          $Queries[] = $this->Database->GetInsert('Runner', array(
            'Id' => $RunnerId,
            'Name' => $Item['Name'],
            'Gender' => $Gender,
            'Team' => $TeamId,
            'ChipNumber' => $Item['ChipNumber'],
            'Year' => $Year,
          ));
          $Runners[$Item['ChipNumber']] = array('Id' => $RunnerId, 'Distance' => -1, 'Team' => $TeamId);
          $NextRunnerId++;
        } else 
        {
          $RunnerId = $Runners[$Item['ChipNumber']]['Id'];

          // Update runner team if it was changed
          $OldRunnerTeamId = $Runners[$Item['ChipNumber']]['Team'];
          if ($Item['TeamId'] == null)
          {
            $NewRunnerTeamId = null;
          } else
          {
            if (!array_key_exists($Item['TeamId'], $Teams))
            {
              $NewRunnerTeamId = $NextTeamId;
              $Queries[] = $this->Database->GetInsert('Team', array(
                'Id' => $NewRunnerTeamId,
                'Name' => '',
                'WebId' => $Item['TeamId'],
                'Year' => $Year,
                'IsFamily' => 0,
              ));
              $Teams[$Item['TeamId']] = array('Id' => $NewRunnerTeamId, 'Distance' => -1);
              $NextTeamId++;              
            } else
            {
              $NewRunnerTeamId = $Teams[$Item['TeamId']]['Id'];            
            }
          }
          if ($OldRunnerTeamId != $NewRunnerTeamId)
          {
            $Runners[$Item['ChipNumber']]['Team'] = $NewRunnerTeamId;
            $Queries[] = $this->Database->GetUpdate('Runner', 'Id='.$RunnerId, array('Team' => $NewRunnerTeamId));
          }
        }

        if ($Runners[$Item['ChipNumber']]['Distance'] != $Item['OverallDistance'])
        {
          $Queries[] = $this->Database->GetInsert('RunnerStat', array(
            'Time' => TimeToMysqlDateTime($Time),
            'Runner' => $RunnerId,
            'Distance' => $Item['OverallDistance'],
            'Rank' => $Item['Pos'],
             'Money' => $Item['Money'],
          ));
        }
      } else
      if (($Item['Type'] == 'team') or ($Item['Type'] == 'rodina'))
      {
        if ($Item['Name'] == null) $Item['Name'] = '';
        if ($Item['Type'] == 'rodina') $IsFamily = 1;
          else $IsFamily = 0;
        if (!array_key_exists($Item['GroupId'], $Teams))
        {
          $Queries[] = $this->Database->GetInsert('Team', array(
            'Id' => $NextTeamId,
            'Name' => $Item['Name'],
            'WebId' => $Item['GroupId'],
            'IsFamily' => $IsFamily,
            'Year' => $Year,
          ));
          $TeamId = $NextTeamId;
          $Teams[$Item['GroupId']] = array('Id' => $NextTeamId, 'Distance' => -1);
          $NextTeamId++;
        } else
          $TeamId = $Teams[$Item['GroupId']]['Id'];

        // Update missing team names
        if (!array_key_exists('Name', $Teams[$Item['GroupId']]) or $Teams[$Item['GroupId']]['Name'] == "")
        {
          $Queries[] = $this->Database->GetUpdate('Team', 'Id='.$TeamId, array(
            'Name' => $Item['Name'],
            'IsFamily' => $IsFamily
          ));
          $Teams[$Item['GroupId']]['Name'] = $Item['Name'];
          $Teams[$Item['GroupId']]['IsFamily'] = $IsFamily;
        }

        if ($Teams[$Item['GroupId']]['Distance'] != $Item['OverallDistance'])
        {
          $Queries[] = $this->Database->GetInsert('TeamStat', array(
            'Time' => TimeToMysqlDateTime($Time),
            'Team' => $TeamId,
            'Distance' => $Item['OverallDistance'],
            'Rank' => $Item['Pos'],
            'Money' => $Item['Money'],
          ));
        }
      } else
      if ($Item['Type'] == '')
      {
        // Skip empty type
      } else
      {
        echo(T('Unsupported type').' "'.$Item['Type'].'".<br/>');
      }
    }
    //print_r($Queries);
    //foreach ($Queries as $Query) $this->Database->query($Query);
    $this->Database->Transaction($Queries);
  }

  function ShowSync()
  {
    $Time = time();
    $Items = $this->QueryRunnersAll('all');
    $Output = T('Loaded items count: '.count($Items).'<br/>');
    $ItemsWithoutProgress = array();
    foreach ($Items as $Item)
    {
      unset($Item['Progress']);
      $ItemsWithoutProgress[] = $Item;
    }
    $Hash = md5(serialize($ItemsWithoutProgress));

    $DbResult = $this->Database->query('SELECT * FROM Import ORDER BY Time DESC LIMIT 1');
    if ($DbResult->num_rows > 0)
    {
      $Import = $DbResult->fetch_assoc();
    } else $Import = array('Hash' => '');
    if ($Import['Hash'] != $Hash)
    {
      $this->Sync($Items, $Time);
      $this->Database->insert('Import', array(
        'Time' => TimeToMysqlDateTime($Time),
        'Hash' => $Hash,
        'ItemCount' => count($Items)
      ));      
    }
    $Output .= T('Data synchronization from leaderboard finished.<br/>');
    return $Output;
  }

  function ShowTeams()
  {
    return $this->ShowTeamsInternal(T('Teams'), T('Team'), 'teams', 'team', 'Team', '(IsFamily=0)');
  }

  function ShowFamilies()
  {
    return $this->ShowTeamsInternal(T('Families'), T('Family'), 'families', 'family', 'Team', '(IsFamily=1)');
  }

  function ShowTeamsInternal($Title, $TitleItem, $UrlDir, $UrlDirItem, $Table, $Where = '1')
  {
    $Output = '';
    $this->Title = $Title.' - '.$this->Title;
    $Output .= '<div class="page-title">'.$Title.'</div>';
    $Year = $this->GetYear();

    $Where .= ' AND (Year='.$Year.') AND (Hidden=0)';
    if (array_key_exists('query', $_GET) and ($_GET['query'] != ''))
    {
      $Where .= ' AND (Name LIKE "%'.addslashes($_GET['query']).'%")';
    }
    $Output .= '<div class="section-title">'.$this->YearList('/'.$UrlDir.'/', $Year, $Table).' '.T('Name').': '.$this->ShowSearch().'</div>';

    $DbResult = $this->Database->query('SELECT COUNT(*) FROM `'.$Table.'` WHERE '.$Where);
    $DbRow = $DbResult->fetch_row();
    $PageList = GetPageList($DbRow[0]);

    $Output .= '<div id="list_content">';
    $Output .= $PageList['Output'];
    $TableColumns = array(
      array('Name' => 'Name', 'Title' => T('Name')),
      array('Name' => 'RunnersCount', 'Title' => T('Runners')),
      array('Name' => 'Distance', 'Title' => T('Distance')),
      array('Name' => 'Money', 'Title' => T('Money')),
      array('Name' => 'DistanceRunner', 'Title' => T('Distance per runner')),
      array('Name' => 'MoneyRunner', 'Title' => T('Money per runner')),
      array('Name' => 'Rank', 'Title' => T('Rank')),
    );
    $Order = GetOrderTableHeader($TableColumns, 'Distance', 1);
    $Output .= '<table class="WideTable">';
    $Output .= $Order['Output'];
    $DbResult = $this->Database->select('Team', '*, '.
      '(SELECT COUNT(*) FROM Runner WHERE Runner.Team=Team.Id) AS RunnersCount, '.
      '(SELECT TeamStat.Distance FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY TeamStat.Time DESC LIMIT 1) AS Distance, '.
      '(SELECT TeamStat.Money FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Money, '.
      '(SELECT TeamStat.Time FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Time, '.
      '(SELECT TeamStat.Rank FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Rank, '.
      'ROUND((SELECT TeamStat.Distance FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY TeamStat.Time DESC LIMIT 1) / (SELECT COUNT(*) FROM Runner WHERE Runner.Team=Team.Id), 1) AS DistanceRunner, '.
      'ROUND((SELECT TeamStat.Money FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) / (SELECT COUNT(*) FROM Runner WHERE Runner.Team=Team.Id)) AS MoneyRunner',
      $Where.$Order['SQL'].$PageList['SQLLimit']);
    while ($Item = $DbResult->fetch_assoc())
    {
      if ($Item['Name'] == '') $Item['Name'] = $TitleItem.' '.$Item['WebId'];
      $Output .= '<tr>'.
        '<td><a href="'.$this->Link('/'.$UrlDirItem.'/'.$Item['Id'].'/').'">'.$Item['Name'].'</a></td>'.
        '<td>'.$Item['RunnersCount'].'</td>'.
        '<td>'.$Item['Distance'].'</td>'.
        '<td>'.$Item['Money'].'</td>'.
        '<td>'.$Item['DistanceRunner'].'</td>'.
        '<td>'.$Item['MoneyRunner'].'</td>'.
        '<td>'.$Item['Rank'].'</td>'.
        '</tr>';
    }
    $Output .= '</table>';
    $Output .= $PageList['Output'];
    $Output .= '</div>';

    return $Output;
  }

  function RunningState($Time)
  {
    $Output = HumanDateTime($Time);
    if ($Time > time() - 30 * 60)
      $Output = '<span style="color: green; font-weight: bold; ">'.$Output.'</span>';
    return $Output;
  }

  function ShowTeam()
  {
    return $this->ShowTeamInternal(T('Team'), 'team');
  }

  function ShowFamily()
  {
    return $this->ShowTeamInternal(T('Family'), 'family');
  }

  function ShowTeamInternal($Title, $UrlDir)
  {
    $Output = '';

    $TeamId = 0;
    if ((count($this->PathItems) > 0) and ($this->PathItems[count($this->PathItems) - 1] != ''))
      $TeamId = $this->PathItems[count($this->PathItems) - 1];
    if (!is_numeric($TeamId)) die(T('Team id needs to be numeric'));

    $DbResult = $this->Database->query('SELECT Team.*, '.
      '(SELECT COUNT(*) FROM Runner WHERE (Runner.Team=Team.Id) AND (Runner.Hidden=0)) AS RunnerCount, '.
      '(SELECT TeamStat.Distance FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY TeamStat.Time DESC LIMIT 1) AS Distance, '.
      '(SELECT TeamStat.Money FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Money, '.
      '(SELECT TeamStat.Rank FROM TeamStat WHERE TeamStat.Team=Team.Id ORDER BY Time DESC LIMIT 1) AS Rank '.
      'FROM Team WHERE (Hidden=0) AND (Id='.$TeamId.')');
    if ($DbResult->num_rows > 0)
    {
      $DbRow = $DbResult->fetch_assoc();
      $this->Title = $Title.' '.$DbRow['Name'].' - '.$this->Title;
      $Output .= '<div class="page-title">'.$Title.' '.$DbRow['Name'].'</div>';
      $Output .= '<div class="section-title">'.$this->ItemsYearList('/'.$UrlDir.'/', $TeamId, 'Team', 'Name="'.$this->Database->real_escape_string($DbRow['Name']).'"').'</div>';
      $this->LoadYearParameters($DbRow['Year']);
      $Output .= '<div class="section-title">'.
        T('Runners').': '.$DbRow['RunnerCount'].', '.
        T('Distance').': '.$DbRow['Distance'].' km, '.
        T('Money').': '.$DbRow['Money'].' Kč, '.
        T('Rank').': '.$DbRow['Rank'].'</div>';

      $Where = '(Hidden=0) AND (Team='.$TeamId.')';

      // Show runners
      $DbResult = $this->Database->query('SELECT COUNT(*) FROM `Runner` WHERE '.$Where);
      $DbRow = $DbResult->fetch_row();
      $PageList = GetPageList($DbRow[0]);

      $Gender = array('', T('Man'), T('Woman'), T('Kid'));
      $Output .= '<div id="list_content">';
      $Output .= $PageList['Output'];
      $TableColumns = array(
        array('Name' => 'Name', 'Title' => T('Name')),
        array('Name' => 'Gender', 'Title' => T('Category')),
        array('Name' => 'Distance', 'Title' => T('Distance')),
        array('Name' => 'Money', 'Title' => T('Money')),
        array('Name' => 'Rank', 'Title' => T('Rank')),
        array('Name' => 'Time', 'Title' => T('Last change')),
      );
      $Order = GetOrderTableHeader($TableColumns, 'Distance', 1);
      $Output .= '<table class="WideTable">';
      $Output .= $Order['Output'];
      $DbResult = $this->Database->select('Runner', '*, '.
        '(SELECT RunnerStat.Distance FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Distance'.
        ', (SELECT RunnerStat.Money FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Money'.
        ', (SELECT RunnerStat.Time FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Time'.
        ', (SELECT RunnerStat.Rank FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Rank',
        $Where.$Order['SQL'].$PageList['SQLLimit']);
      while ($Item = $DbResult->fetch_assoc())
      {
        $Output .= '<tr>'.
          '<td><a href="'.$this->Link('/runner/'.$Item['Id'].'/').'">'.$Item['Name'].'</a></td>'.
          '<td>'.$Gender[$Item['Gender']].'</td>'.
          '<td>'.$Item['Distance'].'</td>'.
          '<td>'.$Item['Money'].'</td>'.
          '<td>'.$Item['Rank'].'</td>'.
          '<td>'.$this->RunningState(MysqlDateTimeToTime($Item['Time'])).'</td>'.
          '</tr>';
      }
      $Output .= '</table>';
      $Output .= $PageList['Output'];
      $Output .= '</div><br/>';

      $Output .= $this->ShowDetailed('Team', $TeamId);
      //$Output .= $this->ShowDetailedChart('Team', $TeamId);
      $Output .= $this->ShowDaily('Team', $TeamId);
      //$Output .= $this->ShowDailyChart('Team', $TeamId);
    } else $Output .= T('Team not found.');
    return $Output;
  }

  function ShowDetailed($Table, $Id)
  {
    $PrefixMultiplier = new PrefixMultiplier();

    $TableStat = $Table.'Stat';
    $Output = '<div class="section-title">'.T('Lap progress').'</div>';
    $Where = $TableStat.'.'.$Table.'='.$Id;
    $DbResult = $this->Database->query('SELECT COUNT(*) FROM '.$TableStat.' WHERE '.$Where);
    $DbRow = $DbResult->fetch_row();
    $PageList = GetPageList($DbRow[0]);

    $Output .= '<div id="list_content">';
    $Output .= $PageList['Output'];
    $TableColumns = array(
      array('Name' => 'Time', 'Title' => T('Time')),
      array('Name' => 'Distance', 'Title' => T('Distance').' [km]'),
      array('Name' => 'Money', 'Title' => T('Money').' [Kč]'),
      array('Name' => 'Rank', 'Title' => T('Rank')),
      array('Name' => 'Duration', 'Title' => T('Duration')),
      array('Name' => 'Length', 'Title' => T('Length').' [km]'),
      array('Name' => 'Speed', 'Title' => T('Speed').' [km/'.T('hour').']'),
    );
    $Order = GetOrderTableHeader($TableColumns, 'Time', 1);
    $Output .= '<table class="WideTable">';
    $Output .= $Order['Output'];
    $DbResult = $this->Database->query('SELECT *'.
      ', (SELECT '.$TableStat.'.Distance - B.Distance FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Length'.
      ', (SELECT TIME_TO_SEC(TIMEDIFF('.$TableStat.'.Time, B.Time)) FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Duration'.
      ', NULL AS Speed'.
      ' FROM '.$TableStat.
      ' WHERE '.$Where.$Order['SQL'].$PageList['SQLLimit']);
    while ($Item = $DbResult->fetch_assoc())
    {
      $Output .= '<tr>'.
        '<td>'.HumanDateTime(MysqlDateTimeToTime($Item['Time'])).'</td>'.
        '<td>'.$Item['Distance'].'</td>'.
        '<td>'.$Item['Money'].'</td>'.
        '<td>'.$Item['Rank'].'</td>';
      if (($Item['Duration'] != null) and ($Item['Duration'] < $Item['Length'] / $this->MinRunnerSpeed * 3600)) $Output .= '<td>'.$PrefixMultiplier->Add($Item['Duration'], '', 4, 'Time').'</td>';
        else $Output .= '<td>&nbsp;</td>';
      $Output .= '<td>'.$Item['Length'].'</td>';

      if (($Item['Duration'] > 0) and ($Item['Duration'] < $Item['Length'] * 3600 / $this->MinRunnerSpeed))
      {
        $Speed = $Item['Length'] / $Item['Duration'] * 3600;
        $Output .= '<td>'.$PrefixMultiplier->Add($Speed, '', 4, 'Decimal').'</td>';
      } else $Output .= '<td>&nbsp;</td>';
      $Output .= '</tr>';
    }
    $Output .= '</table>';
    $Output .= $PageList['Output'];
    $Output .= '</div><br/>';
    return $Output;
  }

  function ShowDetailedChart($Table, $Id)
  {
    $TableStat = $Table.'Stat';
    $DbResult = $this->Database->query('SELECT *'.
      ', (SELECT '.$TableStat.'.Distance - B.Distance FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Length'.
      ', (SELECT TIME_TO_SEC(TIMEDIFF('.$TableStat.'.Time, B.Time)) FROM '.$TableStat.' AS B WHERE (B.Time < '.$TableStat.'.Time) AND (B.'.$Table.' = '.$TableStat.'.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Duration'.
      ', NULL AS Speed'.
      ' FROM '.$TableStat.
      ' WHERE '.$TableStat.'.'.$Table.'='.$Id.' ORDER BY Time');
    $ChartValues = array();
    while ($Item = $DbResult->fetch_assoc())
    {
      $ChartValues[MysqlDateTimeToTime($Item['Time'])] = $Item['Distance'];
    }
    $Output = $this->ShowChart($Table.'Detailed', $ChartValues, T('Lap progress'));
    return $Output;
  }

  function ShowDaily($Table, $Id)
  {
    $PrefixMultiplier = new PrefixMultiplier();
    $Where = '1';

    $TableStat = $Table.'Stat';
    $DailyTableMaxId = 'SELECT * FROM (SELECT MAX(Id) AS MaxId FROM '.$TableStat.' AS T1 WHERE T1.'.$Table.'='.$Id.' GROUP BY DATE(Time)) AS T2 LEFT JOIN '.$TableStat.' AS T3 ON T3.Id=T2.MaxId';
    $DailyTableMinId = 'SELECT * FROM (SELECT MIN(Id) AS MinId FROM '.$TableStat.' AS T1 WHERE T1.'.$Table.'='.$Id.' GROUP BY DATE(Time)) AS T2 LEFT JOIN '.$TableStat.' AS T3 ON T3.Id=T2.MinId';
    $DbResult = $this->Database->query('SELECT COUNT(*) FROM ('.$DailyTableMaxId.') AS B');
    $DbRow = $DbResult->fetch_row();
    $PageList = GetPageList($DbRow[0]);

    $Output = '<div class="section-title">'.T('Daily progress').'</div>';
    $Output .= '<div id="list_content">';
    $Output .= $PageList['Output'];
    $TableColumns = array(
      array('Name' => 'Time', 'Title' => T('Time')),
      array('Name' => 'Distance', 'Title' => T('Distance').' [km]'),
      array('Name' => 'Money', 'Title' => T('Money').' [Kč]'),
      array('Name' => 'Rank', 'Title' => T('Rank')),
      array('Name' => 'Duration', 'Title' => T('Duration')),
      array('Name' => 'Length', 'Title' => T('Length').' [km]'),
      array('Name' => 'Speed', 'Title' => T('Speed').' [km/'.T('hour').']'),
    );
    $Order = GetOrderTableHeader($TableColumns, 'Time', 1);
    $Output .= '<table class="WideTable">';
    $Output .= $Order['Output'];

    if ($this->LapLength == 0)
    {
      $DbResult = $this->Database->query('SELECT * '.
        ', COALESCE((SELECT T4.Distance - B.Distance FROM ('.$DailyTableMaxId.') AS B WHERE (DATE(B.Time) < DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1), T4.Distance) AS Length'.
        ', NULL AS Duration'.
        ', NULL AS Speed'.
        ' FROM ('.$DailyTableMaxId.') AS T4'.
        ' WHERE '.$Where.$Order['SQL'].$PageList['SQLLimit']);
    } else
    {
      $DbResult = $this->Database->query('SELECT * '.
        ', COALESCE((SELECT T4.Distance - B.Distance FROM ('.$DailyTableMaxId.') AS B WHERE (DATE(B.Time) < DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1), T4.Distance) AS Length'.
        //', (SELECT COUNT(*) * '.$this->LapLength.' FROM '.$TableStat.' AS B WHERE (DATE(B.Time) = DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.')) AS LapLength'.
        ', (SELECT TIME_TO_SEC(TIMEDIFF(T4.Time, B.Time)) / (Length - '.$this->LapLength.') * Length FROM ('.$DailyTableMinId.') AS B WHERE (DATE(B.Time) = DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1) AS Duration'.
        ', NULL AS Speed'.
        ' FROM ('.$DailyTableMaxId.') AS T4'.
        ' WHERE '.$Where.$Order['SQL'].$PageList['SQLLimit']);
    }
    while ($Item = $DbResult->fetch_assoc())
    {
      $Output .= '<tr>'.
        '<td>'.HumanDate(MysqlDateTimeToTime($Item['Time'])).'</td>'.
        '<td>'.$Item['Distance'].'</td>'.
        '<td>'.$Item['Money'].'</td>'.
        '<td>'.$Item['Rank'].'</td>';
      if ($Item['Duration'] != null) $Output .= '<td>'.$PrefixMultiplier->Add($Item['Duration'], '', 4, 'Time').'</td>';
        else $Output .= '<td>&nbsp;</td>';
      $Output .= '<td>'.$Item['Length'].'</td>';
      if ($Item['Duration'] > 0) $Output .= '<td>'.$PrefixMultiplier->Add($Item['Length'] / $Item['Duration'] * 3600, '', 4, 'Decimal').'</td>';
        else $Output .= '<td>&nbsp;</td>';
      $Output .= '</tr>';
    }
    $Output .= '</table>';
    $Output .= $PageList['Output'];
    $Output .= '</div>';
    return $Output;
  }

  function ShowDailyChart($Table, $Id)
  {
    $TableStat = $Table.'Stat';
    $DailyTableMaxId = 'SELECT * FROM (SELECT MAX(Id) AS MaxId FROM '.$TableStat.' AS T1 WHERE T1.'.$Table.'='.$Id.' GROUP BY DATE(Time)) AS T2 LEFT JOIN '.$TableStat.' AS T3 ON T3.Id=T2.MaxId';
    $DbResult = $this->Database->query('SELECT * '.
    ', COALESCE((SELECT T4.Distance - B.Distance FROM ('.$DailyTableMaxId.') AS B WHERE (DATE(B.Time) < DATE(T4.Time)) AND (B.'.$Table.' = T4.'.$Table.') ORDER BY B.Time DESC LIMIT 1), T4.Distance) AS Length'.
    ' FROM ('.$DailyTableMaxId.') AS T4'.
    ' ORDER BY Time');
    $ChartValues = array();
    while ($Item = $DbResult->fetch_assoc())
    {
      $ChartValues[MysqlDateTimeToTime($Item['Time'])] = $Item['Length'];
    }
    $Output = $this->ShowChart($Table.'Daily', $ChartValues, T('Daily progress'));
    return $Output;
  }

  function ShowSearch()
  {
    if (array_key_exists('query', $_GET)) $Query = $_GET['query'];
      else $Query = '';
    $Output = '<form action="?" method="get" style="display: inline;">';
    $Output .= '<input type="text" size="10" name="query" value="'.$Query.'"/> '.
      '<input type="submit" value="'.T('Search').'"/>';
    $Output .= '</form>';
    return $Output;
  }

  function ShowRunnersAll()
  {
    return $this->ShowRunners();
  }

  function ShowRunnersMen()
  {
    return $this->ShowRunners('(Gender=1)');
  }

  function ShowRunnersWomen()
  {
    return $this->ShowRunners('(Gender=2)');
  }

  function ShowRunnersKids()
  {
    return $this->ShowRunners('(Gender=3)');
  }

  function ShowRunners($Where = '1')
  {
    $this->Title = T('Runners').' - '.$this->Title;
    $Output = '<div class="page-title">'.T('Runners').'</div>';
    $Output .= '<div class="section-title">'.
      '<a href="'.$this->Link('/runners/').'">'.T('All').'</a> '.
      '<a href="'.$this->Link('/men/').'">'.T('Men').'</a> '.
      '<a href="'.$this->Link('/women/').'">'.T('Women').'</a> '.
      '<a href="'.$this->Link('/kids/').'">'.T('Kids').'</a>'.
      '</div>';
    $Year = $this->GetYear();

    $Output .= '<div class="section-title">'.$this->YearList('/runners/', $Year, 'Runner').' '.T('Name').': '.$this->ShowSearch().'</div>';
    $Where .= ' AND (Year='.$Year.') AND (Hidden=0)';
    if (array_key_exists('query', $_GET) and ($_GET['query'] != ''))
    {
      $Where .= ' AND (Name LIKE "%'.addslashes($_GET['query']).'%")';
    }

    $DbResult = $this->Database->query('SELECT COUNT(*) FROM `Runner` WHERE '.$Where);
    $DbRow = $DbResult->fetch_row();
    $PageList = GetPageList($DbRow[0]);

    $Gender = array('', T('Man'), T('Woman'), T('Kid'));
    $Output .= '<div id="list_content">';
    $Output .= $PageList['Output'];
    $TableColumns = array(
      array('Name' => 'Name', 'Title' => T('Name')),
      array('Name' => 'Gender', 'Title' => T('Category')),
      array('Name' => 'Distance', 'Title' => T('Distance')),
      array('Name' => 'Money', 'Title' => T('Money')),
      array('Name' => 'Rank', 'Title' => T('Rank')),
      array('Name' => 'Time', 'Title' => T('Last change')),
    );
    $Order = GetOrderTableHeader($TableColumns, 'Distance', 1);
    $Output .= '<table class="WideTable">';
    $Output .= $Order['Output'];
    $DbResult = $this->Database->select('Runner', '*, '.
      '(SELECT RunnerStat.Distance FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Distance'.
      ', (SELECT RunnerStat.Money FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Money'.
      ', (SELECT RunnerStat.Time FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Time'.
      ', (SELECT RunnerStat.Rank FROM RunnerStat WHERE RunnerStat.Runner=Runner.Id ORDER BY Time DESC LIMIT 1) AS Rank',
      $Where.$Order['SQL'].$PageList['SQLLimit']);
    while ($Item = $DbResult->fetch_assoc())
    {
      $Output .= '<tr>'.
        '<td><a href="'.$this->Link('/runner/'.$Item['Id'].'/').'">'.$Item['Name'].'</a></td>'.
        '<td>'.$Gender[$Item['Gender']].'</td>'.
        '<td>'.$Item['Distance'].'</td>'.
        '<td>'.$Item['Money'].'</td>'.
        '<td>'.$Item['Rank'].'</td>'.
        '<td>'.$this->RunningState(MysqlDateTimeToTime($Item['Time'])).'</td>'.
        '</tr>';
    }
    $Output .= '</table>';
    $Output .= $PageList['Output'];
    $Output .= '</div>';

    return $Output;
  }

  function ShowRunner()
  {
    $PrefixMultiplier = new PrefixMultiplier();
    $Output = '';

    $RunnerId = 0;
    if ((count($this->PathItems) > 0) and ($this->PathItems[count($this->PathItems) - 1] != ''))
      $RunnerId = $this->PathItems[count($this->PathItems) - 1];
    if (!is_numeric($RunnerId)) die(T('Runner id needs to be numeric'));

    $DbResult = $this->Database->query('SELECT Runner.Name, Team.Name AS TeamName, Team.Id AS TeamId, Runner.Year FROM Runner '.
      'LEFT JOIN Team ON Team.Id=Runner.Team WHERE (Runner.Hidden=0) AND (Runner.Id='.$RunnerId.')');
    if ($DbResult->num_rows > 0)
    {
      $DbRow = $DbResult->fetch_assoc();
      $this->Title = T('Runner').' '.$DbRow['Name'].' - '.$this->Title;
      $Output .= '<div class="page-title">'.T('Runner').' '.$DbRow['Name'].'</div>';
      if ($DbRow['TeamName'] != '')
        $Output .= '<div class="section-title"><a href="'.$this->Link('/team/'.$DbRow['TeamId']).'">'.$DbRow['TeamName'].'</a></div>';
      $Output .= '<div class="section-title">'.$this->ItemsYearList('/runner/', $RunnerId, 'Runner', 'Name="'.$this->Database->real_escape_string($DbRow['Name']).'"').'</div>';
      $this->LoadYearParameters($DbRow['Year']);

      $Output .= $this->ShowDetailed('Runner', $RunnerId);
      //$Output .= $this->ShowDetailedChart('Runner', $RunnerId);
      $Output .= $this->ShowDaily('Runner', $RunnerId);
      //$Output .= $this->ShowDailyChart('Runner', $RunnerId);
    } else $Output .= T('Runner not found.');
    return $Output;
  }

  function GetTotals($Where = '1', $Table = 'Runner')
  {
    $DbResult = $this->Database->query('SELECT (SELECT COUNT(*) FROM '.$Table.' WHERE '.$Where.') AS TotalCount, '.
      '(SELECT SUM(T1.Distance) FROM (SELECT (SELECT Distance FROM '.$Table.'Stat WHERE '.$Table.'Stat.'.$Table.' = '.$Table.'.Id ORDER BY Time DESC LIMIT 1) AS Distance FROM '.$Table.' WHERE '.$Where.') AS T1) AS TotalDistance, '.
      '(SELECT SUM(T2.Money) FROM (SELECT (SELECT Money FROM '.$Table.'Stat WHERE '.$Table.'Stat.'.$Table.' = '.$Table.'.Id ORDER BY Time DESC LIMIT 1) AS Money FROM '.$Table.' WHERE '.$Where.') AS T2) AS TotalMoney');
    $DbRow = $DbResult->fetch_assoc();
    return $DbRow;
  }

  function GetYear()
  {
    $Year = 0;
    if (count($this->PathItems) > 0)
    {
      $Param = $this->PathItems[count($this->PathItems) - 1];
      if (is_numeric($this->PathItems[count($this->PathItems) - 1]))
        $Year = $this->PathItems[count($this->PathItems) - 1] * 1;
    }
    if ($Year == 0) $Year = $this->GetLatestYear();
    $this->LoadYearParameters($Year);
    return $Year;
  }

  function ShowMain()
  {
    $Output = '';
    $Output .= '<p>'.sprintf(T('This website collects data from official <a href="%s">leaderboard</a> site and tracks and presents progress of runners and teams.'), $this->LeaderboardURL).'</p>';
    $Output .= '<div class="page-title">'.T('Summary').'</div>';
    $Year = $this->GetYear();

    $Output .= '<div class="section-title">'.$this->YearList('/', $Year).'</div>';

    $Runners = $this->GetTotals('(Year='.$Year.')');
    $Men = $this->GetTotals('(Runner.Gender=1) AND (Year='.$Year.')', 'Runner');
    $Women = $this->GetTotals('(Runner.Gender=2) AND (Year='.$Year.')', 'Runner');
    $Kids = $this->GetTotals('(Runner.Gender=3) AND (Year='.$Year.')', 'Runner');
    $Teams = $this->GetTotals('(Team.IsFamily=0) AND (Year='.$Year.')', 'Team');
    $Families = $this->GetTotals('(Team.IsFamily=1) AND (Year='.$Year.')', 'Team');

    $Output .= '<table class="WideTable">';
    $Output .= '<tr><th>'.T('Category').'</th><th>'.T('Count').'</th><th>'.T('Distance').' [km]</th><th>'.T('Money').' [Kč]</th></tr>';
    $Output .= '<tr><td>'.T('Everyone').'</td><td>'.$Runners['TotalCount'].'</td><td>'.$Runners['TotalDistance'].'</td><td>'.$Runners['TotalMoney'].'</td></tr>';
    $Output .= '<tr><td>'.T('Men').'</td><td>'.$Men['TotalCount'].'</td><td>'.$Men['TotalDistance'].'</td><td>'.$Men['TotalMoney'].'</td></tr>';
    $Output .= '<tr><td>'.T('Women').'</td><td>'.$Women['TotalCount'].'</td><td>'.$Women['TotalDistance'].'</td><td>'.$Women['TotalMoney'].'</td></tr>';
    $Output .= '<tr><td>'.T('Kids').'</td><td>'.$Kids['TotalCount'].'</td><td>'.$Kids['TotalDistance'].'</td><td>'.$Kids['TotalMoney'].'</td></tr>';
    $Output .= '<tr><td>'.T('Teams').'</td><td>'.$Teams['TotalCount'].'</td><td>'.$Teams['TotalDistance'].'</td><td>'.$Teams['TotalMoney'].'</td></tr>';
    $Output .= '<tr><td>'.T('Families').'</td><td>'.$Families['TotalCount'].'</td><td>'.$Families['TotalDistance'].'</td><td>'.$Families['TotalMoney'].'</td></tr>';
    $Output .= '</table>';

    return $Output;
  }

  function ShowPage($Content)
  {
    global $Config;

    $Lang = 'en';
    $Output = '<?xml version="1.0" encoding="'.$this->Config['Encoding'].'"?>'."\n".
      '<!doctype html>'.
      '<html>'.
      '<head>'.
      '<link rel="stylesheet" href="'.$this->Link('/style/style.css').'" type="text/css" media="all" />'.
      '<meta http-equiv="content-type" content="application/xhtml+xml; charset='.$this->Config['Encoding'].'" />'.
      '<meta name="viewport" content="width=device-width, initial-scale=1">'.
      '<script type="text/javascript" src="'.$this->Link('/Packages/Chart.js/Chart.js').'"></script>';
      //'<script src="'.$this->Link('/jquery.js').'"></script>';
    $Output .= '<title>'.$this->Title.'</title>'.
      '</head><body>';
    $Output .= $Content;
    $Output .= '<br/><div class="footer">'.T('Contact').': <a href="mailto:'.$Config['Contact'].'">'.$Config['Contact'].'</a> '.
       '<a href="https://app.zdechov.net/teribear/">'.T('Source code').'</a></div>';
    $Output .= '</body></html>';
    return $Output;
  }

  function Link($Target)
  {
    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 LoadYearParameters($Year)
  {
    $DbResult = $this->Database->query('SELECT * FROM `Year` WHERE `Year`.`Year`='.$Year);
    if ($DbResult->num_rows > 0)
    {
      $DbRow = $DbResult->fetch_assoc();
      $this->LapLength = $DbRow['LapLength'];
      $this->MoneyKm = $DbRow['MoneyKm'];
    }
  }

  function ShowChart($ChartName, $Values, $Title)
  {
    $Output = '<div style="width:50%; text-align: center; margin-left: auto; margin-right: auto;">'.
		'  <canvas id="'.$ChartName.'"></canvas>'.
    '</div>'.
    '<script type="text/javascript">';
    $Output .= "var ctx = document.getElementById('".$ChartName."').getContext('2d');
    window.chartColors = {
      red: 'rgb(255, 99, 132)',
      orange: 'rgb(255, 159, 64)',
      yellow: 'rgb(255, 205, 86)',
      green: 'rgb(75, 192, 192)',
      blue: 'rgb(54, 162, 235)',
      purple: 'rgb(153, 102, 255)',
      grey: 'rgb(201, 203, 207)'
    };
    var config = {
      type: 'line',
      data: {
        labels: [";
      foreach ($Values as $Key => $Value)
      {
        $Output .= "'".$Key."', ";
      }
      $Output .= "],
        datasets: [{
          label: '',
          backgroundColor: window.chartColors.red,
          borderColor: window.chartColors.red,
          data: [";
          foreach ($Values as $Key => $Value)
          {
            $Output .= "{ x: ".$Key.", ";
            $Output .= "y: ".$Value." }, ";
          }
          $Output .= "
          ],
          fill: false,
        }]
      },
      options: {
        responsive: true,
        title: {
          display: true,
          text: '".$Title."'
        },
        tooltips: {
          mode: 'index',
          intersect: false,
        },
        hover: {
          mode: 'nearest',
          intersect: true
        },
        scales: {
          xAxes: [{
						type: 'time',
						display: true,
						scaleLabel: {
							display: true,
							labelString: 'Date'
						},
						ticks: {
							major: {
								fontStyle: 'bold',
								fontColor: '#FF0000'
							}
						}
					}],
          yAxes: [{
						display: true,
						scaleLabel: {
							display: true,
							labelString: 'value'
						}
          }]
        }
      }
    };
    var ".$ChartName." = new Chart(ctx, config);
    </script>";
    return $Output;
  }

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

    $this->Config = $Config;
    $this->Database = new Database();
    $this->Database->Connect($this->Config['Database']['Host'], $this->Config['Database']['User'],
      $this->Config['Database']['Password'], $this->Config['Database']['Database']);
    $this->Database->Prefix = $this->Config['Database']['Prefix'];
    $this->Database->charset($this->Config['Database']['Charset']);
    //$this->Database->ShowSQLError = true;
    //$this->Database->ShowSQLQuery = true;

    $this->LocaleManager = new LocaleManager($this);
    $this->LocaleManager->Dir = 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('style', 'Packages');

    $this->PathItems = $this->ProcessURL();

    // Detect interface locale
    if (isset($this->Config['Locale']))
      $this->LocaleManager->DefaultLangCode = $this->Config['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);
    }
    InitPrefixMultipliers();

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

    $this->Title = T('Teribear stats');
    $Output = '';

    $this->Year = 0;
    if (count($this->PathItems) > 0)
    {
      $Item = $this->PathItems[0];
      $Item = $this->LocaleManager->CurrentLocale->Texts->TranslateReverse($Item, 'URL');
      if ($Item == 'sync') $Output .= $this->ShowSync();
      //else if ($Item == 'empty') $Output .= $this->ShowEmpty();
      else if ($Item == 'runner') $Output .= $this->ShowRunner();
      else if ($Item == 'runners') $Output .= $this->ShowRunnersAll();
      else if ($Item == 'men') $Output .= $this->ShowRunnersMen();
      else if ($Item == 'women') $Output .= $this->ShowRunnersWomen();
      else if ($Item == 'kids') $Output .= $this->ShowRunnersKids();
      else if ($Item == 'team') $Output .= $this->ShowTeam();
      else if ($Item == 'teams') $Output .= $this->ShowTeams();
      else if ($Item == 'family') $Output .= $this->ShowFamily();
      else if ($Item == 'families') $Output .= $this->ShowFamilies();
      else $Output .= $this->ShowMain();
    } else $Output .= $this->ShowMain();
    if (!$this->NoFullPage)
    {
      $Output = $this->ShowMenu().$Output;
      echo($this->ShowPage($Output));
    } else echo($Output);
  }
}

$Application = new MyApplication();
$Application->Run();
