"""
Compile and run the primes.mixal sample program using mixvm and mixguile.
"""

import os
import pathlib
import subprocess
import sys
import tempfile

from typing import Dict


MIXASM = os.environ.get("TEST_MIXASM_PROG", "mixasm")
MIXVM = os.environ.get("TEST_MIXVM_PROG", "mixvm")
MIXGUILE = os.environ.get("TEST_MIXGUILE_PROG", "mixguile")
MIXLOCAL = os.environ.get("TEST_MIX_USE_LOCAL", "0") == "1"

SRCDIR = pathlib.Path(os.environ.get("TEST_SRCDIR", ".")).absolute()
SRCFILE = SRCDIR / "samples/primes.mixal"

ENC = "us-ascii"


def prepare_home_dir(home_dir: pathlib.Path) -> None:
    """Create any necessary files in the temporary home directory."""
    home_dir.mkdir(mode=0o755)

    if MIXLOCAL:
        guile_dir = SRCDIR / "mixguile"
        mdk_dir = home_dir / ".mdk"
        mdk_dir.mkdir(mode=0o755)
        for path in sorted(guile_dir.glob("*.scm")):
            subprocess.check_call(
                ["cp", "--", path, mdk_dir],
                shell=False,
            )


def examine_output(fname: pathlib.Path) -> None:
    """Examine the primes output file."""
    print(f"Examining {fname}")
    with fname.open(mode="r", encoding=ENC) as printer_file:
        line = printer_file.readline()
        assert line.rstrip() == "FIRST FIVE HUNDRED PRIMES"

        found = {
            "0002": False,
            "0004": False,
            "0239": False,
            "0048": False,
        }
        print(f'Looking for {" ".join(sorted(found.keys()))}')
        for line in printer_file.readlines():
            words = set(line.split())
            for num, value in found.items():
                if num in words:
                    assert not value
                    found[num] = True

        print(f"Result: {found}")
        assert found == {
            "0002": True,
            "0004": False,
            "0239": True,
            "0048": False,
        }


def run_program(
    program: str,
    program_dir: pathlib.Path,
    commands: str,
    mix_env: Dict[str, str],
) -> None:
    """Create a subdir and input and output files, run a program."""
    program_dir.mkdir(mode=0o755)
    infile = program_dir / "in.txt"
    infile.write_text(commands, encoding=ENC)
    print(f"Created {infile}")
    subprocess.check_call(["cat", "--", infile])

    outfile = program_dir / "out.txt"
    print(f"Invoking {program} in {infile} out {outfile}")
    subprocess.check_call(
        [program],
        shell=False,
        env=mix_env,
        stdin=infile.open(mode="r"),
        stdout=outfile.open(mode="w"),
    )
    print(f"Got {program} output:")
    subprocess.check_call(["cat", "--", outfile])

    assert sorted(path.name for path in program_dir.glob("*")) == [
        "in.txt",
        "out.txt",
        "printer.dev",
    ]
    printer_dev = program_dir / "printer.dev"
    assert printer_dev.is_file()
    examine_output(printer_dev)


def test_primes() -> None:
    """Primes sample program test: compile, run, check."""
    print("")
    if not SRCFILE.is_file():
        sys.exit(f'Not a file: "{SRCFILE}"; set TEST_SRCDIR?')

    with tempfile.TemporaryDirectory() as tmpdir_name:
        tmpdir = pathlib.Path(tmpdir_name)
        print(f"Using temporary directory {tmpdir}")

        home_dir = tmpdir / "home"
        mix_env = dict(os.environ)
        mix_env.update({"HOME": str(home_dir)})

        program_base = tmpdir / "primes"
        program_full = program_base.with_suffix(".mix")
        print(f"Running {MIXASM} on {SRCFILE} to create {program_full}")
        subprocess.check_call([MIXASM, "-o", program_base, "--", SRCFILE])
        print(f"Checking for {program_full}")
        assert sorted(path.name for path in tmpdir.glob("*")) == ["primes.mix"]
        assert program_full.is_file()

        prepare_home_dir(home_dir)
        run_program(
            MIXVM,
            tmpdir / "mixvm",
            f"""load {program_base}
sddir {tmpdir}/mixvm
run""",
            mix_env,
        )

        assert sorted(path.name for path in tmpdir.glob("*")) == [
            "home",
            "mixvm",
            "primes.mix",
        ]

        run_program(
            MIXGUILE,
            tmpdir / "mixguile",
            f"""(mix-load "{program_base}")
(mix-sddir "{tmpdir}/mixguile")
(mix-run)""",
            mix_env,
        )

        assert sorted(path.name for path in tmpdir.glob("*")) == [
            "home",
            "mixguile",
            "mixvm",
            "primes.mix",
        ]
