from lcd_api import LcdApi
from machine import Pin
from utime import sleep_ms, sleep_us

class GpioLcd(LcdApi):
    def __init__(self, rs_pin, enable_pin, d0_pin=None, d1_pin=None,
                 d2_pin=None, d3_pin=None, d4_pin=None, d5_pin=None,
                 d6_pin=None, d7_pin=None, rw_pin=None, backlight_pin=None,
                 num_lines=2, num_columns=16):
        self.rs_pin = rs_pin
        self.enable_pin = enable_pin
        self.rw_pin = rw_pin
        self.backlight_pin = backlight_pin
        self._4bit = True
        if d4_pin and d5_pin and d6_pin and d7_pin:
            self.d0_pin = d0_pin
            self.d1_pin = d1_pin
            self.d2_pin = d2_pin
            self.d3_pin = d3_pin
            self.d4_pin = d4_pin
            self.d5_pin = d5_pin
            self.d6_pin = d6_pin
            self.d7_pin = d7_pin
            if self.d0_pin and self.d1_pin and self.d2_pin and self.d3_pin:
                self._4bit = False
        else:
            self.d0_pin = None
            self.d1_pin = None
            self.d2_pin = None
            self.d3_pin = None
            self.d4_pin = d0_pin
            self.d5_pin = d1_pin
            self.d6_pin = d2_pin
            self.d7_pin = d3_pin
        self.rs_pin.init(Pin.OUT)
        self.rs_pin.off()
        if self.rw_pin:
            self.rw_pin.init(Pin.OUT)
            self.rw_pin.off()
        self.enable_pin.init(Pin.OUT)
        self.enable_pin.off()
        self.d4_pin.init(Pin.OUT)
        self.d5_pin.init(Pin.OUT)
        self.d6_pin.init(Pin.OUT)
        self.d7_pin.init(Pin.OUT)
        self.d4_pin.off()
        self.d5_pin.off()
        self.d6_pin.off()
        self.d7_pin.off()
        if not self._4bit:
            self.d0_pin.init(Pin.OUT)
            self.d1_pin.init(Pin.OUT)
            self.d2_pin.init(Pin.OUT)
            self.d3_pin.init(Pin.OUT)
            self.d0_pin.off()
            self.d1_pin.off()
            self.d2_pin.off()
            self.d3_pin.off()
        if self.backlight_pin is not None:
            self.backlight_pin.init(Pin.OUT)
            self.backlight_pin.off()
        sleep_ms(20)
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(5)
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(1)
        self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
        sleep_ms(1)
        cmd = self.LCD_FUNCTION
        if not self._4bit:
            cmd |= self.LCD_FUNCTION_8BIT
        self.hal_write_init_nibble(cmd)
        sleep_ms(1)
        LcdApi.__init__(self, num_lines, num_columns)
        if num_lines > 1:
            cmd |= self.LCD_FUNCTION_2LINES
        self.hal_write_command(cmd)
    def hal_pulse_enable(self):
        self.enable_pin.off()
        sleep_us(1)
        self.enable_pin.on()
        sleep_us(1)
        self.enable_pin.off()
        sleep_us(100)
    def hal_write_init_nibble(self, nibble):
        self.hal_write_4bits(nibble >> 4)
    def hal_backlight_on(self):
        if self.backlight_pin:
            self.backlight_pin.on()
    def hal_backlight_off(self):
        if self.backlight_pin:
            self.backlight_pin.off()
    def hal_write_command(self, cmd):
        self.rs_pin.off()
        self.hal_write_8bits(cmd)
        if cmd <= 3:
            sleep_ms(5)
    def hal_write_data(self, data):
        self.rs_pin.on()
        self.hal_write_8bits(data)
    def hal_write_8bits(self, value):
        if self.rw_pin:
            self.rw_pin.off()
        if self._4bit:
            self.hal_write_4bits(value >> 4)
            self.hal_write_4bits(value)
        else:
            self.d3_pin.value(value & 0x08)
            self.d2_pin.value(value & 0x04)
            self.d1_pin.value(value & 0x02)
            self.d0_pin.value(value & 0x01)
            self.hal_write_4bits(value >> 4)
    def hal_write_4bits(self, nibble):
        self.d7_pin.value(nibble & 0x08)
        self.d6_pin.value(nibble & 0x04)
        self.d5_pin.value(nibble & 0x02)
        self.d4_pin.value(nibble & 0x01)
        self.hal_pulse_enable()
