import argparse
import git
import os
import re

def collect_non_matching_funcs():
    result = []
    for root, dirs, files in os.walk('src'):
        for file in files:
            if file.endswith('.c'):
                with open(os.path.join(root, file), 'r') as f:
                    data = f.read()
                    # Find all NONMATCH and ASM_FUNC macros
                    for match in re.findall(r'(NONMATCH|ASM_FUNC)\(".*",\W*\w*\W*(\w*).*\)', data):
                        result.append(match)
    return result


def parse_map(non_matching_funcs):
    src = 0
    asm = 0
    src_data = 0
    data = 0
    non_matching = 0

    with open('tmc.map', 'r') as map:
        # Skip to the linker script section
        line = map.readline()
        while not line.startswith('Linker script and memory map'):
            line = map.readline()
        while not line.startswith('rom'):
            line = map.readline()

        prev_symbol = None
        prev_addr = 0
        for line in map:
            if line.startswith(' .'):
                arr = line.split()
                section = arr[0]
                size = int(arr[2], 16)
                filepath = arr[3]
                dir = filepath.split('/')[0]

                if section == '.text':
                    if dir == 'src':
                        src += size
                    elif dir == 'asm':
                        asm += size
                    elif dir == 'data':
                        # scripts
                        data += size
                    elif dir == '..':
                        # libc
                        src += size
                elif section == '.rodata':
                    if dir == 'src':
                        src_data += size
                    elif dir == 'data':
                        data += size

            elif line.startswith('  '):
                arr = line.split()
                if len(arr) == 2 and arr[1] != '':  # It is actually a symbol

                    if prev_symbol in non_matching_funcs:
                        # Calculate the length for non matching function
                        non_matching += int(arr[0], 16) - prev_addr

                    prev_symbol = arr[1]
                    prev_addr = int(arr[0], 16)
            elif line.strip() == '':
                # End of linker script section
                break

    src -= non_matching
    asm += non_matching

    return (src, asm, src_data, data)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-m', '--matching', dest='matching', action='store_true',
                        help='Output matching progress instead of decompilation progress')
    args = parser.parse_args()
    matching = args.matching

    non_matching_funcs = []
    funcs = collect_non_matching_funcs()
    if matching:
        # Remove all non matching funcs from count
        non_matching_funcs = [x[1] for x in funcs]
    else:
        # Only remove ASM_FUNC functions from count
        for func in funcs:
            if func[0] == 'ASM_FUNC':
                non_matching_funcs.append(func[1])

    (src, asm, src_data, data) = parse_map(non_matching_funcs)

    total = src + asm
    data_total = src_data + data

    src_pct = '%.4f' % (100 * src / total)
    asm_pct = '%.4f' % (100 * asm / total)

    src_data_pct = '%.4f' % (100 * src_data / data_total)
    data_pct = '%.4f' % (100 * data / data_total)

    version = 1
    git_object = git.Repo().head.object
    timestamp = str(git_object.committed_date)
    git_hash = git_object.hexsha

    csv_list = [str(version), timestamp, git_hash, str(src_pct),
                str(asm_pct), str(src_data_pct), str(data_pct)]

    print(','.join(csv_list))


if __name__ == '__main__':
    main()
