#!/usr/bin/env python3

import os
from tqdm import tqdm

import ahocorasick_rs
from ahocorasick_rs import *

script_dir = os.path.dirname(os.path.realpath(__file__))
root_dir = os.path.join(script_dir, "..")
src_dir = os.path.join(root_dir, "src")
include_dir = os.path.join(root_dir, "include")
asm_dir = os.path.join(root_dir, "ver", "us", "asm")

renames = {}
patterns = []
deletes = []


def handle_file(f_path, try_rename_file=False):
    with open(f_path) as f:
        f_text_orig = f.read()

    if try_rename_file:
        extless = f_path.split("/")[-1][:-2]
        if extless in renames:
            deletes.append(f_path)
            f_path = f_path.replace(extless, renames[extless])

    # find all matches in one pass with aho-corasick algorithm
    f_text = f_text_orig
    matches = ac.find_matches_as_indexes(f_text)
    if matches:
        to_join = []
        pos = 0
        # replace all matches
        for match in matches:
            # head part
            to_join.append(f_text[pos : match[1]])
            to_replace = patterns[match[0]]
            to_join.append(renames[to_replace])
            pos = match[2]
        # tail part
        to_join.append(f_text[pos:])
        f_text = "".join(to_join)
        # save changes
        with open(f_path, "w", newline="\n") as f:
            f.write(f_text)


def apply_renames(file_path: str):
    global ac

    # Read input file
    # One valid whitespace-separated find-replace pair is given per line
    with open(file_path) as f:
        renames_text = f.readlines()

    # Create dict of old -> new names
    for line in renames_text:
        split = line.split()
        if len(split) == 2:
            if split[0] and split[1]:
                renames[split[0]] = split[1]
                patterns.append(split[0])
        elif len(split) != 0:
            raise Exception('input contains invalid rename pattern: \n"' + line.strip() + '"')

    ac = ahocorasick_rs.AhoCorasick(patterns, matchkind=MATCHKIND_LEFTMOST_LONGEST)

    # Walk through asm files and rename stuff
    print("Walking through asm files")
    asm_files = []
    for root, dirs, files in os.walk(asm_dir):
        for f_name in files:
            asm_files.append(os.path.join(root, f_name))
    for f_path in tqdm(asm_files):
        if f_path.endswith(".s"):
            handle_file(f_path, True)

    # Walk through src files and rename stuff
    print("Walking through src files")
    c_files = []
    for root, dirs, files in os.walk(src_dir):
        for f_name in files:
            c_files.append(os.path.join(root, f_name))
    for f_path in tqdm(c_files):
        if f_path.endswith(".c") or f_path.endswith(".h") or f_path.endswith(".yaml"):
            handle_file(f_path, True)

    # Walk through include files and rename stuff
    print("Walking through include files")
    for root, dirs, files in os.walk(include_dir):
        for f_name in files:
            f_path = os.path.join(root, f_name)

            handle_file(f_path)

    # Delete old versions of newly saved files
    print("Deleting old files")
    for d in deletes:
        os.remove(d)

    # Walk through asm dirs and rename stuff
    print("Walking through asm dirs")
    for root, dirs, files in os.walk(asm_dir):
        for dir_name in dirs:
            for rename in renames:
                if rename == dir_name:
                    os.rename(os.path.join(root, dir_name), os.path.join(root, renames[rename]))

    # Rename stuff in symbol_addrs.txt
    handle_file(os.path.join(root_dir, "ver", "current", "symbol_addrs.txt"))

    # splat.yaml
    handle_file(os.path.join(root_dir, "ver", "current", "splat.yaml"))


if __name__ == "__main__":
    from sys import argv

    for arg in argv[1:]:
        if arg == "--help":
            print("Usage: python3 rename.py [path/to/to_rename.txt...]")
            exit(0)

    paths = []
    if len(argv) > 1:
        paths = argv[1:]
    else:
        paths = [os.path.join(script_dir, "to_rename.txt")]
    for path in paths:
        apply_renames(path)
