#!/usr/bin/env python3
"""
Wrapper script to run FrCheck with context from framecpp_dump_objects
This helps diagnose FrameCPP vs FrameL compatibility issues
"""

import sys
import subprocess
import os
import re
from pathlib import Path


def run_command(cmd, description):
    """Run a command and return stdout, stderr, and return code"""
    print(f"\n{'=' * 60}")
    print(f"{description}")
    print(f"{'=' * 60}")
    print(f"Command: {' '.join(cmd)}")
    print()

    try:
        result = subprocess.run(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            timeout=30
        )
        # Decode with error handling for binary output
        try:
            output = result.stdout.decode('utf-8')
        except UnicodeDecodeError:
            output = result.stdout.decode('utf-8', errors='replace')
        return output, result.returncode
    except subprocess.TimeoutExpired:
        return "Command timed out after 30 seconds", 1
    except Exception as e:
        return f"Error running command: {e}", 1


def parse_framecpp_objects(output):
    """Parse framecpp_dump_objects output to extract object positions and types"""
    objects = []
    for line in output.split('\n'):
        # Position: 8316 length: 66 chkType: 1 class (FrSerData): 13 instance: 0
        match = re.match(r'Position:\s+(\d+)\s+length:\s+(\d+).*class\s+\(([^)]+)\):\s+(\d+)\s+instance:\s+(\d+)', line)
        if match:
            pos, length, class_name, class_id, instance = match.groups()
            objects.append({
                'position': int(pos),
                'length': int(length),
                'class_name': class_name,
                'class_id': int(class_id),
                'instance': int(instance)
            })
    return objects


def find_error_context(framecpp_objects, frcheck_output):
    """Find which object FrCheck is having trouble with"""
    errors = []

    # Look for FrCheck errors with position or length info
    for line in frcheck_output.split('\n'):
        if 'FrError' in line or 'Record length error' in line:
            errors.append(line)

    # Look for last successfully processed position
    last_pos = 0
    last_obj = None
    for line in frcheck_output.split('\n'):
        # length:        66 type=  13 (   FrSerData)
        match = re.search(r'length:\s*(\d+)\s+type=\s*(\d+)\s+\(([^)]+)\)', line)
        if match:
            length, type_id, class_name = match.groups()
            last_obj = {
                'length': int(length),
                'type_id': int(type_id),
                'class_name': class_name.strip()
            }

    return errors, last_obj


def main():
    if len(sys.argv) < 4:
        print("Usage: frcheck_with_context.py <framecpp_dump_objects> <FrCheck> <frame_file>", file=sys.stderr)
        sys.exit(1)

    dump_objects = Path(sys.argv[1]).resolve()
    fr_check = Path(sys.argv[2]).resolve()

    # Frame file is typically relative to CWD
    frame_file = Path(sys.argv[3])
    if not frame_file.is_absolute():
        frame_file = (Path.cwd() / frame_file).resolve()

    if not dump_objects.exists():
        print(f"ERROR: framecpp_dump_objects not found: {dump_objects}", file=sys.stderr)
        sys.exit(1)

    if not fr_check.exists():
        print(f"ERROR: FrCheck not found: {fr_check}", file=sys.stderr)
        sys.exit(1)

    if not frame_file.exists():
        print(f"ERROR: Frame file not found: {frame_file}", file=sys.stderr)
        print(f"       Current working directory: {os.getcwd()}", file=sys.stderr)
        sys.exit(1)

    print(f"{'=' * 60}")
    print(f"Frame File Analysis: {frame_file.name}")
    print(f"{'=' * 60}\n")

    # Run framecpp_dump_objects
    framecpp_output, framecpp_code = run_command(
        [str(dump_objects), str(frame_file)],
        "FrameCPP Object Dump"
    )

    # Parse objects
    objects = parse_framecpp_objects(framecpp_output)

    # Show summary of objects
    print("\nFrameCPP found {} objects:".format(len(objects)))
    for i, obj in enumerate(objects[:20]):  # Show first 20
        print(f"  {i+1:3d}. Position {obj['position']:6d}, length {obj['length']:4d}, "
              f"class {obj['class_name']:15s} (id={obj['class_id']:2d}, instance={obj['instance']})")
    if len(objects) > 20:
        print(f"  ... and {len(objects) - 20} more objects")

    # Run FrCheck
    frcheck_output, frcheck_code = run_command(
        [str(fr_check), '-d', '4', '-i', str(frame_file)],
        "FrCheck Validation (FrameL)"
    )

    print(frcheck_output)

    # Analyze errors
    if frcheck_code != 0:
        print(f"\n{'=' * 60}")
        print(f"ANALYSIS: FrCheck FAILED (exit code {frcheck_code})")
        print(f"{'=' * 60}\n")

        errors, last_obj = find_error_context(objects, frcheck_output)

        if errors:
            print("Errors found:")
            for err in errors[:10]:  # Show first 10 errors
                print(f"  • {err}")

        if last_obj:
            print(f"\nLast object FrCheck processed:")
            print(f"  Class: {last_obj['class_name']}")
            print(f"  Type ID: {last_obj['type_id']}")
            print(f"  Length: {last_obj['length']}")

            # Find corresponding FrameCPP object
            matching = [obj for obj in objects if obj['class_id'] == last_obj['type_id']]
            if matching:
                print(f"\n  FrameCPP objects with same class ID {last_obj['type_id']}:")
                for obj in matching[:5]:
                    print(f"    - Position {obj['position']}, length {obj['length']}, instance {obj['instance']}")
                    if obj['length'] != last_obj['length']:
                        print(f"      ⚠️  LENGTH MISMATCH: FrameCPP={obj['length']}, FrCheck={last_obj['length']}")

        sys.exit(frcheck_code)
    else:
        print(f"\n{'=' * 60}")
        print("✓ FrCheck validation PASSED")
        print(f"{'=' * 60}")
        sys.exit(0)


if __name__ == '__main__':
    main()
