﻿import os
import re
import zipfile
import struct

# --- Configuration ---
REPORT_PATH = "tcreport.txt"
ZIP_PATH = "roms/c64_cass/10hits2.zip"

# Default C64 Clock Frequencies
DEFAULT_PAL = 985248.0
DEFAULT_NTSC = 1022730.0

# Global variables for current frequencies
CLK_PAL = DEFAULT_PAL
CLK_NTSC = DEFAULT_NTSC

def get_tap_path_from_zip(zip_path):
    if not os.path.exists(zip_path):
        return None
    with zipfile.ZipFile(zip_path, 'r') as z:
        taps = [f for f in z.namelist() if f.lower().endswith('.tap')]
        return taps[0] if taps else None

def read_tap_header(zip_path, tap_internal_path):
    try:
        with zipfile.ZipFile(zip_path, 'r') as z:
            with z.open(tap_internal_path) as f:
                header = f.read(20)
                if len(header) < 20:
                    return None, None
                version = header[12]
                video_standard = header[14]
                return version, video_standard
    except Exception as e:
        print(f"Error reading TAP header: {e}")
        return None, None

def calculate_time_from_tap(zip_path, tap_internal_path, byte_offset, tap_version, pal_freq, ntsc_freq):
    if not zip_path or not tap_internal_path:
        return 0.0, 0.0

    try:
        with zipfile.ZipFile(zip_path, 'r') as z:
            with z.open(tap_internal_path) as f:
                total_cycles = 0
                current_pos = 0
                bytes_to_read = byte_offset

                while bytes_to_read > 0:
                    byte = f.read(1)
                    if not byte:
                        break
                    byte_val = byte[0]
                    bytes_to_read -= 1
                    current_pos += 1

                    if byte_val != 0:
                        total_cycles += byte_val * 8
                    else:
                        if tap_version == 1 and bytes_to_read >= 3:
                            cycle_bytes = f.read(3)
                            if len(cycle_bytes) == 3:
                                exact_cycles = struct.unpack('<I', cycle_bytes + b'\x00')[0] & 0xFFFFFF
                                total_cycles += exact_cycles
                                bytes_to_read -= 3
                                current_pos += 3
                        else:
                            total_cycles += 20000

                return total_cycles / pal_freq, total_cycles / ntsc_freq

    except Exception as e:
        print(f"Error reading TAP binary: {e}")
        return 0.0, 0.0

def parse_tcreport():
    if not os.path.exists(REPORT_PATH):
        print(f"Error: '{REPORT_PATH}' not found.")
        return []

    with open(REPORT_PATH, 'r', encoding='utf-8') as f:
        text = f.read()

    results = []
    lines = text.split('\n')
    in_header_block = False
    current_loc_hex = None
    current_loc_dec = None
    current_name = None

    for line in lines:
        s = line.strip()
        if s.startswith("Seq. no.:"):
            if in_header_block and current_name and current_loc_dec:
                results.append({"hex": current_loc_hex, "dec": current_loc_dec, "name": current_name})
            in_header_block = False
            current_loc_hex = None
            current_loc_dec = None
            current_name = None
            continue

        if s == "File Type: C64 ROM-TAPE HEADER":
            in_header_block = True

        if in_header_block:
            if s.startswith("Location:"):
                match = re.search(r'\$([0-9A-Fa-f]+)', s)
                if match:
                    current_loc_hex = match.group(1).upper()
                    current_loc_dec = int(current_loc_hex, 16)
            if s.startswith("File Name:"):
                parts = s.split(":", 1)
                if len(parts) > 1: current_name = parts[1].strip()

    if in_header_block and current_name and current_loc_dec:
        results.append({"hex": current_loc_hex, "dec": current_loc_dec, "name": current_name})

    return results

def format_time(seconds):
    if seconds < 0: seconds = 0
    return f"{int(seconds):04d}"

def main():
    global CLK_PAL, CLK_NTSC

    # Pre-calculate data
    files = parse_tcreport()
    if not files:
        return

    seen = set()
    unique_files = []
    for f in files:
        if f['name'] not in seen:
            seen.add(f['name'])
            unique_files.append(f)

    tap_internal = get_tap_path_from_zip(ZIP_PATH)
    if not tap_internal:
        print(f"Error: No .tap file found in {ZIP_PATH}")
        return

    tap_version, video_standard = read_tap_header(ZIP_PATH, tap_internal)
    if tap_version is None:
        print("Error: Could not read TAP file version.")
        return

    # Extract just the filename from the full path
    tap_filename = os.path.basename(tap_internal)

    def show_files():
        print(f"Archive: {ZIP_PATH}")
        print(f"File:    {tap_filename}")
        print(f"\n{'#':<2} {'File Name':<23} {'Location':<10} {'PAL Counter':<12} {'NTSC Counter':<12}")
        print("-" * 62)
        base_times = []
        for f in unique_files:
            t_pal, t_ntsc = calculate_time_from_tap(ZIP_PATH, tap_internal, f['dec'], tap_version, CLK_PAL, CLK_NTSC)
            base_times.append({"name": f['name'], "hex": f['hex'], "pal": t_pal, "ntsc": t_ntsc})

        for i, item in enumerate(base_times):
            if i == 0:
                final_pal = 0.0
                final_ntsc = 0.0
            else:
                final_pal = item['pal']
                final_ntsc = item['ntsc']
            print(f"{i+1:<2} {item['name']:<23} ${item['hex']:<9} {format_time(final_pal):<12} {format_time(final_ntsc):<12}")

    # Show the list immediately
    show_files()

    while True:
        print("\n--- Tape Counter Calculator ---")
        print(f"Current Frequencies: PAL={CLK_PAL:.0f} Hz, NTSC={CLK_NTSC:.0f} Hz")
        print("1. Show File List (First entry locked to 0000)")
        print("2. Set New PAL Frequency")
        print("3. Set New NTSC Frequency")
        print("4. Reset Frequencies to Default")
        print("5. Quit")

        choice = input("\nSelect option: ")

        if choice == '5':
            break
        elif choice == '2':
            try:
                new_freq = float(input(f"Enter new PAL frequency (current {CLK_PAL:.0f} Hz): "))
                if new_freq > 0:
                    CLK_PAL = new_freq
                    print(f"PAL frequency set to {CLK_PAL:.0f} Hz.")
                else:
                    print("Frequency must be greater than 0.")
            except ValueError:
                print("Invalid input. Please enter a number.")
        elif choice == '3':
            try:
                new_freq = float(input(f"Enter new NTSC frequency (current {CLK_NTSC:.0f} Hz): "))
                if new_freq > 0:
                    CLK_NTSC = new_freq
                    print(f"NTSC frequency set to {CLK_NTSC:.0f} Hz.")
                else:
                    print("Frequency must be greater than 0.")
            except ValueError:
                print("Invalid input. Please enter a number.")
        elif choice == '4':
            CLK_PAL = DEFAULT_PAL
            CLK_NTSC = DEFAULT_NTSC
            print(f"Frequencies reset to default. PAL={CLK_PAL:.0f} Hz, NTSC={CLK_NTSC:.0f} Hz.")
        elif choice == '1':
            show_files()
        else:
            print("Invalid option.")

if __name__ == "__main__":
    main()
