#!/usr/bin/env python3

import argparse
import platform

device_ids = {
    "k:0000:0000", # cros_ec keyboard
    "k:0001:0001", # AT keyboard
    "k:18d1:503c", # Google Inc. Hammer
    "k:18d1:5050", # Google Inc. Hammer
    "k:18d1:504c", # Google Inc. Hammer
    "k:18d1:5052", # Google Inc. Hammer
    "k:18d1:5057", # Google Inc. Hammer
    "k:18d1:505b", # Google Inc. Hammer
    "k:18d1:5030", # Google Inc. Hammer
    "k:18d1:503d", # Google Inc. Hammer
    "k:18d1:5044", # Google Inc. Hammer
    "k:18d1:5061", # Google Inc. Hammer
    "k:18d1:502b", # Google Inc. Hammer
}

vivaldi_keys = {
    "x86_64": {
        "90": "previoussong",
        "91": "zoom",
        "92": "scale",
        "93": "print",
        "94": "brightnessdown",
        "95": "brightnessup",
        "97": "kbdillumdown",
        "98": "kbdillumup",
        "99": "nextsong",
        "9A": "playpause",
        "9B": "micmute",
        "9E": "kbdillumtoggle",
        "A0": "mute",
        "AE": "volumedown",
        "B0": "volumeup",
        "E9": "forward",
        "EA": "back",
        "E7": "refresh",
    },
    "arm": {
        "158": "back",
        "159": "forward",
        "173": "refresh",
        "372": "zoom",
        "120": "scale",
        "224": "brightnessdown",
        "225": "brightnessup",
        "113": "mute",
        "114": "volumedown",
        "115": "volumeup",
        "99" : "print",
    }
}

def get_arch():
    return platform.uname().machine

def get_ids_string(device_ids):
    return "\n".join(device_ids)

def get_dt_layout():
    keys = []
    keycodes = []

    fdt = libfdt.Fdt(open("/sys/firmware/fdt", "rb").read())
    currentnode = fdt.first_subnode(0)

    while True:
        try:
            if fdt.get_name(currentnode) == "keyboard-controller":
                prop = fdt.getprop(currentnode, "linux,keymap")
                keys = prop.as_uint32_list()
            currentnode = fdt.next_node(currentnode, 10)[0]
        except:
            break

    if not keys:
        return ""

    for key in keys:
        keycode = str(key & 0xFFFF)
        if keycode in vivaldi_keys["arm"]:
            keycodes.append(keycode)
    return keycodes

def get_physmap_data():
    if get_arch() == "x86_64":
        try:
            with open("/sys/bus/platform/devices/i8042/serio0/function_row_physmap", "r") as file:
                return file.read().strip().split()
        except FileNotFoundError:
            return ""
    else:
        return get_dt_layout()

def get_functional_row(physmap, use_vivaldi, super_is_held, super_inverted):
    arch = get_arch()
    if arch != "x86_64":
        arch = "arm"

    i = 0
    result = ""
    for code in physmap:
        i += 1
        # Map zoom to f11 since most applications wont listen to zoom
        mapping = "f11" if vivaldi_keys[arch][code] == "zoom" \
            else vivaldi_keys[arch][code]

        match [super_is_held, use_vivaldi, super_inverted]:
            case [True, True, False] | [False, True, True]:
                result += f"{vivaldi_keys[arch][code]} = f{i}\n"
            case [True, False, False] | [False, False, True]:
                result += f"f{i} = f{i}\n"
            case [False, True, False] | [True, True, True]:
                result += f"{vivaldi_keys[arch][code]} = {mapping}\n"
            case [False, False, False] | [True, False, True]:
                result += f"f{i} = {mapping}\n"

    return result

def get_keyd_config(physmap, inverted):
    config = f"""\
[ids]
{get_ids_string(device_ids)}

[main]
{get_functional_row(physmap, use_vivaldi=False, super_is_held=False, super_inverted=inverted)}
{get_functional_row(physmap, use_vivaldi=True, super_is_held=False, super_inverted=inverted)}
f13=coffee
sleep=coffee

[meta]
{get_functional_row(physmap, use_vivaldi=False, super_is_held=True, super_inverted=inverted)}
{get_functional_row(physmap, use_vivaldi=True, super_is_held=True, super_inverted=inverted)}

[alt]
backspace = delete
brightnessdown = kbdillumdown
brightnessup = kbdillumup
f6 = kbdillumdown
f7 = kbdillumup

[control]
f5 = print
scale = print

[control+alt]
backspace = C-A-delete
"""
    return config

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-f", "--file", default="cros.conf", help="path to save config (default: cros.conf)")
    parser.add_argument("-i", "--inverted", action="store_true", 
                        help="use functional keys by default and media keys when super is held")
    args = vars(parser.parse_args())

    

    physmap = get_physmap_data()
    if not physmap:
        print("no function row mapping found, using default mapping")
        if get_arch() == "x86_64":
            physmap = ['EA', 'E9', 'E7', '91', '92', '94', '95', 'A0', 'AE', 'B0']
        else:
            physmap = ['158', '159', '173', '372', '120', '224', '225', '113', '114', '115']
    
    config = get_keyd_config(physmap, args["inverted"])
    with open(args["file"], "w") as conf:
        conf.write(config)

if __name__ == "__main__":
    if get_arch() != "x86_64":
        import libfdt
    main()
