# e_ink_library.py
# Waveshare 2.7 inch EPD library for Raspberry Pi Pico / Pico 2 W

from machine import Pin, SPI
import framebuf
import utime
import time
from logo import youtube_logo



import uasyncio as asyncio

# =========================
# DISPLAY RESOLUTION
# =========================
EPD_WIDTH  = 176
EPD_HEIGHT = 264

RST_PIN  = 12
DC_PIN   = 8
CS_PIN   = 9
BUSY_PIN = 13

# =========================
# DRIVER CLASS (FROM WAVESHARE)
# =========================
class EPD_2in7_V2:
    def __init__(self):
        self.reset_pin = Pin(RST_PIN, Pin.OUT)
        self.busy_pin = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP)
        self.cs_pin = Pin(CS_PIN, Pin.OUT)
        self.dc_pin = Pin(DC_PIN, Pin.OUT)

        self.width = EPD_WIDTH
        self.height = EPD_HEIGHT

        self.black = 0x00
        self.white = 0xff

        self.spi = SPI(1)
        self.spi.init(baudrate=4000000)
        self.rotation = 0   # 0=portrait, 1=landscape
        self.buffer_black = bytearray(self.width * self.height // 8)
        self.imageblack = framebuf.FrameBuffer(self.buffer_black, self.width, self.height, framebuf.MONO_HLSB)

        self.init()
        self.clear()
        utime.sleep_ms(500)

    def reset(self):
        self.reset_pin.value(1)
        time.sleep(0.2)
        self.reset_pin.value(0)
        time.sleep(0.01)
        self.reset_pin.value(1)
        time.sleep(0.2)

    def send_command(self, cmd):
        self.dc_pin.value(0)
        self.cs_pin.value(0)
        self.spi.write(bytearray([cmd]))
        self.cs_pin.value(1)

    def send_data(self, data):
        self.dc_pin.value(1)
        self.cs_pin.value(0)
        self.spi.write(bytearray([data]))
        self.cs_pin.value(1)

    def send_data_buf(self, buf):
        self.dc_pin.value(1)
        self.cs_pin.value(0)
        self.spi.write(buf)
        self.cs_pin.value(1)

    def wait_busy(self):
        while self.busy_pin.value() == 1:
            time.sleep(0.01)

    def init(self):
        self.reset()
        self.send_command(0x12)
        self.wait_busy()

        self.send_command(0x11)
        self.send_data(0x03)

        self.send_command(0x44)
        self.send_data(0x00)
        self.send_data(0x15)

        self.send_command(0x45)
        self.send_data(0x00)
        self.send_data(0x00)
        self.send_data(0x07)
        self.send_data(0x01)

    def clear(self):
        self.imageblack.fill(1)
        self.display()

    def display(self):
    # portrait normal
        if self.rotation == 0:

            self.send_command(0x24)
            self.send_data_buf(self.buffer_black)

        # landscape proper
        else:

            rot_buf = bytearray(176 * 264 // 8)
            rot = framebuf.FrameBuffer(rot_buf, 176, 264, framebuf.MONO_HLSB)
            rot.fill(1)

            # logical landscape canvas = 264 x 176
            for y in range(176):
                for x in range(264):

                    if self.imageblack.pixel(x, y) == 0:

                        # rotate clockwise properly
                        px = y
                        py = 263 - x

                        rot.pixel(px, py, 0)

            self.send_command(0x24)
            self.send_data_buf(rot_buf)

        self.send_command(0x22)
        self.send_data(0xF7)
        self.send_command(0x20)
        self.wait_busy()

    def sleep(self):
        self.send_command(0x10)
        self.send_data(0x01)
        
    def set_rotation(self, mode):
        if mode == "portrait":
            self.rotation = 0
            self.width = 176
            self.height = 264

        else:

            self.rotation = 1
            self.width = 264
            self.height = 176

        self.buffer_black = bytearray(self.width * self.height // 8)

        self.imageblack = framebuf.FrameBuffer(
            self.buffer_black,
            self.width,
            self.height,
            framebuf.MONO_HLSB
        )

    def display_partial(self):

        if self.rotation == 0:

            self.reset()
            self.send_command(0x3C)
            self.send_data(0x80)

            self.send_command(0x24)
            self.send_data_buf(self.buffer_black)

            self.send_command(0x22)
            self.send_data(0xFF)
            self.send_command(0x20)
            self.wait_busy()

        else:

            # rotate landscape -> portrait first
            rot_buf = bytearray(176 * 264 // 8)
            rot = framebuf.FrameBuffer(rot_buf, 176, 264, framebuf.MONO_HLSB)
            rot.fill(1)

            for y in range(176):
                for x in range(264):

                    if self.imageblack.pixel(x, y) == 0:
                        rot.pixel(y, 263 - x, 0)

            self.reset()
            self.send_command(0x3C)
            self.send_data(0x80)

            self.send_command(0x24)
            self.send_data_buf(rot_buf)

            self.send_command(0x22)
            self.send_data(0xFF)
            self.send_command(0x20)
            self.wait_busy()

