#!/usr/bin/env python3
import buttons
import display
import os
import struct
import utime

MODES = [
    "single",  # show single picture, switch with buttons on the right
    "rotate",  # rotate pictures
]
BTN_LISTEN = buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT


def draw_bmp(filename):
    with open(filename, "rb+") as bmpfile:
        header, filesize, _, _, data_offset = struct.unpack("<HIHHI", bmpfile.read(14))
        if header != 0x4D42:
            raise ValueError("Only Windows BMP supported")
        bmpinfo = bmpfile.read(data_offset - 14)
        width, height, _, bpp, compr = struct.unpack("<IIHHI", bmpinfo[4:20])
        if bpp != 24:
            raise ValueError("Only 24 bpp BMP supported")
        if compr != 0:
            raise ValueError("CompressValueError not supported")
        pad = (width * -3) % 4
        if width > 161:
            raise ValueError("Maximum width 161 supported")
        if height > 81:
            raise ValueError("Maximum height 81 supported")

        pressed = 0
        data = bmpfile.read(512)
        i = 2
        with display.open() as d:
            d.clear()
            for y in range(height - 1, -1, -1):
                for x in range(width):
                    if i >= len(data):
                        i = 2 + (i - len(data))
                        data = data[-2:] + bmpfile.read(512)
                    d.pixel(x, y, col=(data[i], data[i - 1], data[i - 2]))
                    i += 3
                i += pad  # skip padding
                pressed = buttons.read(BTN_LISTEN) or pressed
                if pressed:
                    break
            d.update()
        return pressed


def message(msg, delay=0):
    with display.open() as disp:
        disp.clear()
        posy = 0
        while msg and posy < 75:
            cur, msg = msg[:11], msg[11:]
            disp.print(cur, posx=0, posy=posy)
            posy += 18
        disp.update()
    return utime.time_ms() + int(delay * 1000)


def main():
    if "pics" not in os.listdir("/"):
        message("No pics dir found")
        utime.sleep(5)
        return
    pics = list(
        sorted(
            [
                "/pics/{}".format(fn)
                for fn in os.listdir("/pics")
                if fn.lower().endswith(".bmp")
            ],
            key=lambda x: x.lower(),
        )
    )
    if not pics:
        message("No BMP images found in /pics")
        utime.sleep(5)
        return
    pic_i = 0  # current picture index
    pic_dirty = True  # whether a new picture should be drawn on screen
    mode = MODES[0]  # show single picture
    delay = 5.0  # delay between pictures in rotate mode
    last_update = 0  # last time a new picture was displayed
    last_press = 0  # last time a button press was acted upon
    # used when the screen should not be updated for a while, except when
    # responding to button presses:
    sleep_until = 0
    pressed = 0
    while True:
        # Listen for buttons and change state as needed
        pressed = pressed or buttons.read(BTN_LISTEN)
        if pressed:
            if utime.time_ms() - last_press < 500:
                pressed = 0  # only accept key press every half second
            else:
                last_press = utime.time_ms()
        else:
            last_press = 0
        if pressed == buttons.BOTTOM_LEFT:  # bottom left: switch mode
            if mode == MODES[0]:
                mode = MODES[1]
            else:
                mode = MODES[0]
                pic_dirty = True
            sleep_until = message("Mode:{}".format(mode), 0.5)
        elif pressed == buttons.BOTTOM_RIGHT:
            if mode == MODES[0]:
                pic_i = (pic_i + 1) % len(pics)
                pic_dirty = True
                sleep_until = message(pics[pic_i][6:-4], 0.5)
            else:
                delay = delay * 1.5
                sleep_until = message("Delay:{:.1f} seconds".format(delay), 1)
        elif pressed == buttons.TOP_RIGHT:
            if mode == MODES[0]:
                pic_i = (pic_i - 1) % len(pics)
                pic_dirty = True
                sleep_until = message(pics[pic_i][6:-4], 0.5)
            else:
                delay = max(0.1, delay / 1.5)
                sleep_until = message("Delay:{:.1f} seconds".format(delay), 0.5)
        pressed = 0

        if mode == MODES[1]:
            if utime.time_ms() - last_update > int(delay * 1000):
                pic_i = (pic_i + 1) % len(pics)
                pic_dirty = True

        if pic_dirty and sleep_until < utime.time_ms():
            pic_dirty = False
            last_update = utime.time_ms()
            try:
                pressed = draw_bmp(pics[pic_i])
            except Exception as e:
                message("{}: {}".format(pics[pic_i], str(e)))

        utime.sleep(0.1)


main()
