#include <Wire.h>
#include "PCF8574.h"

PCF8574::PCF8574() : _PORT(0), _PIN(0), _DDR(0), _address(0) {}

void PCF8574::begin(uint8_t address) {
    _address = address;
    Wire.begin();
    readGPIO(); // Initialize _PIN with current GPIO states
}

void PCF8574::pinMode(uint8_t pin, uint8_t mode) {
    if (mode == OUTPUT) {
        _DDR |= (1 << pin);
    } else {
        _DDR &= ~(1 << pin);
    }
    updateGPIO();
}

void PCF8574::digitalWrite(uint8_t pin, uint8_t value) {
    if (value == HIGH) {
        _PORT |= (1 << pin);
    } else {
        _PORT &= ~(1 << pin);
    }
    updateGPIO();
}

uint8_t PCF8574::digitalRead(uint8_t pin) {
    readGPIO();
    return (_PIN >> pin) & 1;
}

void PCF8574::write(uint8_t value) {
    _PORT = value;
    updateGPIO();
}

uint8_t PCF8574::read() {
    readGPIO();
    return _PIN;
}

void PCF8574::clear() {
    _PORT = 0;
    updateGPIO();
}

void PCF8574::set() {
    _PORT = 0xFF;
    updateGPIO();
}

void PCF8574::toggle(uint8_t pin) {
    _PORT ^= (1 << pin);
    updateGPIO();
}

void PCF8574::pullUp(uint8_t pin) {
    // Do nothing, no software pull-up resistors available
}

void PCF8574::pullDown(uint8_t pin) {
    // Do nothing, no software pull-down resistors available
}

void PCF8574::blink(uint8_t pin, uint16_t count, uint32_t duration) {
    uint32_t interval = duration / (count * 2);
    for (uint16_t i = 0; i < count; i++) {
        digitalWrite(pin, HIGH);
        delay(interval);
        digitalWrite(pin, LOW);
        delay(interval);
    }
}

void PCF8574::readGPIO() {
    Wire.requestFrom(_address, (uint8_t)1);
    if (Wire.available()) {
        _PIN = I2CREAD();
    }
}

void PCF8574::updateGPIO() {
    readGPIO(); // Ensure we have the latest input states
    Wire.beginTransmission(_address);
    I2CWRITE(_PORT & _DDR); // Only set output pins
    Wire.endTransmission();
}