""" Generates the syscall.rs code """
import argparse
import os
import sys
import json

# We need to map to major/minor/build to the names used in the syscall table
# https://www.gaijin.at/en/infos/windows-version-numbers
OS_VERSION_LOOKUP = {
    ("Windows Vista", "SP0"): (6, 0, 6000),
    ("Windows Vista", "SP1"): (6, 0, 6001),
    ("Windows Vista", "SP2"): (6, 0, 6002),
    ("Windows 7", "SP0"): (6, 1, 7600),
    ("Windows 7", "SP1"): (6, 1, 7601),
    ("Windows 8", "8.0"): (6, 2, 9200),
    ("Windows 8", "8.1"): (6, 3, 9600),
    ("Windows 10", "1507"): (10, 0, 10240),
    ("Windows 10", "1511"): (10, 0, 10586),
    ("Windows 10", "1607"): (10, 0, 14393),
    ("Windows 10", "1703"): (10, 0, 15063),
    ("Windows 10", "1709"): (10, 0, 16299),
    ("Windows 10", "1803"): (10, 0, 17134),
    ("Windows 10", "1809"): (10, 0, 17763),
    ("Windows 10", "1903"): (10, 0, 18362),
}

# I'm not supporting anything prior to vista here
UNSUPPORTED_OS = {"Windows XP", "Windows Server 2003"}

# these seem to have the same NT syscall as the consumer versions
SERVER_OS = {"Windows Server 2008", "Windows Server 2012"}

# how different are the syscall tables here?
OS_SERVER_LOOKUP = {
    ("Windows Server 2008", "SP0"): (6, 0, 6001),
    ("Windows Server 2008", "SP2"): (6, 0, 6002),
    ("Windows Server 2008", "R2"): (6, 1, 7600),
    ("Windows Server 2012", "SP0"): (6, 2, 9200),
    ("Windows Server 2012", "R2"): (6, 3, 9600),
    ("Windows Server 2008", "R2 SP1"): (6, 1, 7601),
}


def generate(inputpath, arch="x64", namefilter=lambda s: not s.startswith("NtWait")):
    nt = json.load(open(os.path.join(inputpath, arch, "json", "nt-per-system.json")))
    win32k = json.load(open(os.path.join(inputpath, arch, "json", "win32k-per-system.json")))
    outputpath = os.path.dirname(os.path.realpath(__file__))
    outputfilename = os.path.join(outputpath, f"syscalls_{arch}.rs")

    syscallnames = set()
    matches = []

    for ((osname, osversion), (major, minor, build)) in OS_VERSION_LOOKUP.items():
        syscalls = nt[osname][osversion]
        syscalls.update(win32k[osname][osversion])
        for syscallname, syscallnumber in syscalls.items():
            if namefilter and namefilter(syscallname):
                continue

            matches.append((major, minor, build, syscallnumber, syscallname))
            syscallnames.add(syscallname)

    matches.sort()
    syscallnames = sorted(syscallnames)

    print(outputfilename)
    with open(outputfilename, "w") as o:
        o.write(f"// Automatically generated by '{__file__}' - do not edit\n")
        if namefilter:
            o.write("// Note that this is a subset of the syscalls, run again with no namefilter\n")
            o.write("//   for the complete list (which takes forever to compile)\n")
        o.write("\n")
        o.write("#[allow(non_camel_case_types)]\n\n")

        o.write("pub enum Syscall {\n")
        for syscall in syscallnames:
            o.write(f"    {syscall},\n")
        o.write("}\n\n")

        o.write("pub fn lookup_syscall(major: u32, minor: u32, build: u32, syscall: u32)")
        o.write(" -> Option<Syscall> {\n")
        o.write("    match (major, minor, build, syscall) {\n")
        for major, minor, build, syscallnumber, syscallname in matches:
            o.write(f"        ({major}, {minor}, {build}, {syscallnumber})")
            o.write(f" => Some(Syscall::{syscallname}),\n")
        o.write("        _ => None\n")
        o.write("    }\n")
        o.write("}\n\n")


if __name__ == "__main__":

    if sys.platform.startswith("win"):
        default_syscall_path = os.path.join(os.getenv("userprofile"), "code", "windows-syscalls")
    else:
        default_syscall_path = os.path.join(os.getenv("HOME"), "code", "windows-syscalls")

    parser = argparse.ArgumentParser(
        description="generates syscall.rs", formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser.add_argument(
        "--syscallpath",
        type=str,
        default=default_syscall_path,
        dest="syscallpath",
        help="path to syscall repo https://github.com/j00ru/windows-syscalls",
    )
    args = parser.parse_args()

    if not os.path.isdir(args.syscallpath):
        print(f"Directory '{args.syscallpath}' doesn't exist!")
        print("Pass a valid path to the j00ru/windows-syscalls in with --syscallpath <pathname>")
        sys.exit(1)

    generate(args.syscallpath)
