# core/sfp_structures.py
"""
Defines the ctypes Structures for the Simple Fragmentation Protocol (SFP)
and the application-specific image data format.

This centralizes data structure definitions, allowing them to be shared
between the transport layer (which uses SFPHeader) and the application
layer (which interprets the image-specific structures).
"""

import ctypes

# --- SFP Transport Layer Structures ---


class SFPHeader(ctypes.Structure):
    """Structure representing the SFP header (32 bytes)."""

    _pack_ = 1
    _fields_ = [
        ("SFP_MARKER", ctypes.c_uint8),
        ("SFP_DIRECTION", ctypes.c_uint8),
        ("SFP_PROT_VER", ctypes.c_uint8),
        ("SFP_PT_SPARE", ctypes.c_uint8),
        ("SFP_TAG", ctypes.c_uint8),
        ("SFP_SRC", ctypes.c_uint8),
        ("SFP_FLOW", ctypes.c_uint8),
        ("SFP_TID", ctypes.c_uint8),
        ("SFP_FLAGS", ctypes.c_uint8),
        ("SFP_WIN", ctypes.c_uint8),
        ("SFP_ERR", ctypes.c_uint8),
        ("SFP_ERR_INFO", ctypes.c_uint8),
        ("SFP_TOTFRGAS", ctypes.c_uint16),
        ("SFP_FRAG", ctypes.c_uint16),
        ("SFP_RECTYPE", ctypes.c_uint8),
        ("SFP_RECSPARE", ctypes.c_uint8),
        ("SFP_PLDAP", ctypes.c_uint8),
        ("SFP_PLEXT", ctypes.c_uint8),
        ("SFP_RECCOUNTER", ctypes.c_uint16),
        ("SFP_PLSIZE", ctypes.c_uint16),
        ("SFP_TOTSIZE", ctypes.c_uint32),
        ("SFP_PLOFFSET", ctypes.c_uint32),
    ]

    @classmethod
    def size(cls):
        """Returns the size of the structure in bytes."""
        return ctypes.sizeof(cls)

    @staticmethod
    def get_field_offset(field_name: str) -> int:
        """Returns the byte offset of a specific field within the structure."""
        try:
            return getattr(SFPHeader, field_name).offset
        except AttributeError:
            return -1


# --- Application Layer (Image Format) Structures ---


class DataTag(ctypes.Structure):
    """Structure representing a generic data tag (8 bytes)."""

    _pack_ = 1
    _fields_ = [
        ("ID", ctypes.c_uint8 * 2),
        ("VALID", ctypes.c_uint8),
        ("VERSION", ctypes.c_uint8),
        ("SIZE", ctypes.c_uint32),
    ]

    @classmethod
    def size(cls):
        """Returns the size of the structure in bytes."""
        return ctypes.sizeof(cls)


class HeaderData(ctypes.Structure):
    """Structure representing the HEADER_DATA block (~48 bytes)."""

    _pack_ = 1
    _fields_ = [
        ("TYPE", ctypes.c_uint8),
        ("SUBTYPE", ctypes.c_uint8),
        ("LOGCOLORS", ctypes.c_uint8),
        ("IMG_RESERVED", ctypes.c_uint8),
        ("PRODINFO", ctypes.c_uint32 * 2),
        ("TOD", ctypes.c_uint16 * 2),
        ("RESERVED", ctypes.c_uint32),
        ("FCOUNTER", ctypes.c_uint32),
        ("TIMETAG", ctypes.c_uint32),
        ("NOMINALFRATE", ctypes.c_uint16),
        ("FRAMETAG", ctypes.c_uint16),
        ("OX", ctypes.c_uint16),
        ("OY", ctypes.c_uint16),
        ("DX", ctypes.c_uint16),
        ("DY", ctypes.c_uint16),
        ("STRIDE", ctypes.c_uint16),
        ("BPP", ctypes.c_uint8),
        ("COMP", ctypes.c_uint8),
        ("SPARE", ctypes.c_uint16),
        ("PALTYPE", ctypes.c_uint8),
        ("GAP", ctypes.c_uint8),
    ]

    @classmethod
    def size(cls):
        """Returns the size of the structure in bytes."""
        return ctypes.sizeof(cls)


class GeoData(ctypes.Structure):
    """Structure representing the GEO_DATA block (~80 bytes)."""

    _pack_ = 1
    _fields_ = [
        ("INVMASK", ctypes.c_uint32),
        ("ORIENTATION", ctypes.c_float),
        ("LATITUDE", ctypes.c_float),
        ("LONGITUDE", ctypes.c_float),
        ("REF_X", ctypes.c_uint16),
        ("REF_Y", ctypes.c_uint16),
        ("SCALE_X", ctypes.c_float),
        ("SCALE_Y", ctypes.c_float),
        ("POI_ORIENTATION", ctypes.c_float),
        ("POI_LATITUDE", ctypes.c_float),
        ("POI_LONGITUDE", ctypes.c_float),
        ("POI_ALTITUDE", ctypes.c_float),
        ("POI_X", ctypes.c_uint16),
        ("POI_Y", ctypes.c_uint16),
        ("SPARE", ctypes.c_uint32 * 7),
    ]

    @classmethod
    def size(cls):
        """Returns the size of the structure in bytes."""
        return ctypes.sizeof(cls)


class ImageLeaderData(ctypes.Structure):
    """Represents the complete Image Leader data structure (~1320 bytes)."""

    _pack_ = 1
    _fields_ = [
        ("HEADER_TAG", DataTag),
        ("HEADER_DATA", HeaderData),
        ("GEO_TAG", DataTag),
        ("GEO_DATA", GeoData),
        ("RESERVED_TAG", DataTag),
        ("RESERVED_DATA", ctypes.c_uint8 * 128),
        ("CM_TAG", DataTag),
        ("COLOUR_MAP", ctypes.c_uint32 * 256),
        ("PIXEL_TAG", DataTag),
    ]

    @classmethod
    def size(cls):
        """Returns the size of the structure in bytes."""
        return ctypes.sizeof(cls)

    @staticmethod
    def get_reserved_data_size() -> int:
        """Returns the static size of the RESERVED_DATA field."""
        return 128

    @staticmethod
    def get_colour_map_size() -> int:
        """Returns the static size of the COLOUR_MAP field in bytes."""
        return 1024


# --- RIS Status payload (minimal) ---
# These are conservative, minimal definitions to extract useful fields
# from the server's SFP_RIS::status_message_t. They intentionally include
# only the fields we currently need (timetag, platform azimuth, velocity,
# geographic position, and a small target struct).

DSP_RIS_MAX_TGT = 32


class RisTarget(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("flags", ctypes.c_uint32),
        ("heading", ctypes.c_float),
        ("x", ctypes.c_float),
        ("y", ctypes.c_float),
        ("z", ctypes.c_float),
        ("spare", ctypes.c_uint32 * 3),
    ]


class RisScenario(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("timetag", ctypes.c_uint32),
        ("flags", ctypes.c_uint32),
        ("mode", ctypes.c_uint32),
        ("spare1", ctypes.c_uint32 * 6),
        ("platform_azimuth", ctypes.c_float),
        ("vx", ctypes.c_float),
        ("vy", ctypes.c_float),
        ("vz", ctypes.c_float),
        ("baro_altitude", ctypes.c_float),
        ("true_heading", ctypes.c_float),
        ("latitude", ctypes.c_float),
        ("longitude", ctypes.c_float),
        ("ant_nav_az", ctypes.c_float),
        ("ant_nav_el", ctypes.c_float),
        ("spare2", ctypes.c_uint32 * 8),
    ]


class RisTargetsBlock(ctypes.Structure):
    _pack_ = 1
    _fields_ = [("tgt", RisTarget * DSP_RIS_MAX_TGT)]


class SfpRisStatusPayload(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("scenario_tag", DataTag),
        ("scenario", RisScenario),
        ("target_tag", DataTag),
        ("tgt", RisTargetsBlock),
    ]

    @classmethod
    def size(cls):
        return ctypes.sizeof(cls)
