#!/usr/bin/env python3
from __future__ import annotations

import argparse
import json
import os
import pprint
from pathlib import Path
from typing import Dict, Optional, Sequence


MODULES = {
    "box64rc": "box64rc.py",
    "usage": "usage.py",
    "model": "model.py",
    "configurator": "configurator.py",
}

UI_FILE = "configurator.ui"


def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Build the standalone box64-configurator script")
    parser.add_argument("--output", required=True, type=Path)
    parser.add_argument("--configurator-dir", required=True, type=Path)
    parser.add_argument("--usage", required=True, type=Path)
    parser.add_argument("--usage-cn", required=True, type=Path)
    return parser.parse_args(argv)


def read_modules(configurator_dir: Path) -> Dict[str, str]:
    return {
        name: (configurator_dir / filename).read_text(encoding="utf-8")
        for name, filename in MODULES.items()
    }


def read_texts(configurator_dir: Path) -> Dict[str, Dict[str, str]]:
    return {language: read_json(configurator_dir / f"text_{language}.json") for language in ("en", "zh")}


def read_ui(configurator_dir: Path) -> str:
    return (configurator_dir / UI_FILE).read_text(encoding="utf-8")


def read_json(path: Path):
    with path.open("r", encoding="utf-8") as file:
        return json.load(file)


def render_script(
    modules: Dict[str, str],
    texts: Dict[str, Dict[str, str]],
    ui: str,
    usage_data,
    usage_cn_data,
) -> str:
    return f"""#!/usr/bin/env python3
# Generated by configurator/build_standalone.py. Do not edit this file directly.
from __future__ import annotations

import sys
import types

_MODULES = {pprint.pformat(modules, width=120)}
_EMBEDDED_TEXTS = {pprint.pformat(texts, width=120)}
_EMBEDDED_UI = {pprint.pformat(ui, width=120)}
_EMBEDDED_USAGE_DATA = {pprint.pformat(usage_data, width=120)}
_EMBEDDED_USAGE_CN_DATA = {pprint.pformat(usage_cn_data, width=120)}


def _load_module(name: str) -> types.ModuleType:
    module = types.ModuleType(name)
    module.__file__ = f"<box64-configurator:{{name}}>"
    module.__package__ = ""
    if name == "usage":
        module._EMBEDDED_USAGE_DATA = _EMBEDDED_USAGE_DATA
        module._EMBEDDED_USAGE_CN_DATA = _EMBEDDED_USAGE_CN_DATA
    sys.modules[name] = module
    exec(compile(_MODULES[name], module.__file__, "exec"), module.__dict__)
    return module


for _name in ("box64rc", "usage", "model"):
    _load_module(_name)

_app = types.ModuleType("box64_configurator_app")
_app.__file__ = __file__
_app.__package__ = ""
_app._EMBEDDED_TEXTS = _EMBEDDED_TEXTS
_app._EMBEDDED_UI = _EMBEDDED_UI
sys.modules["box64_configurator_app"] = _app
exec(compile(_MODULES["configurator"], __file__, "exec"), _app.__dict__)

if __name__ == "__main__":
    raise SystemExit(_app.main())
"""


def main(argv: Optional[Sequence[str]] = None) -> int:
    args = parse_args(argv)
    configurator_dir = args.configurator_dir.resolve()
    output = args.output.resolve()

    script = render_script(
        read_modules(configurator_dir),
        read_texts(configurator_dir),
        read_ui(configurator_dir),
        read_json(args.usage),
        read_json(args.usage_cn),
    )

    output.parent.mkdir(parents=True, exist_ok=True)
    output.write_text(script, encoding="utf-8")
    os.chmod(output, 0o755)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
