#!/usr/bin/env python3
"""Apply our small XIAO configuration to a fresh, pinned grblHAL checkout.

Does not flash hardware. All changes are confined to the supplied checkout.
Run before CMake configuration. See the HTML wiring guide for build commands.
"""
import argparse
from pathlib import Path
import shutil
import subprocess

REVISION = "2f5bc4a06ecea3b1f78c1644623810f9b6416376"


def replace_once(text, old, new):
    if new in text and old not in text.replace(new, ''):
        return text
    if text.count(old) == 1:
        return text.replace(old, new, 1)
    if old not in text and new in text:
        return text
    else:
        raise ValueError(f"Expected one upstream match: {old!r}")


def configure(source):
    revision = subprocess.check_output(
        ["git", "-C", str(source), "rev-parse", "HEAD"], text=True).strip()
    if revision != REVISION:
        raise ValueError(f"Use upstream revision {REVISION}; found {revision}")
    here = Path(__file__).resolve().parent
    machine_path = source / "my_machine.h"
    original_machine = subprocess.check_output(
        ["git", "-C", str(source), "show", "HEAD:my_machine.h"], text=True)
    configured_machine = '#include "plotter_profile.h"\n'
    if machine_path.read_text() not in (original_machine, configured_machine):
        raise ValueError("Refusing to overwrite a modified my_machine.h. Use a fresh checkout.")
    cmake_path = source / "CMakeLists.txt"
    cmake = cmake_path.read_text()
    # Upstream's first MCU check recognizes only Pico/Pico W by board name.
    cmake = replace_once(cmake,
        'if(PICO_BOARD STREQUAL "pico" OR PICO_BOARD STREQUAL "pico_w")',
        'if(PICO_PLATFORM STREQUAL "rp2040" OR PICO_BOARD STREQUAL "pico" OR PICO_BOARD STREQUAL "pico_w")')
    marker = '# Rwanda plotter profile (included before core defaults in every translation unit)'
    if marker not in cmake:
        cmake += '\n' + marker + '\ntarget_compile_options(grblHAL PRIVATE -include "${CMAKE_CURRENT_LIST_DIR}/plotter_profile.h")\n'
    servo_path = source / "plugins/pwm_servo_m280.c"
    servo = servo_path.read_text()
    # A narrower initial pulse range; fit the horn with servo power disconnected.
    servo = replace_once(servo, '#define DEFAULT_MIN_PULSE_WIDTH 544e-6',
                         '#define DEFAULT_MIN_PULSE_WIDTH 1000e-6')
    servo = replace_once(servo, '#define DEFAULT_MAX_PULSE_WIDTH 2400e-6',
                         '#define DEFAULT_MAX_PULSE_WIDTH 2000e-6')
    servo = replace_once(servo, 'pwm_servo_set_angle(n_servos++, 0.0f);',
                         'pwm_servo_set_angle(n_servos++, 90.0f);')
    driver_path = source / "driver.c"
    driver = driver_path.read_text()
    driver = replace_once(driver, '    add_limit_pin(Z)\n',
                          '#ifdef Z_LIMIT_PIN\n    add_limit_pin(Z)\n#endif\n')
    driver = replace_once(driver, '    signals.min.z = DIGITAL_IN(Z_LIMIT_PIN);\n',
                          '#ifdef Z_LIMIT_PIN\n    signals.min.z = DIGITAL_IN(Z_LIMIT_PIN);\n#endif\n')
    copies = [(here / "my_machine_map.h", source / "boards/my_machine_map.h"),
              (here / "plotter_profile.h", source / "plotter_profile.h")]
    for origin, dest in copies:
        if dest.exists() and dest.read_bytes() != origin.read_bytes():
            raise ValueError(f"Refusing to overwrite an existing different file: {dest}")
    # Validate every patch and destination before changing the checkout.
    for origin, dest in copies:
        shutil.copyfile(origin, dest)
    machine_path.write_text(configured_machine)
    cmake_path.write_text(cmake)
    servo_path.write_text(servo)
    driver_path.write_text(driver)
    print("Configured XIAO RP2040, USB, X/Y motion and M280 servo. No device flashed.")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("source", type=Path, help="Fresh grblHAL/RP2040 checkout")
    configure(parser.parse_args().source.resolve())
