<?php
// Config
// RIOT_API_KEY in the environment overrides the key below.
// Development keys expire every 24h; register a Personal API key for something that keeps running.
$apiKey = getenv('RIOT_API_KEY') ? getenv('RIOT_API_KEY') : 'RGAPI-fa1e14bb-9f5b-431e-ab7b-00e27519d244';

date_default_timezone_set('Europe/Lisbon');

define('PLATFORM', 'euw1');   // platform routing (spectator)
define('REGION',   'europe');  // regional routing (account)
define('LOCALE',   'en_US');   // Data Dragon locale, pt_BR also exists

// Riot IDs are now gameName#tagLine. Check the real tags in the client.
$aUsers = array(
    array('name' => 'PAdeiras', 'tag' => 'EUW'),
    array('name' => 'task4z',   'tag' => 'EUW'),
    array('name' => 'Kyzbet',   'tag' => 'EUW'),
    array('name' => 'Pao de Lol', 'tag' => 'EUW'),
);

function val($arr, $key, $default = null)
{
    return (is_array($arr) && isset($arr[$key])) ? $arr[$key] : $default;
}

function riotGet($url, $apiKey)
{
    $ch = curl_init($url);
    curl_setopt_array($ch, array(
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => array('X-Riot-Token: ' . $apiKey),
        CURLOPT_TIMEOUT        => 5,
    ));
    $body = curl_exec($ch);
    $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    return array($code, $body === false ? null : json_decode($body, true));
}

function httpGetJson($url)
{
    $ch = curl_init($url);
    curl_setopt_array($ch, array(CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10));
    $body = curl_exec($ch);
    curl_close($ch);
    return $body === false ? null : json_decode($body, true);
}

function cacheFile($name)
{
    return sys_get_temp_dir() . '/lolcheck_' . $name . '.json';
}

function cacheRead($name, $ttl)
{
    $file = cacheFile($name);
    if (is_file($file) && filemtime($file) > time() - $ttl) {
        $data = json_decode(file_get_contents($file), true);
        return is_array($data) ? $data : null;
    }
    return null;
}

function cacheWrite($name, $data)
{
    file_put_contents(cacheFile($name), json_encode($data));
}

// Champion id => [name, title], from Data Dragon (the old static-data API is gone)
function championMap()
{
    $cached = cacheRead('champions_' . LOCALE, 86400);
    if ($cached !== null) return $cached;

    $versions = httpGetJson('https://ddragon.leagueoflegends.com/api/versions.json');
    if (!$versions) return array();

    $data = httpGetJson('https://ddragon.leagueoflegends.com/cdn/' . $versions[0] . '/data/' . LOCALE . '/champion.json');
    $map = array();
    foreach (val($data, 'data', array()) as $c) {
        $map[(int) $c['key']] = array($c['name'], $c['title']);
    }
    if ($map) cacheWrite('champions_' . LOCALE, $map);
    return $map;
}

// Riot ID => PUUID, cached since it never changes
function puuidFor($name, $tag, $apiKey)
{
    // PUUIDs are encrypted per API key, so a new key needs its own cache
    $cacheName = 'puuids_' . substr(md5($apiKey), 0, 8);
    $map = cacheRead($cacheName, 30 * 86400);
    if ($map === null) $map = array();

    $key = strtolower($name . '#' . $tag);
    if (isset($map[$key])) return $map[$key];

    list($code, $acc) = riotGet(
        'https://' . REGION . '.api.riotgames.com/riot/account/v1/accounts/by-riot-id/'
            . rawurlencode($name) . '/' . rawurlencode($tag),
        $apiKey
    );
    $puuid = val($acc, 'puuid');
    if ($code !== 200 || !$puuid) {
        $GLOBALS['lastRiotError'] = array($code, val(val($acc, 'status'), 'message'));
        return null;
    }

    $map[$key] = $puuid;
    cacheWrite($cacheName, $map);
    return $puuid;
}

// Last finished match: [endTimestampSeconds, [champName, champTitle], win] or null.
// The match list is cached for 5 minutes, match details forever (they never change).
function lastMatch($puuid, $apiKey)
{
    $idsCache = 'lastid_' . md5($puuid);
    $ids = cacheRead($idsCache, 300);
    if ($ids === null) {
        list($code, $ids) = riotGet(
            'https://' . REGION . '.api.riotgames.com/lol/match/v5/matches/by-puuid/' . $puuid . '/ids?start=0&count=1',
            $apiKey
        );
        if ($code !== 200 || !is_array($ids)) return null;
        cacheWrite($idsCache, $ids);
    }
    if (empty($ids)) return null;

    $matchId    = $ids[0];
    $matchCache = 'match_' . preg_replace('/[^A-Za-z0-9_]/', '_', $matchId) . '_' . md5($puuid);
    $summary    = cacheRead($matchCache, 365 * 86400);
    if ($summary === null) {
        list($code, $match) = riotGet(
            'https://' . REGION . '.api.riotgames.com/lol/match/v5/matches/' . rawurlencode($matchId),
            $apiKey
        );
        if ($code !== 200) return null;

        $info = val($match, 'info', array());
        $end  = val($info, 'gameEndTimestamp');
        if (!$end) {
            $end = val($info, 'gameStartTimestamp', 0) + val($info, 'gameDuration', 0) * 1000;
        }
        $summary = array('end' => (int) floor($end / 1000), 'champ' => null, 'win' => null);
        foreach (val($info, 'participants', array()) as $p) {
            if (val($p, 'puuid') === $puuid) {
                $summary['champ'] = (int) $p['championId'];
                $summary['win']   = (bool) val($p, 'win', false);
            }
        }
        cacheWrite($matchCache, $summary);
    }

    $champions = championMap();
    return array(
        $summary['end'],
        val($champions, $summary['champ'], array('Desconhecido', '')),
        $summary['win'],
    );
}

function plural($n, $one, $many)
{
    return $n . ' ' . ($n == 1 ? $one : $many);
}

// "há 1 hora e 50 minutos", "há 1 dia e 18 horas"
function timeAgo($ts)
{
    $d = max(0, time() - $ts);
    if ($d < 60) return 'há menos de um minuto';

    $min = (int) floor($d / 60);
    if ($min < 60) return 'há ' . plural($min, 'minuto', 'minutos');

    $h = (int) floor($min / 60);
    $m = $min % 60;
    if ($h < 24) {
        return 'há ' . plural($h, 'hora', 'horas') . ($m ? ' e ' . plural($m, 'minuto', 'minutos') : '');
    }

    $days = (int) floor($h / 24);
    $h    = $h % 24;
    return 'há ' . plural($days, 'dia', 'dias') . ($h ? ' e ' . plural($h, 'hora', 'horas') : '');
}

// Main
foreach ($aUsers as &$aUser) {
    $aUser['ingame'] = false;
    $aUser['error']  = null;
    $aUser['last']   = null;

    $puuid = puuidFor($aUser['name'], $aUser['tag'], $apiKey);
    if (!$puuid) {
        $err = isset($GLOBALS['lastRiotError']) ? $GLOBALS['lastRiotError'] : array(0, null);
        $aUser['error'] = $err[0] == 404
            ? 'Riot ID not found'
            : 'Account API error (' . $err[0] . ($err[1] ? ': ' . $err[1] : '') . ')';
        continue;
    }

    list($code, $game) = riotGet(
        'https://' . PLATFORM . '.api.riotgames.com/lol/spectator/v5/active-games/by-summoner/' . $puuid,
        $apiKey
    );

    if ($code === 404) {                    // not in game
        $aUser['last'] = lastMatch($puuid, $apiKey);
        continue;
    }
    if ($code !== 200) {
        $msg = val(val($game, 'status'), 'message');
        $aUser['error'] = 'Spectator API error (' . $code . ($msg ? ': ' . $msg : '') . ')';
        continue;
    }

    $aUser['ingame'] = true;
    $champions = championMap();
    foreach (val($game, 'participants', array()) as $p) {
        if (val($p, 'puuid') === $puuid) {
            $aUser['champion'] = val($champions, $p['championId'], array('Desconhecido', ''));
        }
    }
}
unset($aUser);

function e($s)
{
    return htmlspecialchars((string) $s, ENT_QUOTES, 'UTF-8');
}
?>
<!doctype html>
<html lang="pt">
<head>
    <meta charset="utf-8">
    <title>será que algum deles está a jogar?</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        html, body { margin: 0; color: #727272; font: 13px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; }
        .wrap { max-width: 600px; margin: 0 auto; padding: 0 20px; box-sizing: border-box; }
        .about { margin: 40px 0 36px; }
        h3 { font-size: 13px; color: #333; font-weight: bold; border-bottom: 1px solid #e8e8e8; margin: 0 0 8px; padding-bottom: 8px; }
        p { margin: 0 0 1.5em; }
        .yes { color: green; font-weight: bold; }
        .no { color: red; font-weight: bold; }
        .err { color: #c30; }
        .last { color: #999; font-size: 12px; }
    </style>
</head>
<body>
<?php foreach ($aUsers as $player): ?>
    <div class="wrap">
        <div class="about">
            <h3>Será que o <b><?php echo e($player['name']); ?></b> está a viciar?</h3>
            <p>
            <?php if ($player['error']): ?>
                <span class="err"><?php echo e($player['error']); ?></span>
            <?php elseif ($player['ingame']): ?>
                <?php $champ = val($player, 'champion', array('?', '')); ?>
                <span class="yes">Sim</span>, e está a feedar de
                <b><?php echo e($champ[0]); ?></b>
            <?php else: ?>
                <span class="no">Não</span>... Algo se passa!
                <?php if ($player['last']): ?>
                    <?php list($lastTs, $lastChamp, $lastWin) = $player['last']; ?>
                    <br><span class="last">
                        Último jogo <?php echo e(timeAgo($lastTs)); ?>
                        (<?php echo e(date('d/m H:i', $lastTs)); ?>),
                        de <b><?php echo e($lastChamp[0]); ?></b>,
                        <?php echo $lastWin ? '<span class="yes">ganhou</span>' : '<span class="no">perdeu</span>'; ?>
                    </span>
                <?php endif; ?>
            <?php endif; ?>
            </p>
        </div>
    </div>
<?php endforeach; ?>
</body>
</html>
