<?php
/**
 * Standalone PO -> MO compiler
 * Reads flowdino-fr_FR.po and writes flowdino-fr_FR.mo next to it.
 */

$po_file = __DIR__ . '/flowdino-fr_FR.po';
$mo_file = __DIR__ . '/flowdino-fr_FR.mo';

if (!file_exists($po_file)) {
    fwrite(STDERR, "ERROR: PO file not found: $po_file\n");
    exit(1);
}

$po_content = file_get_contents($po_file);
$lines      = explode("\n", $po_content);

$translations = [];  // msgid => msgstr
$cur_id  = null;
$cur_str = null;
$in_id   = false;
$in_str  = false;

$unquote = function(string $s): string {
    // Strip surrounding quotes and unescape sequences
    if (strlen($s) >= 2 && $s[0] === '"' && $s[-1] === '"') {
        $s = substr($s, 1, -1);
    }
    return stripcslashes($s);
};

foreach ($lines as $line) {
    $line = rtrim($line);

    if ($line === '' || $line[0] === '#') {
        // Flush current entry on blank line or comment
        if ($cur_id !== null && $cur_str !== null && $cur_id !== '') {
            $translations[$cur_id] = $cur_str;
        }
        if ($line === '') {
            $cur_id  = null;
            $cur_str = null;
            $in_id   = false;
            $in_str  = false;
        }
        continue;
    }

    if (preg_match('/^msgid\s+"(.*)"$/', $line, $m)) {
        if ($cur_id !== null && $cur_str !== null && $cur_id !== '') {
            $translations[$cur_id] = $cur_str;
        }
        $cur_id  = stripcslashes($m[1]);
        $cur_str = null;
        $in_id   = true;
        $in_str  = false;
        continue;
    }

    if (preg_match('/^msgstr\s+"(.*)"$/', $line, $m)) {
        $cur_str = stripcslashes($m[1]);
        $in_id   = false;
        $in_str  = true;
        continue;
    }

    // Continuation line
    if (preg_match('/^"(.*)"$/', $line, $m)) {
        $cont = stripcslashes($m[1]);
        if ($in_id && $cur_id !== null) {
            $cur_id .= $cont;
        } elseif ($in_str && $cur_str !== null) {
            $cur_str .= $cont;
        }
        continue;
    }
}
// Flush last entry
if ($cur_id !== null && $cur_str !== null && $cur_id !== '') {
    $translations[$cur_id] = $cur_str;
}

// Remove header entry (empty msgid)
unset($translations['']);

$count  = count($translations);
$keys   = array_keys($translations);
$values = array_values($translations);

// Build MO binary (little-endian, revision 0)
// Header: magic, revision, count, key_offset, val_offset, hashtab_size, hashtab_offset
$key_offset = 28 + $count * 16; // after header (28 bytes) + descriptor table ($count * 2 * 8 bytes)
$val_offset = $key_offset;      // recalculated below after placing keys

// Calculate key string block
$key_block  = '';
$key_descs  = [];
$off = $key_offset;
foreach ($keys as $k) {
    $key_descs[] = [strlen($k), $off];
    $key_block   .= $k . "\0";
    $off         += strlen($k) + 1;
}

$val_offset_start = $off;
$val_block  = '';
$val_descs  = [];
foreach ($values as $v) {
    $val_descs[] = [strlen($v), $off];
    $val_block   .= $v . "\0";
    $off         += strlen($v) + 1;
}

$mo  = pack('V', 0x950412de);  // magic LE
$mo .= pack('V', 0);           // revision
$mo .= pack('V', $count);      // number of strings
$mo .= pack('V', 28);          // offset of key descriptor table
$mo .= pack('V', 28 + $count * 8); // offset of value descriptor table
$mo .= pack('V', 0);           // hashtable size (unused)
$mo .= pack('V', 0);           // hashtable offset (unused)

foreach ($key_descs as $d) {
    $mo .= pack('V', $d[0]) . pack('V', $d[1]);
}
foreach ($val_descs as $d) {
    $mo .= pack('V', $d[0]) . pack('V', $d[1]);
}
$mo .= $key_block;
$mo .= $val_block;

if (file_put_contents($mo_file, $mo) !== false) {
    echo "OK: $count translations compiled to $mo_file\n";
} else {
    fwrite(STDERR, "ERROR: could not write $mo_file\n");
    exit(1);
}
