import struct
import ctypes

class MessageBase:
    """
    Abstract base class for all 1553 Messages.
    
    Manages the underlying data buffer (32 words of 16 bits) and
    handles serialization/deserialization ensuring Big Endian format
    (Network Byte Order) typically used in 1553 transmission over UDP.
    
    EXTENSIBILITY: All fields defined via descriptors (BitField, EnumField, ScaledField)
    are fully writable, enabling command generation and parameter injection for future
    control/simulation features. Field values modified via assignment are automatically
    reflected in pack() output.
    """
    
    # Subaddress ID must be defined in subclasses
    SUBADDRESS = None
    # Direction (True=Transmit/RT->BC, False=Receive/BC->RT)
    IS_TRANSMIT = False
    # Rate in Hz (defined in subclasses, used by scheduler)
    RATE_HZ = None
    
    def __init__(self, raw_bytes=None):
        """
        Initialize the message.
        
        Args:
            raw_bytes (bytes, optional): If provided, populates the message 
                                         from raw bytes (e.g. received from UDP).
        """
        # Internal storage: List of 32 integers (16-bit)
        # We initialize with 0. 
        # Note: 1553 messages max length is 32 words.
        self._data = [0] * 32
        
        # Store raw bytes for GUI display
        self._raw_data = None
        
        if raw_bytes:
            self.unpack(raw_bytes)

    @property
    def data(self):
        """
        Exposes the internal data buffer to Field descriptors.
        The descriptors in fields.py expect an object with a 'data' attribute
        that supports indexing (get/set).
        """
        return self._data

    def pack(self):
        """
        Serializes the message words into bytes using Big Endian format.
        
        Returns:
            bytes: The binary representation of the 32 words (64 bytes).
        """
        # Pack 32 unsigned shorts (H) in Big Endian (>)
        return struct.pack(f">{len(self._data)}H", *self._data)

    def unpack(self, raw_bytes):
        """
        Populates the message words from raw bytes using Big Endian format.
        
        Args:
            raw_bytes (bytes): The raw data received.
        """
        if not raw_bytes:
            return
        
        # Store raw bytes for GUI display
        self._raw_data = raw_bytes

        # Calculate how many words we can read
        num_bytes = len(raw_bytes)
        num_words = num_bytes // 2
        
        # Clamp to 32 words max
        if num_words > 32:
            num_words = 32
            
        # Unpack available words
        unpacked = struct.unpack(f">{num_words}H", raw_bytes[:num_words * 2])
        
        # Update internal buffer
        for i in range(num_words):
            self._data[i] = unpacked[i]

    def __repr__(self):
        """
        String representation for debugging.
        Shows Subaddress and non-zero words.
        """
        direction = "TX" if self.IS_TRANSMIT else "RX"
        sa = self.SUBADDRESS if self.SUBADDRESS is not None else "??"
        
        # Show only first few words or non-zero ones for brevity
        words_str = " ".join([f"{w:04X}" for w in self._data[:8]])
        return f"<{self.__class__.__name__} ({direction} SA:{sa}) Data[{words_str}...]>"