#!/usr/bin/env python3
"""
HC900 Register Map Builder — C3/C4 Full Modbus Map CSV → register-map.json

Reads the "Modbus Full Address Report" CSV exported from HC Designer
(C3-All-Modbus-Map.csv / C4-All-Modbus-Map.csv) and produces the
JSON register map consumed by the hc900_gateway.
"""

import csv
import json
import argparse
from pathlib import Path


def parse_full_map(path: Path) -> dict:
    registers = []

    with open(path, "r", encoding="utf-8-sig") as f:
        reader = csv.reader(f)
        rows = list(reader)

    # Determine which section we're in
    for row in rows:
        if len(row) < 6:
            continue
        hex_addr_raw = row[0].strip()
        if not hex_addr_raw.startswith("0x"):
            continue
        addr = int(hex_addr_raw, 16)
        tag = row[2].strip()
        desc = row[3].strip()
        typ = row[4].strip()
        num = row[5].strip()

        if typ == "PID":
            dtype_raw = row[6].strip()
            access_raw = row[7].strip() if len(row) > 7 else "R"
            if "unsigned 16" in dtype_raw.lower() or "integer" in dtype_raw.lower() or "signed 16" in dtype_raw.lower():
                dtype = "uint16"
                count = 1
            else:
                dtype = "float32"
                count = 2
            access = "RW" if "w" in access_raw.lower() else "R"
            registers.append({
                "tag": tag, "addr": addr, "count": count,
                "type": dtype, "access": access, "description": desc,
            })

        elif typ in ("Signal Tag", "Signal"):
            dtype_raw = row[6].strip()
            if "unsigned 16" in dtype_raw.lower() or "signed 16" in dtype_raw.lower():
                dtype = "uint16"
                count = 1
            else:
                dtype = "float32"
                count = 2
            registers.append({
                "tag": tag, "addr": addr, "count": count,
                "type": dtype, "access": "R", "description": desc,
            })

        elif typ in ("Variable", "Math Variable"):
            dtype_raw = row[6].strip()
            if "unsigned 16" in dtype_raw.lower() or "signed 16" in dtype_raw.lower():
                dtype = "uint16"
                count = 1
            else:
                dtype = "float32"
                count = 2
            registers.append({
                "tag": tag, "addr": addr, "count": count,
                "type": dtype, "access": "RW", "description": desc,
            })

    registers.sort(key=lambda r: r["addr"])

    # Detect controller name from header
    controller = "HC900-C70"
    with open(path, "r", encoding="utf-8-sig") as f:
        for line in f:
            if line.startswith("File Name:"):
                controller = line.split(",", 1)[1].strip()
                break

    return {
        "controller": controller,
        "float_format": "FP_B",
        "notes": "FP_B = IEEE 754 Big Endian (byte order 4,3,2,1). "
                 "Address is the first register (0-based). "
                 "float32 uses 2 consecutive registers.",
        "register_count": len(registers),
        "registers": registers,
    }


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Build register-map.json from HC Designer Full Modbus Map CSV")
    parser.add_argument("csv", help="Path to the *-All-Modbus-Map.csv")
    parser.add_argument("-o", "--output", default="register-map.json", help="Output JSON path")
    args = parser.parse_args()

    data = parse_full_map(Path(args.csv))

    with open(args.output, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)

    pid_count = sum(1 for r in data["registers"] if r["tag"] and any(c in r["tag"] for c in ".-_"))
    print(f"✓ Register map written to {args.output}")
    print(f"  Controller: {data['controller']}")
    print(f"  Total registers: {data['register_count']}")
