from __future__ import annotations

from typing import Dict

from ..errors import AdapterError
from .adapter import Adapter, BusCommandModifier, Output
from .command_bundle import AsyncHandlerWithWriter
from .periodic_message import PeriodicMessage


class STN22xx:
    static_commands: Dict[str, str] = {}

    dynamic_commands: Dict[str, AsyncHandlerWithWriter] = {
        "STI": lambda _s, adapter, writer: writer(adapter.stn_sti_version),
        "STIX": lambda _s, adapter, writer: writer(adapter.stn_stix_version),
        "STSN": lambda _s, adapter, writer: writer(adapter.stn_stsn_serial),
        "STMFR": lambda _s, adapter, writer: writer(adapter.stn_stmfr_manufacturer),
        "STDI": lambda _s, adapter, writer: writer(adapter.name),
        "STDIX": lambda _s, adapter, writer: writer(_format_stdix(adapter)),
        "STUIL0": lambda _s, _adapter, writer: writer(Output.ok),
        "STUIL1": lambda _s, _adapter, writer: writer(Output.ok),
        "STCSEGR0": lambda _s, adapter, writer: _set_attr(adapter, "auto_segmentation_rx", False, writer),
        "STCSEGR1": lambda _s, adapter, writer: _set_attr(adapter, "auto_segmentation_rx", True, writer),
        "STCSEGT0": lambda _s, adapter, writer: _set_attr(adapter, "auto_segmentation_tx", False, writer),
        "STCSEGT1": lambda _s, adapter, writer: _set_attr(adapter, "auto_segmentation_tx", True, writer),
        "STPPMC": lambda _s, adapter, writer: _clear_periodic(adapter, writer),
    }

    prefix_commands: Dict[str, AsyncHandlerWithWriter] = {
        "STCAFCP": lambda _s, _adapter, writer: writer(Output.ok),
        "STPX": lambda s, adapter, writer: _set_bus_modifiers(s, adapter, writer),
        "STPPMA": lambda s, adapter, writer: _add_periodic(s, adapter, writer),
        "STPPMD": lambda s, adapter, writer: _delete_periodic(s, adapter, writer),
    }


def _set_attr(adapter: Adapter, field: str, value: bool, writer) -> None:
    setattr(adapter, field, value)
    writer(Output.ok)


def _format_stdix(adapter: Adapter) -> str:
    return (
        f"Device:       {adapter.name}\r"
        f"Firmware:     {adapter.stn_stix_version}\r"
        f"Mfr:          {adapter.stn_stmfr_manufacturer}\r"
        f"Serial #:     {adapter.stn_stsn_serial}"
    )


def _clear_periodic(adapter: Adapter, writer) -> None:
    adapter.clear_periodic_messages()
    writer(Output.ok)


def _set_bus_modifiers(value: str, adapter: Adapter, writer) -> None:
    for parameter in value.split(","):
        if len(parameter) <= 1:
            continue
        kind = parameter[0]
        try:
            parsed = int(parameter[1:])
        except ValueError:
            continue
        if kind == "R":
            adapter.next_bus_command_modifiers.append(BusCommandModifier(kind="expected_responses", value=parsed))
        elif kind == "T":
            adapter.next_bus_command_modifiers.append(BusCommandModifier(kind="timeout", value=parsed / 1000.0))
    writer("DATA")


def _add_periodic(value: str, adapter: Adapter, writer) -> None:
    parts = value.split(",")
    if len(parts) != 3:
        raise AdapterError.invalid_command()
    try:
        interval = int(parts[0])
        header = int(parts[1], 16)
        bytes_ = _hex_to_bytes(parts[2])
    except ValueError:
        raise AdapterError.invalid_command()
    if len(bytes_) >= 8:
        writer(Output.invalid_command)
        return
    periodic = PeriodicMessage(header=header, interval_ms=interval, bytes=bytes_)
    adapter.add_periodic_message(periodic)
    writer(str(periodic.id))


def _delete_periodic(value: str, adapter: Adapter, writer) -> None:
    try:
        message_id = int(value)
    except ValueError:
        raise AdapterError.invalid_command()
    adapter.clear_periodic_message(message_id)
    writer(Output.ok)


def _hex_to_bytes(value: str):
    if not value:
        return []
    if len(value) % 2 == 1:
        value = "0" + value
    return [int(value[i:i + 2], 16) for i in range(0, len(value), 2)]
