#include <avr/io.h>
#include <util/delay.h>

#define I2C_ADDR       0x27  // I2C address for the LCD
#define LCD_COLUMNS    20    // Number of columns on the LCD
#define LCD_LINES      4     // Number of lines on the LCD
#define LCD_BACKLIGHT  0x08  // Backlight bit for the LCD
#define LCD_ENABLE_BIT 0x04  // Enable bit for the LCD

// I2C functions to replace Wire.h functionality
void i2c_init() {
    TWSR = 0x00;               // Set prescaler to 1
    TWBR = 0x12;               // Set SCL frequency (100 kHz if F_CPU is 16MHz)
    //TWCR = (1 << TWEN);        // Enable TWI
}

void i2c_start() {
    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Send START condition
    while (!(TWCR & (1 << TWINT)));                   // Wait for TWINT flag set
}

void i2c_stop() {
    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); // Send STOP condition
    while (TWCR & (1 << TWSTO));                      // Wait for STOP to finish
}

void i2c_write(uint8_t data) {
    TWDR = data;                                      // Load data into TWDR register
    TWCR = (1 << TWINT) | (1 << TWEN);                // Start transmission
    while (!(TWCR & (1 << TWINT)));                   // Wait for TWINT flag
}

// LCD functions
void lcd_expanderWrite(uint8_t data) {
    i2c_start();
    i2c_write((I2C_ADDR << 1) | 0);                   // Send address with write bit
    i2c_write(data | LCD_BACKLIGHT);                  // Write data with backlight
    i2c_stop();
}

void lcd_pulseEnable(uint8_t data) {
    lcd_expanderWrite(data | LCD_ENABLE_BIT);         // Pulse enable bit
    _delay_us(1);
    lcd_expanderWrite(data & ~LCD_ENABLE_BIT);
    _delay_us(50);
}

void lcd_write4Bits(uint8_t value) {
    lcd_expanderWrite(value);
    lcd_pulseEnable(value);
}

void lcd_send(uint8_t value, uint8_t mode) {
    uint8_t highNibble = value & 0xF0;
    uint8_t lowNibble = (value << 4) & 0xF0;
    lcd_write4Bits(highNibble | mode);
    lcd_write4Bits(lowNibble | mode);
}

void lcd_sendCommand(uint8_t command) {
    lcd_send(command, 0);
}

void lcd_sendData(uint8_t data) {
    lcd_send(data, 0x01);
}

void lcd_init() {
    i2c_init();
    _delay_ms(50);

    lcd_sendCommand(0x33); // Initialize in 8-bit mode
    lcd_sendCommand(0x32); // Switch to 4-bit mode
    lcd_sendCommand(0x28); // Set to 2-line, 5x8 dots per character
    lcd_sendCommand(0x0C); // Display on, cursor off
    lcd_sendCommand(0x06); // Set entry mode
    lcd_sendCommand(0x01); // Clear display
    _delay_ms(2);
}

void lcd_setCursor(uint8_t col, uint8_t row) {
    uint8_t row_offsets[] = {0x00, 0x40, 0x14, 0x54};
    if (row >= LCD_LINES) row = LCD_LINES - 1;
    lcd_sendCommand(0x80 | (col + row_offsets[row]));
}

void lcd_print(const char* str) {
    while (*str) {
        lcd_sendData(*str++);
    }
}

void lcd_backlight() {
    lcd_expanderWrite(0x08); // Turn on backlight
}

void setup1() {
    lcd_init();
    lcd_backlight();

    lcd_setCursor(3, 0);
    lcd_print("Hello, world!");
    lcd_setCursor(2, 1);
    lcd_print("Wokwi Online IoT");
    lcd_setCursor(5, 2);
    lcd_print("Simulator");
    lcd_setCursor(7, 3);
    lcd_print("Enjoy!");
}

int main(void) {
    setup1();
    while (1) {
        // Main loop does nothing in this example
    }
}