# =============================================================================
# DISCLAIMER
# -----------------------------------------------------------------------------
# This script is provided as-is and is **not** part of any official Acroname 
# API or SDK. It is for illustrative or utility purposes only.
#
# There are no guarantees of correctness, compatibility, or ongoing support.
# This code may change at any time without notice.
#
# Use at your own risk.
# =============================================================================

class PDMessageHeader:
    def __init__(self, msgHeaderVal=0):
        # Initialize with the full 16-bit message header value
        self.msgHeaderVal = msgHeaderVal

    @property
    def messageType(self):
        # Extract the 5-bit message type (bits 0-4)
        return self.msgHeaderVal & 0x1F

    @messageType.setter
    def messageType(self, value):
        # Set the 5-bit message type (bits 0-4)
        self.msgHeaderVal = (self.msgHeaderVal & ~0x1F) | (value & 0x1F)

    @property
    def portDataRole(self):
        # Extract the 1-bit port data role (bit 5)
        return (self.msgHeaderVal >> 5) & 0x01

    @portDataRole.setter
    def portDataRole(self, value):
        # Set the 1-bit port data role (bit 5)
        self.msgHeaderVal = (self.msgHeaderVal & ~(0x01 << 5)) | ((value & 0x01) << 5)

    @property
    def specRevision(self):
        # Extract the 2-bit spec revision (bits 6-7)
        return (self.msgHeaderVal >> 6) & 0x03

    @specRevision.setter
    def specRevision(self, value):
        # Set the 2-bit spec revision (bits 6-7)
        self.msgHeaderVal = (self.msgHeaderVal & ~(0x03 << 6)) | ((value & 0x03) << 6)

    @property
    def portPowerRoleOrCablePlug(self):
        # Extract the 1-bit port power role or cable plug (bit 8)
        return (self.msgHeaderVal >> 8) & 0x01

    @portPowerRoleOrCablePlug.setter
    def portPowerRoleOrCablePlug(self, value):
        # Set the 1-bit port power role or cable plug (bit 8)
        self.msgHeaderVal = (self.msgHeaderVal & ~(0x01 << 8)) | ((value & 0x01) << 8)

    @property
    def messageID(self):
        # Extract the 3-bit message ID (bits 9-11)
        return (self.msgHeaderVal >> 9) & 0x07

    @messageID.setter
    def messageID(self, value):
        # Set the 3-bit message ID (bits 9-11)
        self.msgHeaderVal = (self.msgHeaderVal & ~(0x07 << 9)) | ((value & 0x07) << 9)

    @property
    def numOfDataObjs(self):
        # Extract the 3-bit number of data objects (bits 12-14)
        return (self.msgHeaderVal >> 12) & 0x07

    @numOfDataObjs.setter
    def numOfDataObjs(self, value):
        # Set the 3-bit number of data objects (bits 12-14)
        self.msgHeaderVal = (self.msgHeaderVal & ~(0x07 << 12)) | ((value & 0x07) << 12)

    @property
    def extended(self):
        # Extract the 1-bit extended flag (bit 15)
        return (self.msgHeaderVal >> 15) & 0x01

    @extended.setter
    def extended(self, value):
        # Set the 1-bit extended flag (bit 15)
        self.msgHeaderVal = (self.msgHeaderVal & ~(0x01 << 15)) | ((value & 0x01) << 15)

    def __repr__(self):
        return (f"PDMessageHeader(messageType={self.messageType}, portDataRole={self.portDataRole}, "
                f"specRevision={self.specRevision}, portPowerRoleOrCablePlug={self.portPowerRoleOrCablePlug}, "
                f"messageID={self.messageID}, numOfDataObjs={self.numOfDataObjs}, extended={self.extended})")


class PDExtendedMessageHeader:
    def __init__(self, extendedMsgHeaderVal=0):
        # Initialize with the full 16-bit extended message header value
        self.extendedMsgHeaderVal = extendedMsgHeaderVal

    @property
    def dataSize(self):
        # Extract the 9-bit data size (bits 0-8)
        return self.extendedMsgHeaderVal & 0x1FF

    @dataSize.setter
    def dataSize(self, value):
        # Set the 9-bit data size (bits 0-8)
        self.extendedMsgHeaderVal = (self.extendedMsgHeaderVal & ~0x1FF) | (value & 0x1FF)

    @property
    def reserved(self):
        # Extract the 1-bit reserved (bit 9)
        return (self.extendedMsgHeaderVal >> 9) & 0x01

    @reserved.setter
    def reserved(self, value):
        # Set the 1-bit reserved (bit 9)
        self.extendedMsgHeaderVal = (self.extendedMsgHeaderVal & ~(0x01 << 9)) | ((value & 0x01) << 9)

    @property
    def requestChunk(self):
        # Extract the 1-bit request chunk (bit 10)
        return (self.extendedMsgHeaderVal >> 10) & 0x01

    @requestChunk.setter
    def requestChunk(self, value):
        # Set the 1-bit request chunk (bit 10)
        self.extendedMsgHeaderVal = (self.extendedMsgHeaderVal & ~(0x01 << 10)) | ((value & 0x01) << 10)

    @property
    def chunkNumber(self):
        # Extract the 4-bit chunk number (bits 11-14)
        return (self.extendedMsgHeaderVal >> 11) & 0x0F

    @chunkNumber.setter
    def chunkNumber(self, value):
        # Set the 4-bit chunk number (bits 11-14)
        self.extendedMsgHeaderVal = (self.extendedMsgHeaderVal & ~(0x0F << 11)) | ((value & 0x0F) << 11)

    @property
    def chunked(self):
        # Extract the 1-bit chunked flag (bit 15)
        return (self.extendedMsgHeaderVal >> 15) & 0x01

    @chunked.setter
    def chunked(self, value):
        # Set the 1-bit chunked flag (bit 15)
        self.extendedMsgHeaderVal = (self.extendedMsgHeaderVal & ~(0x01 << 15)) | ((value & 0x01) << 15)

    def __repr__(self):
        return (f"PDExtendedMessageHeader(dataSize={self.dataSize}, reserved={self.reserved}, "
                f"requestChunk={self.requestChunk}, chunkNumber={self.chunkNumber}, chunked={self.chunked})")


class BatteryInfoBitFields:
    def __init__(self):
        self._invalidBatteryRef = 0
        self._batteryIsPresent = 0
        self._batteryChargingStatus = 0
        self._reserved = 0

    # Getters and setters for the bitfields
    @property
    def invalid_battery_ref(self):
        return self._invalidBatteryRef

    @invalid_battery_ref.setter
    def invalid_battery_ref(self, value):
        if value in [0, 1]:
            self._invalidBatteryRef = value
        else:
            raise ValueError("Invalid Battery Reference must be 0 or 1.")

    @property
    def battery_is_present(self):
        return self._batteryIsPresent

    @battery_is_present.setter
    def battery_is_present(self, value):
        if value in [0, 1]:
            self._batteryIsPresent = value
        else:
            raise ValueError("Battery Is Present must be 0 or 1.")

    @property
    def battery_charging_status(self):
        return self._batteryChargingStatus

    @battery_charging_status.setter
    def battery_charging_status(self, value):
        if 0 <= value <= 3:
            self._batteryChargingStatus = value
        else:
            raise ValueError("Battery Charging Status must be between 0 and 3.")

    @property
    def reserved(self):
        return self._reserved

    @reserved.setter
    def reserved(self, value):
        if 0 <= value <= 15:
            self._reserved = value
        else:
            raise ValueError("Reserved (bitfields) must be between 0 and 15.")


class PDBatteryStatusDataObject:
    def __init__(self):
        self._reserved = 0
        self._batteryInfoBitFields = BatteryInfoBitFields()
        self._batteryPC = 0

    # Getters and setters for the fields
    @property
    def reserved(self):
        return self._reserved

    @reserved.setter
    def reserved(self, value):
        if 0 <= value <= 255:  # 8-bit value
            self._reserved = value
        else:
            raise ValueError("Reserved must be between 0 and 255.")

    @property
    def battery_info_bit_fields(self):
        return self._batteryInfoBitFields

    @property
    def battery_pc(self):
        return self._batteryPC

    @battery_pc.setter
    def battery_pc(self, value):
        if 0 <= value <= 65535:  # 16-bit value
            self._batteryPC = value
        else:
            raise ValueError("Battery PC must be between 0 and 65535.")

    def __str__(self):
        return (f"Reserved (8-bit): {self._reserved}\n"
                f"Invalid Battery Ref: {self._batteryInfoBitFields.invalid_battery_ref}\n"
                f"Battery Is Present: {self._batteryInfoBitFields.battery_is_present}\n"
                f"Battery Charging Status: {self._batteryInfoBitFields.battery_charging_status}\n"
                f"Reserved (bitfields, 4-bit): {self._batteryInfoBitFields.reserved}\n"
                f"Battery PC: {self._batteryPC}")


# Function to decode rule into PDBatteryStatusDataObject
def rule_to_battery_status_data_object(rule):
    bsdo_rule = PDBatteryStatusDataObject()

    # Decode the 8-bit reserved field
    bsdo_rule.reserved = (rule >> 0) & 0xFF

    # Decode the bitfields from the byte at bit 8
    bsdo_rule.battery_info_bit_fields.invalid_battery_ref = (rule >> 8) & 0x1
    bsdo_rule.battery_info_bit_fields.battery_is_present = (rule >> 9) & 0x1
    bsdo_rule.battery_info_bit_fields.battery_charging_status = (rule >> 10) & 0x3
    bsdo_rule.battery_info_bit_fields.reserved = (rule >> 12) & 0xF

    # Decode the 16-bit batteryPC field
    bsdo_rule.battery_pc = (rule >> 16) & 0xFFFF

    return bsdo_rule


