import logging
import epdconfig

from PIL import Image

import time

# Display resolution
width  = 800
height = 480

BLACK  = 0x000000 # 00 BGR
WHITE  = 0xffffff # 01
YELLOW = 0x00ffff # 10
RED    = 0x0000ff # 11

logger = logging.getLogger(__name__)

# Hardware reset
def reset():
    epdconfig.GPIO_RST_PIN.on()
    time.sleep(200/1000)
    epdconfig.GPIO_RST_PIN.off() # module reset
    time.sleep(2/1000)
    epdconfig.GPIO_RST_PIN.on()
    time.sleep(200/1000)

def send_command(*command):
    if command:
        epdconfig.GPIO_DC_PIN.off()
        epdconfig.spi_writebyte2(command[:1])
        if data := command[1:]:
            epdconfig.GPIO_DC_PIN.on()
            epdconfig.spi_writebyte2(data)

def wait_for_busy_hi():
    logger.debug("e-Paper busy H")
    while not epdconfig.GPIO_BUSY_PIN.value: # 0: idle, 1: busy
        time.sleep(5/1000)
    logger.debug("e-Paper busy H release")

def wait_for_busy_lo():
    logger.debug("e-Paper busy L")
    while epdconfig.GPIO_BUSY_PIN.value: # 0: busy, 1: idle
        time.sleep(5/1000)
    logger.debug("e-Paper busy L release")

def display_on():
    send_command(0x12, 0x01) # DISPLAY_REFRESH
    wait_for_busy_hi()

    send_command(0x02, 0x00) # POWER_OFF
    wait_for_busy_hi()

def init():
    epdconfig.module_init()

    # EPD hardware init start
    reset()
    wait_for_busy_hi()
    time.sleep(30/1000)

    send_command(0xAA, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18)
    send_command(0x01, 0x3F)
    send_command(0x00, 0x4F, 0x69)
    send_command(0x05, 0x40, 0x1F, 0x1F, 0x2C)
    send_command(0x08, 0x6F, 0x1F, 0x1F, 0x22)
    # ===================
    # 20211212
    # First setting
    send_command(0x06, 0x6F, 0x1F, 0x14, 0x14)
    # ===================
    send_command(0x03, 0x00, 0x54, 0x00, 0x44)
    send_command(0x60, 0x02, 0x00)
    # Please notice that PLL must be set for version 2 IC
    send_command(0x30, 0x08)
    send_command(0x50, 0x3F)
    send_command(0x61, 0x03, 0x20, 0x01, 0xE0)
    send_command(0xE3, 0x2F)
    send_command(0x84, 0x01)

def get_buffer(image):
    # Create a pallette with the 4 colors supported by the panel
    pal_image = Image.new("P", (1,1))
    pal_image.putpalette((0x00,0x00,0x00, 0xff,0xff,0xff, 0xff,0xff,0x00, 0xff,0x00,0x00) + (0x00,0x00,0x00)*252)

    # Check if we need to rotate the image
    if image.size == (width, height):
        image_temp = image
    elif image.size == (height, width):
        image_temp = image.rotate(90, expand=True)
    else:
        raise RuntimeError("Invalid image dimensions: %d x %d, expected %d x %d" % (image.size[0], image.height[1], width, height))

    # Convert the source image to the 4 colors, dithering if needed
    image_4color = image_temp.convert("RGB").quantize(palette=pal_image)
    buf_4color = bytearray(image_4color.tobytes('raw'))

    # into a single byte to transfer to the panel
    buf = [0x00] * int(width * height / 4)
    for idx in range(len(buf_4color) // 4):
        buf[idx] = (buf_4color[idx*4] << 6) + (buf_4color[idx*4+1] << 4) + (buf_4color[idx*4+2] << 2) + buf_4color[idx*4+3]
    return buf

def display(image):
    assert len(image) == width * height / 4

    send_command(0x04)
    wait_for_busy_hi()

    send_command(*([0x10] + image))
    display_on()

def clear(color=0x55):
    assert not width % 4
    iwidth = width // 4

    send_command(0x04)
    wait_for_busy_hi()

    send_command(*([0x10] + [color]*(height * iwidth)))
    display_on()

def sleep():
    send_command(0x02, 0x00) # POWER_OFF
    send_command(0x07, 0xa5) # DEEP_SLEEP
    time.sleep(2)
    epdconfig.module_exit()
