"""Compile every locale/<lang>/LC_MESSAGES/django.po into a django.mo file.

This is a self-contained, pure-Python replacement for `manage.py compilemessages`
(GNU gettext / msgfmt is not installed on this machine). Run it after editing any
.po file:

    python compile_messages.py

Then restart the Django dev server so the new catalogs are loaded.
"""
import ast
import struct
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent
LOCALE_DIR = BASE_DIR / 'locale'


def parse_po(path):
    """Return {msgid: msgstr} for a .po file (handles multi-line quoted strings)."""
    entries = {}
    msgid = None
    msgstr = None
    target = None  # 'id' or 'str'
    buf = []

    def flush():
        nonlocal msgid, msgstr
        # Keep the empty-msgid entry too — it holds the catalog metadata header
        # (Content-Type / charset) that GNUTranslations reads.
        if msgid is not None and msgstr is not None:
            entries[msgid] = msgstr

    for raw in path.read_text(encoding='utf-8').splitlines():
        line = raw.strip()
        if not line or line.startswith('#'):
            continue
        if line.startswith('msgid '):
            flush()
            msgid = ast.literal_eval(line[len('msgid '):].strip())
            msgstr = None
            target = 'id'
        elif line.startswith('msgstr '):
            msgstr = ast.literal_eval(line[len('msgstr '):].strip())
            target = 'str'
        elif line.startswith('"'):
            piece = ast.literal_eval(line)
            if target == 'id':
                msgid += piece
            elif target == 'str':
                msgstr += piece
    flush()
    return entries


def make_mo(entries):
    """Serialize {msgid: msgstr} into GNU .mo binary format."""
    keys = sorted(k for k, v in entries.items() if v)
    offsets = []
    ids = b''
    strs = b''
    for k in keys:
        v = entries[k]
        kb = k.encode('utf-8')
        vb = v.encode('utf-8')
        offsets.append((len(ids), len(kb), len(strs), len(vb)))
        ids += kb + b'\x00'
        strs += vb + b'\x00'

    n = len(keys)
    keystart = 7 * 4 + 16 * n
    valuestart = keystart + len(ids)
    koffsets = []
    voffsets = []
    for o1, l1, o2, l2 in offsets:
        koffsets += [l1, o1 + keystart]
        voffsets += [l2, o2 + valuestart]

    output = struct.pack(
        'Iiiiiii',
        0x950412de,        # magic
        0,                 # version
        n,                 # number of entries
        7 * 4,             # start of key index
        7 * 4 + n * 8,     # start of value index
        0, 0,              # size/offset of hash table (unused)
    )
    output += struct.pack('i' * len(koffsets), *koffsets)
    output += struct.pack('i' * len(voffsets), *voffsets)
    output += ids
    output += strs
    return output


def main():
    compiled = 0
    for po_path in LOCALE_DIR.glob('*/LC_MESSAGES/django.po'):
        entries = parse_po(po_path)
        mo_path = po_path.with_suffix('.mo')
        mo_path.write_bytes(make_mo(entries))
        print(f'compiled {po_path.relative_to(BASE_DIR)} -> {len(entries)} strings')
        compiled += 1
    if not compiled:
        print('No .po files found under', LOCALE_DIR)


if __name__ == '__main__':
    main()
