from .abstract_i2c_device import AbstractI2CDevice
from iowp.port import IowPort

class PCF8574(AbstractI2CDevice):
    """
    Implementation of PCF8574 as a virtual IowPort via I2C.
    """
    NAME = "PCF8574"
    CLASS = 0x4  # PCF8547 Base address 0x20
    
    def __init__(self, device_address, io_mask=0xFF, initial_data=None):
        # WICHTIG: Wir nutzen self.NAME und self.CLASS statt fester Werte!
        super().__init__(self.NAME, self.CLASS, device_address)
        
        # Wir nutzen die IowPort-Logik zur Verwaltung der Bits
        # Index setzen wir auf die I2C-Adresse zur Identifikation
        self._port = IowPort(self.dev_addr.write_address, io_mask, initial_data)
        self._last_applied_data = -1

    def set_i2c(self, i2c):
        """Wird von der Factory/System aufgerufen, um den Bus zuzuweisen."""
        super().set_i2c(i2c)
        if self.i2c:
            self.apply() # Initialzustand auf Hardware schreiben

    @property
    def port(self):
        """Zugriff auf das interne Port-Objekt für Bit-Operationen."""
        return self._port

    def apply(self):
        """Schreibt den aktuellen Zustand des IowPort Objekts auf den I2C-Bus."""
        if not self.i2c:
            return
            
        # Wir nutzen die IowPort-Logik: data | direction
        # Das sorgt dafür, dass Inputs auf '1' (Pull-up) gesetzt werden.
        data_to_send = self._port.get_data_to_write()
        
        # Nur senden, wenn sich etwas geändert hat (Optimierung)
        if data_to_send != self._last_applied_data:
            wdata = [self.dev_addr.write_address, data_to_send]
            self.i2c.write_i2c(self, wdata)
            self._last_applied_data = data_to_send

    def refresh(self):
        """Liest den Port vom I2C-Bus und aktualisiert das IowPort Objekt."""
        if not self.i2c:
            return
            
        rdata = [self.dev_addr.read_address]
        result = self.i2c.read_i2c(self, rdata, 1)
        
        # Nutzt die IowPort-Methode, um Listener zu benachrichtigen
        self._port.set_data_from_read(result[0])
        return result[0]

    #def __str__(self):
    #    return f"{self.NAME}@{self.dev_addr}: {self._port}"

    def __str__(self):
        return f"{self.NAME}: Addr[0x{self.dev_addr}]"