#!/usr/bin/env python

import os
import re
import sys

# === CONFIGURATION ===

# Mappings of invalid identifiers (starting with digits) to valid ones.
# Generated based on the provided 'nopscad.txt' list.
REPLACEMENTS = {
    # Headers and Joiners (Move dimension to suffix)
    "2p54header": "header_2p54",
    "2p54joiner": "joiner_2p54",

    # 1-2-3 Blocks (Invert name for clarity)
    "123_block": "block_123",
    "102040_block": "block_102040",
    "123_block_stand_stl": "block_123_stand_stl",

    # Bins
    "1x1_bin_stl": "bin_1x1_stl",

    # Stepper Motors (Add 'stepper_' prefix)
    "28BYJ_48": "stepper_28BYJ_48",
    "35BYGHJ75": "stepper_35BYGHJ75",

    # 2-Screw Blocks (Spell out 'two')
    "2screw_block": "two_screw_block",
    "2screw_block_holes": "two_screw_block_holes",
    "2screw_block_width": "two_screw_block_width",
    "2screw_block_v_hole": "two_screw_block_v_hole",
    "2screw_block_h_hole": "two_screw_block_h_hole",
    "2screw_block_assembly": "two_screw_block_assembly",
    "2screw_block_M20_stl": "two_screw_block_M20_stl",
    "2screw_block_M25_stl": "two_screw_block_M25_stl",
    "2screw_block_M30_stl": "two_screw_block_M30_stl",
    "2screw_block_M40_stl": "two_screw_block_M40_stl",
    "2screw_block_M20_s_stl": "two_screw_block_M20_s_stl",
    "2screw_block_M25_s_stl": "two_screw_block_M25_s_stl",
    "2screw_block_M30_s_stl": "two_screw_block_M30_s_stl",
    "2screw_block_M40_s_stl": "two_screw_block_M40_s_stl",
    "2screw_block_M20_assembly": "two_screw_block_M20_assembly",
    "2screw_block_M20_s_assembly": "two_screw_block_M20_s_assembly",
    "2screw_block_M25_assembly": "two_screw_block_M25_assembly",
    "2screw_block_M25_s_assembly": "two_screw_block_M25_s_assembly",
    "2screw_block_M40_assembly": "two_screw_block_M40_assembly",
    "2screw_block_M40_s_assembly": "two_screw_block_M40_s_assembly",

    # 7-Segment Displays (Spell out 'seven')
    "7_segment_digit": "seven_segment_digit",
    "7_segment_digits": "seven_segment_digits",
    "7_segments": "seven_segments",
    "7_segment_size": "seven_segment_size",
    "7_segment_digit_size": "seven_segment_digit_size",
    "7_segment_pins": "seven_segment_pins",
    "7_segment_pin_pitch": "seven_segment_pin_pitch",

    # Discs (Move dimension to suffix)
    "6p4mm_disc": "disc_6p4mm",
    "10mm_disc": "disc_10mm",

    # Opaque Part Numbers (Prefix with 'part_')
    "02531A": "part_02531A",
    "02352A": "part_02352A",
    "5_160430_7": "part_5_160430_7",

    # Faceplates
    "80ST_faceplate": "faceplate_80ST"
}

def process_file(filepath):
    """
    Reads a file, performs regex replacements for all keys in REPLACEMENTS,
    and writes the file back if changes were made.
    """
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            original_content = f.read()
    except UnicodeDecodeError:
        print(f"Skipping binary or non-utf8 file: {filepath}")
        return

    new_content = original_content
    changes_made = False

    # Sort keys by length (descending) to ensure we don't partial-match
    # if one key is a substring of another (though \b helps prevent this).
    sorted_keys = sorted(REPLACEMENTS.keys(), key=len, reverse=True)

    for old_id in sorted_keys:
        new_id = REPLACEMENTS[old_id]

        # Regex explanation:
        # \b       : Word boundary (ensures we don't match inside another word)
        # old_id   : The identifier to replace
        # \b       : Word boundary
        pattern = r'\b' + re.escape(old_id) + r'\b'

        # Check if exists before replacing to save processing
        if re.search(pattern, new_content):
            new_content = re.sub(pattern, new_id, new_content)
            changes_made = True

    if changes_made:
        print(f"Updating: {filepath}")
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(new_content)

def main():
    target_dir = "."
    if len(sys.argv) > 1:
        target_dir = sys.argv[1]

    print(f"Scanning directory: {os.path.abspath(target_dir)}")
    print("Replacing deprecated numeric identifiers...")

    count = 0
    for root, dirs, files in os.walk(target_dir):
        for file in files:
            if file.endswith(".scad"):
                filepath = os.path.join(root, file)
                process_file(filepath)
                count += 1

    print(f"\nDone. Scanned {count} .scad files.")

if __name__ == "__main__":
    main()
