#!/usr/bin/env python3
"""
Convert Nordic SoftDevice hex files to C source arrays.

This script converts Intel HEX format SoftDevice files to C arrays that can be
embedded directly in firmware projects.
"""

import sys
import os
from pathlib import Path


def hex_to_c_array(hex_file, output_file, array_name, version=""):
    """
    Convert a hex file to a C array.
    
    Args:
        hex_file: Path to input .hex file
        output_file: Path to output .c file
        array_name: Name of the C array variable
        version: Optional version string for documentation
    """
    # Read and parse hex file
    with open(hex_file, 'r') as f:
        lines = f.readlines()
    
    # Dictionary to store data by address
    memory_map = {}
    extended_linear_address = 0x00000000
    
    for line in lines:
        line = line.strip()
        if not line.startswith(':'):
            continue
        
        # Parse Intel HEX record format
        byte_count = int(line[1:3], 16)
        address = int(line[3:7], 16)
        record_type = int(line[7:9], 16)
        
        # Handle Extended Linear Address record (type 0x04)
        if record_type == 0x04:
            extended_linear_address = int(line[9:13], 16) << 16
            continue
        
        # Handle End of File record (type 0x01)
        if record_type == 0x01:
            break
        
        # Only process data records (type 0x00)
        if record_type == 0x00:
            full_address = extended_linear_address + address
            data = line[9:9+byte_count*2]
            for i in range(0, len(data), 2):
                byte_val = int(data[i:i+2], 16)
                memory_map[full_address + i // 2] = byte_val
    
    # Extract data from 0x00000 onwards, combining MBR and SoftDevice regions
    # MBR/Application area: 0x00000 - 0x00FFF (skip this - it's not SoftDevice)
    # We need to include both areas to match the expected firmware structure
    bytes_data = []
    
    # Find the range of data
    if memory_map:
        # We need to extract from address 0x00000 to the end
        # But some addresses might be gaps - we need to handle that
        min_addr = min(a for a in memory_map.keys() if a < 0x10000)  # MBR area
        max_addr = max(memory_map.keys())
        
        # Extract all bytes sequentially from 0x00000, padding gaps with 0xFF
        current_addr = 0x00000
        for addr in range(min_addr, max_addr + 1):
            if addr in memory_map:
                bytes_data.append(memory_map[addr])
            else:
                # Fill gaps with 0xFF (flash unprogrammed state)
                bytes_data.append(0xFF)
    
    # Generate C source file
    with open(output_file, 'w') as f:
        # Write header
        f.write('/**\n')
        f.write(' * @file      ' + os.path.basename(output_file) + '\n')
        f.write(' *\n')
        f.write(' * @brief     SoftDevice of nrf52 s113 ' + version + '\n')
        f.write(' *\n')
        f.write(' * @author    Qorvo Applications\n')
        f.write(' *\n')
        f.write(' * @copyright SPDX-FileCopyrightText: Copyright (c) 2025 Qorvo US, Inc.\n')
        f.write(' *            SPDX-License-Identifier: LicenseRef-QORVO-2\n')
        f.write(' *\n')
        f.write(' */\n')
        f.write('\n')
        f.write('#include <stdint.h>\n')
        f.write('\n')
        
        # Write array declaration
        f.write(f'const uint8_t {array_name}[] __attribute((used, section(".soft_device_prog")))\n')
        f.write('= {\n')
        
        # Write bytes in rows of 16 for readability
        for i, byte in enumerate(bytes_data):
            if i % 16 == 0:
                f.write('    ')
            f.write(f'0x{byte:02x}')
            if i < len(bytes_data) - 1:
                f.write(', ')
            if (i + 1) % 16 == 0:
                f.write('\n')
        
        f.write('\n};\n')
    
    return len(bytes_data)


def main():
    """Main entry point for the script."""
    if len(sys.argv) < 2:
        print("Usage: python3 convert_softdevice.py <hex_file> [output_file] [version]")
        print("\nExamples:")
        print("  python3 convert_softdevice.py s113_nrf52_7.3.0_softdevice.hex")
        print("  python3 convert_softdevice.py s113_nrf52_7.3.0_softdevice.hex output.c")
        print("  python3 convert_softdevice.py s113_nrf52_7.3.0_softdevice.hex output.c v7.3.0")
        sys.exit(1)
    
    hex_file = sys.argv[1]
    
    # Determine output file name
    if len(sys.argv) > 2:
        output_file = sys.argv[2]
    else:
        # Replace .hex with .c in the input filename
        output_file = Path(hex_file).stem + '.c'
    
    # Extract version from filename if not provided
    version = ""
    if len(sys.argv) > 3:
        version = sys.argv[3]
    else:
        # Try to extract version from hex filename (e.g., s113_nrf52_7.3.0_softdevice.hex)
        parts = Path(hex_file).stem.split('_')
        for part in parts:
            if part[0].isdigit() and '.' in part:
                version = part
                break
    
    # Validate input file exists
    if not os.path.isfile(hex_file):
        print(f"Error: Input file '{hex_file}' not found")
        sys.exit(1)
    
    try:
        print(f"Converting {hex_file} -> {output_file}")
        array_name = "soft_device_prog"
        size = hex_to_c_array(hex_file, output_file, array_name, version)
        print(f"✓ Conversion successful!")
        print(f"  - Array size: {size} bytes ({size/1024:.1f} KB)")
        print(f"  - Array name: {array_name}")
        print(f"  - Output: {output_file}")
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()

