from __future__ import annotations

import enum
from dataclasses import dataclass
from typing import List

from .automotive import UdsNegativeResponse
from .uds import NegativeResponseCode


class ScalingDataType(enum.IntEnum):
    unsigned_numeric = 0x0
    signed_numeric = 0x1
    bit_mapped_reported_without_mask = 0x2
    bit_mapped_reported_with_mask = 0x3
    binary_coded_decimal = 0x4
    state_encoded_variable = 0x5
    ascii = 0x6
    signed_floating_point = 0x7
    packet = 0x8
    formula = 0x9
    unit_or_format = 0xA
    vehicle_manufacturer_specific_c = 0xB
    vehicle_manufacturer_specific_d = 0xC
    vehicle_manufacturer_specific_e = 0xD
    reserved = 0xE


@dataclass
class ECUIdentificationEntry:
    option: int
    data_type: ScalingDataType
    data: List[int]

    @staticmethod
    def create_from(data_table: List[int], scaling_table: List[int]) -> List["ECUIdentificationEntry"]:
        if not data_table or len(scaling_table) <= 2:
            return []
        data_index = 0
        scaling_index = 0
        entries: List[ECUIdentificationEntry] = []
        while scaling_index < len(scaling_table):
            next_offset = scaling_table[scaling_index]
            if next_offset == 0xFF:
                break
            if scaling_index + next_offset > len(scaling_table):
                raise UdsNegativeResponse(NegativeResponseCode.invalid_key)
            option = scaling_table[scaling_index + 1]
            payload: List[int] = []
            data_types: set[ScalingDataType] = set()
            for format_byte in scaling_table[scaling_index + 2: scaling_index + next_offset]:
                data_type = ScalingDataType((format_byte >> 4) & 0xF)
                data_length = format_byte & 0xF
                max_length = min(data_length, len(data_table) - data_index)
                payload_slice = data_table[data_index:data_index + max_length]
                data_types.add(data_type)
                if data_type == ScalingDataType.formula:
                    break
                payload.extend(payload_slice)
                data_index += max_length
                if data_index >= len(data_table):
                    break
            if not data_types:
                raise UdsNegativeResponse(NegativeResponseCode.general_reject)
            data_type = next(iter(data_types)) if len(data_types) == 1 else ScalingDataType.reserved
            entries.append(ECUIdentificationEntry(option=option, data_type=data_type, data=payload))
            scaling_index += next_offset
        return entries
