"""pymodbus-based helpers for the ZLAC8015D V4 RS485 (Modbus RTU) protocol.

Register reference (from "ZLAC8015D V4 Series RS485 Communication" manual):
  0x2001  RS485 Custom Drive Node Number   (RW/S, range 1-127, default 1)
  0x2002  RS485 custom communication baud rate (RW/S)
  0x2010  Save all RW attribute parameters to EEPROM (write 1 to save)

Requires: pip install pymodbus pyserial
"""
from pymodbus.client import ModbusSerialClient

REG_NODE_ID = 0x2001
REG_SAVE_EEPROM = 0x2010

# pymodbus renamed the "slave id" keyword across major versions
# (unit -> slave -> device_id). Try each so this works regardless of
# which pymodbus version is installed (e.g. on the Ubuntu test box).
_ID_KWARGS = ("device_id", "slave", "unit")


def _call(fn, *args, node_id, **kwargs):
    last_err = None
    for kw in _ID_KWARGS:
        try:
            return fn(*args, **{kw: node_id}, **kwargs)
        except TypeError as exc:
            last_err = exc
            continue
    raise TypeError(f"pymodbus client does not accept any of {_ID_KWARGS}: {last_err}")


def make_client(port: str, baud: int = 115200, timeout: float = 0.3) -> ModbusSerialClient:
    return ModbusSerialClient(
        port,
        baudrate=baud,
        bytesize=8,
        parity="N",
        stopbits=1,
        timeout=timeout,
    )


def read_register(client: ModbusSerialClient, node_id: int, address: int, count: int = 1):
    """Return list of register values, or None on error/no response."""
    result = _call(client.read_holding_registers, address, node_id=node_id, count=count)
    if result is None or result.isError():
        return None
    return result.registers


def write_register(client: ModbusSerialClient, node_id: int, address: int, value: int) -> bool:
    result = _call(client.write_register, address, value, node_id=node_id)
    if result is None or result.isError():
        return False
    return True
