#!/usr/bin/env python3
"""
HC900 Modbus TCP Simulator
Loads register-map.json and serves dummy values via pymodbus.
"""
import json
import struct
import logging
from pymodbus.server import StartTcpServer
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.device import ModbusDeviceIdentification

logging.basicConfig(level=logging.INFO, format="%(message)s")

REG_MAP_PATH = "docs/register-map.json"

def build_datastore(map_path: str) -> ModbusSlaveContext:
    with open(map_path) as f:
        data = json.load(f)

    # store up to 0x8000 (32768) holding registers
    store = [0] * (0x8000)

    seed = 0.0
    for r in data["registers"]:
        a = r["addr"]
        t = r["type"]
        tag = r["tag"]

        # generate dummy value based on tag name
        seed += 100.0
        if "MODE" in tag or "FUZZY" in tag or "EN" in tag or "STATE" in tag or "_REQ" in tag:
            val = 1  # integer status
        elif "SP_LO" in tag or "OP_LO" in tag:
            val = 0.0
        elif "SP_HI" in tag or "OP_HI" in tag:
            val = 100.0
        elif "PV" in tag or "SP" in tag or "OP" in tag:
            val = seed * 0.01
        elif "DIR" in tag:
            val = 1.0
        elif "LOOP_STATUS" in tag:
            val = 0x8000
        elif "PV_LO" in tag:
            val = 0.0
        elif "PV_HI" in tag:
            val = 150.0
        elif "DEV" in tag:
            val = 2.5
        elif "ALM" in tag:
            val = 80.0
        elif "LSP" in tag:
            val = 50.0
        elif "GAIN" in tag:
            val = 1.0
        elif "RESET" in tag:
            val = 10.0
        elif "RATE" in tag:
            val = 0.0
        elif "RSP" in tag:
            val = -1.0  # none
        elif "TRIP" in tag or "ESD" in tag or "IL" in tag:
            val = 0.0
        elif "_HS" in tag:
            val = 0.0
        else:
            val = 42.0

        if t == "float32":
            raw = struct.pack(">f", val)  # FP_B = big-endian IEEE 754
            store[a]     = struct.unpack(">H", raw[:2])[0]
            store[a + 1] = struct.unpack(">H", raw[2:])[0]
        elif t == "uint16":
            store[a] = int(val) & 0xFFFF

    ctx = ModbusSlaveContext(
        zero_mode=True,  # 0-based addressing (hw addr = array index)
        di=None, co=None, ir=None,
        hr=store,
    )
    print(f"Datastore built: {len(data['registers'])} registers, {len(store)} words")
    return ctx


if __name__ == "__main__":
    import sys, os
    os.chdir(os.path.join(os.path.dirname(__file__), ".."))
    ctx = build_datastore(REG_MAP_PATH)
    identity = ModbusDeviceIdentification()
    identity.VendorName = "HC900 Sim"
    identity.ProductCode = "HC900"
    identity.ModelName = "HC900-C70 Simulator"
    identity.MajorMinorRevision = "4.4x"

    print("Starting HC900 Modbus TCP simulator on 0.0.0.0:5020 ...")
    print("  (use port 5020 to avoid root)")
    StartTcpServer(
        context=ModbusServerContext(slaves=ctx, single=True),
        identity=identity,
        address=("0.0.0.0", 5020),
    )
