from typing import Dict, Optional, List, Union
from enum import Enum
from attrs import define, field, validators

# we define a validator for our names - alphanumeric and underscores
# most things can't support numbers as the first character, so we don't either.
name_validator = validators.matches_re(r"^[A-Za-z_][A-Za-z0-9_]?$")

@define
class Bus():
    name: str
    baud_rate: str
    extended_id: bool = False




class FieldType(str, Enum):
    """FieldType indicates the type of the field - the enum represents the C type,
    but you can use a map to convert the type to another language."""

    # used to ensure types are valid, and act as representations for other languages/mappings.
    U8 = "uint8_t"
    U16 = "uint16_t"
    U32 = "uint32_t"
    U64 = "uint64_t"
    I8 = "int8_t"
    I16 = "int16_t"
    I32 = "int32_t"
    I64 = "int64_t"
    F32 = "float"

    Bitfield = "bitfield"

    def size(self) -> int:
        """Returns the size, in bytes, of the type."""
        match self:
            case FieldType.U8:
                return 1
            case FieldType.U16:
                return 2
            case FieldType.U32:
                return 4
            case FieldType.U64:
                return 8
            case FieldType.I8:
                return 1
            case FieldType.I16:
                return 2
            case FieldType.I32:
                return 4
            case FieldType.I64:
                return 8
            case FieldType.F32:
                return 4
            case FieldType.Bitfield:
                return 1
        return -1


@define
class CustomTypeDef():
    name: str = field(validator=[name_validator])
    base_type: FieldType # should be a strict size
    values: Union[List[str], Dict[str, int]]


@define
class BitfieldBit():
    "micro class to represent one bit in bitfields"
    name: str = field(validator=[name_validator])

@define
class Field():
    name: str = field(validator=[name_validator])
    type: FieldType

    #metadata
    units: Optional[str]
    conversion: Optional[float]


@define
class BitField():
    name: str = field(validator=[name_validator])
    type: str = field(default="bitfield", init=False) # it's a constant value
    bits: List[BitfieldBit]


class Endian(str, Enum):
    BIG = "big"
    LITTLE = "little"

@define
class Packet():
    name: str
    description: str
    id: int
    endian: Endian
    frequency: Optional[int]
    data: List[Field]

@define
class RepeatedPacket():
    name: str
    description: str
    id: int
    endian: Endian
    frequency: Optional[int]
    data: List[Field]
    repeat: int
    offset: int