from __future__ import annotations

import threading
from dataclasses import dataclass
from typing import List


@dataclass
class PIDEncoding:
    kind: str
    scale: float = 1.0
    offset: float = 0.0

    @property
    def byte_width(self) -> int:
        if self.kind in ("uint8", "int8"):
            return 1
        return 2

    def encode(self, value: float) -> List[int]:
        if self.kind == "uint8":
            scaled = round(value * self.scale)
            clamped = min(max(scaled, 0), 255)
            return [int(clamped)]
        if self.kind == "uint16":
            scaled = round(value * self.scale)
            clamped = min(max(scaled, 0), 65535)
            uint16_value = int(clamped)
            return [(uint16_value >> 8) & 0xFF, uint16_value & 0xFF]
        if self.kind == "int8":
            offset_value = value - self.offset
            scaled = round(offset_value * self.scale)
            clamped = min(max(scaled, -128), 127)
            return [int(clamped) & 0xFF]
        if self.kind == "int16":
            offset_value = value - self.offset
            scaled = round(offset_value * self.scale)
            clamped = min(max(scaled, -32768), 32767)
            int16_value = int(clamped) & 0xFFFF
            return [(int16_value >> 8) & 0xFF, int16_value & 0xFF]
        return []


class DynamicPIDValue:
    def __init__(self, generator, encoding: PIDEncoding) -> None:
        self.generator = generator
        self.encoding = encoding

    def current_bytes(self) -> List[int]:
        value = self.generator.current_value()
        return self.encoding.encode(value)


class SequencePIDValue:
    def __init__(self, responses: List[List[int]], behavior: str) -> None:
        self.responses = responses
        self.behavior = behavior
        self.lock = threading.Lock()
        self.index = 0

    @property
    def response_length(self) -> int:
        return len(self.responses[0]) if self.responses else 0

    def next_bytes(self) -> List[int]:
        with self.lock:
            if not self.responses:
                return []
            current = self.responses[self.index]
            if len(self.responses) == 1:
                return current
            if self.behavior == "loop":
                self.index = (self.index + 1) % len(self.responses)
            elif self.behavior == "once" and self.index < len(self.responses) - 1:
                self.index += 1
            return current


class PID:
    def __init__(self, value_type) -> None:
        self.value_type = value_type

    @property
    def content(self) -> List[int]:
        if self.value_type[0] == "static":
            return list(self.value_type[1])
        if self.value_type[0] == "dynamic":
            return self.value_type[1].current_bytes()
        if self.value_type[0] == "sequence":
            return self.value_type[1].next_bytes()
        return []

    def process(self, message: List[int]) -> List[int]:
        return self.content


class PIDRegistry(dict):
    def supported(self, pid: int) -> List[int]:
        if pid % 0x20 != 0:
            raise ValueError("PID must be a multiple of 0x20")
        offset = pid & 0xFF
        value = 0x00
        start = 0x01
        end = 0x1F if pid == 0xE0 else 0x20
        for bit in range(start, end + 1):
            if (offset + bit) in self or ((offset + bit) % 0x20 == 0):
                value |= 1 << (0x20 - bit)
        return [(value >> 24) & 0xFF, (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]
