<?php

session_name("a3e0f0ee-9f5a-4be2-88cb-3b7ceb626d00");
session_start();

function get(string $id, string $name): ?string {
    $file = __DIR__ . "/../_runners/$id/results/" . $name;
    if (!is_file($file))
        return null;
    return file_get_contents($file);
}

function csrf_token(): string {
    return $_SESSION["csrf-token"] = base64_encode(random_bytes(30));
}

function getid(): string {
    return str_pad(bin2hex(random_bytes(16)), 32, "0", STR_PAD_LEFT);
}

function delTree($dir) { 
    foreach (scandir($dir) as $ent) {
        if ($ent === "." || $ent === "..")
            continue;
        $path = $dir . DIRECTORY_SEPARATOR . $ent;
        if (is_dir($path))
            delTree($path);
        else
            unlink($path);
    }
    return rmdir($dir);
}

function file_cachebuster(string $filename): string {
    return htmlentities(urlencode(md5(
        strval(filemtime($filename)) . 
        strval(filesize($filename))
    )));
}

$defaultCode = <<<java
public class Program {
    public static void main(String[] args) {
        // Your code here
        System.out.println("Hello, world!");
    }
}
java;

const CODE_MAXLENGTH = 16383;

$code = null;

if (!empty($_GET["c"])) {
    $compressedCode = @base64_decode($_GET["c"], true);
    if ($compressedCode !== false) {
        $uncompressedCode = @gzinflate($compressedCode, CODE_MAXLENGTH);
        if ($compressedCode !== false) {
            $code = &$uncompressedCode;
        }
    }
}

$result = null;

if (isset($_POST["code"]) && strlen($_POST["code"]) <= CODE_MAXLENGTH) {
    header("Content-Type: application/json");

    $code = $_POST["code"];
    $csrf = $_POST["csrf"] ?? null;
    
    if ($csrf === null || $csrf !== $_SESSION["csrf-token"]) {
        echo json_encode([
            "ok" => false,
            "message" => "CSRF token mismatch",
            "csrf" => csrf_token(),
        ]);
        exit;
    }

    if (($_POST["permalink"] ?? "") === "1") {
        echo json_encode([
            "ok" => true,
            "data" => base64_encode(gzdeflate($code, 9, ZLIB_ENCODING_RAW)),
        ]);
        exit;
    }

    $id = getid();

    $runnerDir = __DIR__ . "/../_runners/$id";
    $srcDir = $runnerDir . "/src";

    mkdir($srcDir, recursive: true);

    file_put_contents($srcDir . "/Program.java", $code);

    $isInDocker = getenv("IS_IN_DOCKER") === "1";

    if ($isInDocker)
        file_put_contents("php://stderr", "[debug] In Docker\n");
    chdir(__DIR__ . "/..");

    $stderrFile = sys_get_temp_dir() . "/tmp{$id}.2";
    exec(
        ($isInDocker ? "su-exec root " : "") .
        "./compile-and-run.sh '$id' Program 2>'$stderrFile'", $resultStr, $code);
    $resultStr = implode("\n", $resultStr);
    if (is_file($stderrFile)) {
        $stderrContents = file_get_contents($stderrFile);
        unlink($stderrFile);
        if (!empty($stderrContents)) {
            file_put_contents("php://stderr", "compile-and-run.sh failed: $stderrContents\n");
            if ($code != 0) {
                echo json_encode([
                    "ok" => false,
                    "message" => "Execution failed:\n$stderrContents",
                    "csrf" => csrf_token(),
                ]);
                delTree($runnerDir);
                exit;
            }
        }
    }
    chdir(__DIR__);
    file_put_contents("php://stderr", "result: $result\n");

    $csrf = csrf_token();


    $result = [
        "compile" => [
            "status" => get($id, "c.s"),
            "stdout" => get($id, "c.0"),
            "stderr" => get($id, "c.1"),
        ],
        "run" => [
            "status" => get($id, "r.s"),
            "stdout" => get($id, "r.0"),
            "stderr" => get($id, "r.1"),
        ],
        "runner" => $id,
        "ok" => true,
        "csrf" => $csrf,
    ];

    if ($isInDocker)
        exec("su-exec root rm -rf '$runnerDir'");
    else
        delTree($runnerDir);

    echo json_encode($result);
    exit;
}

$csrf = csrf_token();

?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Java</title>
    <link rel="stylesheet" href="dist/codemirror.css">
    <link rel="stylesheet" href="dist/eclipse.css">
    <link rel="stylesheet" href="site.css?_=<?= file_cachebuster(__DIR__ . "/site.css") ?>">
    <script src="dist/jquery-1.12.4.min.js"></script>
    <script src="dist/codemirror.js"></script>
    <script src="dist/clike.js"></script>
    <script src="site.js?_=<?= file_cachebuster(__DIR__ . "/site.js") ?>"></script>
</head>
<body>
    <div id="loader" style="display: none;">
        <div id="spinner"></div>
    </div>
    <h1>&#9749;&#65039; Java Compiler</h1>
    <p>Powered by Eclipse Temurin</p>
    <form method="POST">
        <input name="csrf" id="csrf-token" type="hidden" value="<?= htmlentities($csrf) ?>">
        <textarea maxlength="<?= CODE_MAXLENGTH ?>" id="editor" name="code" rows="20"><?= htmlentities($code ?? $defaultCode) ?></textarea>
        <div id="toolbar">
            <button type="submit">&#9654;&#65039;&nbsp;Compile &amp; run</button>
            <button type="button" id="dlCodeButton">&#128190;&#65039;&nbsp;Download code as file</button>
            <button type="button" id="permalink">&#128279;&#65039;&nbsp;Permalink</button>
        </div>
    </form>
    <div id="output" style="display: none;"></div>
    <div id="footer">
        &copy; <?= date("Y") ?> <a href="https://jkohl.link/" target="_blank">Jonas Kohl</a>.
        <a href="https://git.jkohl.link/online-java-compiler.git" target="_blank">Source code</a>.
        <a href="javascript:;" id="toggleFullWidth">Toggle full width</a>
    </div>
</body>
</html>