import gc
import time
import math
import badger2040
from badger2040 import WIDTH
import launchericons
import badger_os

# Reduce clock speed to 48MHz
badger2040.system_speed(badger2040.SYSTEM_NORMAL)

changed = False
exited_to_launcher = False
woken_by_button = badger2040.woken_by_button()  # Must be done before we clear_pressed_to_wake

if badger2040.pressed_to_wake(badger2040.BUTTON_A) and badger2040.pressed_to_wake(badger2040.BUTTON_C):
    # Pressing A and C together at start quits app
    exited_to_launcher = badger_os.state_clear_running()
else:
    # Otherwise restore previously running app
    badger_os.state_launch()

# for e.g. 2xAAA batteries, try max 3.4 min 3.0
MAX_BATTERY_VOLTAGE = 4.0
MIN_BATTERY_VOLTAGE = 3.2

display = badger2040.Badger2040()
display.led(128)

state = {
    "page": 0,
    "font_size": 1,
    "inverted": False,
    "running": "launcher"
}

badger_os.state_load("launcher", state)

display.invert(state["inverted"])

icons = bytearray(launchericons.data())
icons_width = 576

examples = [
    ("_clock", 0),
    ("_fonts", 1),
    ("_ebook", 2),
    ("_image", 3),
    ("_list", 4),
    ("_badge", 5),
    ("_qrgen", 8),
    ("_info", 6),
    ("_help", 7),
]

font_sizes = (0.5, 0.7, 0.9)

# Approximate center lines for buttons A, B and C
centers = (41, 147, 253)

MAX_PAGE = math.ceil(len(examples) / 3)


def map_value(input, in_min, in_max, out_min, out_max):
    return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min


def draw_battery(level, x, y):
    # Outline
    display.thickness(1)
    display.pen(15)
    display.rectangle(x, y, 19, 10)
    # Terminal
    display.rectangle(x + 19, y + 3, 2, 4)
    display.pen(0)
    display.rectangle(x + 1, y + 1, 17, 8)
    if level < 1:
        display.pen(0)
        display.line(x + 3, y, x + 3 + 10, y + 10)
        display.line(x + 3 + 1, y, x + 3 + 11, y + 10)
        display.pen(15)
        display.line(x + 2 + 2, y - 1, x + 4 + 12, y + 11)
        display.line(x + 2 + 3, y - 1, x + 4 + 13, y + 11)
        return
    # Battery Bars
    display.pen(15)
    for i in range(4):
        if level / 4 > (1.0 * i) / 4:
            display.rectangle(i * 4 + x + 2, y + 2, 3, 6)


def draw_disk_usage(x):
    _, f_used, _ = badger_os.get_disk_usage()

    display.image(
        bytearray(
            (
                0b00000000,
                0b00111100,
                0b00111100,
                0b00111100,
                0b00111000,
                0b00000000,
                0b00000000,
                0b00000001,
            )
        ),
        8,
        8,
        x,
        4,
    )
    display.pen(15)
    display.rectangle(x + 10, 3, 80, 10)
    display.pen(0)
    display.rectangle(x + 11, 4, 78, 8)
    display.pen(15)
    display.rectangle(x + 12, 5, int(76 / 100.0 * f_used), 6)
    display.text("{:.2f}%".format(f_used), x + 91, 8, 0.4)


def render():
    display.pen(15)
    display.clear()
    display.pen(0)
    display.thickness(2)

    max_icons = min(3, len(examples[(state["page"] * 3):]))

    for i in range(max_icons):
        x = centers[i]
        label, icon = examples[i + (state["page"] * 3)]
        label = label[1:].replace("_", " ")
        display.pen(0)
        display.icon(icons, icon, icons_width, 64, x - 32, 24)
        w = display.measure_text(label, font_sizes[state["font_size"]])
        display.text(label, x - int(w / 2), 16 + 80, font_sizes[state["font_size"]])

    for i in range(MAX_PAGE):
        x = 286
        y = int((128 / 2) - (MAX_PAGE * 10 / 2) + (i * 10))
        display.pen(0)
        display.rectangle(x, y, 8, 8)
        if state["page"] != i:
            display.pen(15)
            display.rectangle(x + 1, y + 1, 6, 6)

    display.pen(0)
    display.rectangle(0, 0, WIDTH, 16)
    display.thickness(1)
    draw_disk_usage(90)
    vbat = badger_os.get_battery_level()
    bat = int(map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, 4))
    draw_battery(bat, WIDTH - 22 - 3, 3)
    display.pen(15)
    display.text("badgerOS", 3, 8, 0.4)

    display.update()


def wait_for_user_to_release_buttons():
    pr = display.pressed
    while pr(badger2040.BUTTON_A) or pr(badger2040.BUTTON_B) or pr(badger2040.BUTTON_C) or pr(badger2040.BUTTON_UP) or pr(badger2040.BUTTON_DOWN):
        time.sleep(0.01)


def launch_example(index):
    wait_for_user_to_release_buttons()

    file = examples[(state["page"] * 3) + index][0]

    for k in locals().keys():
        if k not in ("gc", "file", "badger_os"):
            del locals()[k]

    gc.collect()

    badger_os.launch(file)


def button(pin):
    global changed
    changed = True

    if not display.pressed(badger2040.BUTTON_USER):  # User button is NOT held down
        if pin == badger2040.BUTTON_A:
            launch_example(0)
        if pin == badger2040.BUTTON_B:
            launch_example(1)
        if pin == badger2040.BUTTON_C:
            launch_example(2)
        if pin == badger2040.BUTTON_UP:
            if state["page"] > 0:
                state["page"] -= 1
            render()
        if pin == badger2040.BUTTON_DOWN:
            if state["page"] < MAX_PAGE - 1:
                state["page"] += 1
            render()
    else:  # User button IS held down
        if pin == badger2040.BUTTON_UP:
            state["font_size"] += 1
            if state["font_size"] == len(font_sizes):
                state["font_size"] = 0
            render()
        if pin == badger2040.BUTTON_DOWN:
            state["font_size"] -= 1
            if state["font_size"] < 0:
                state["font_size"] = 0
            render()
        if pin == badger2040.BUTTON_A:
            state["inverted"] = not state["inverted"]
            display.invert(state["inverted"])
            render()


if exited_to_launcher or not woken_by_button:
    wait_for_user_to_release_buttons()
    display.update_speed(badger2040.UPDATE_MEDIUM)
    render()

display.update_speed(badger2040.UPDATE_FAST)

while True:
    if display.pressed(badger2040.BUTTON_A):
        button(badger2040.BUTTON_A)
    if display.pressed(badger2040.BUTTON_B):
        button(badger2040.BUTTON_B)
    if display.pressed(badger2040.BUTTON_C):
        button(badger2040.BUTTON_C)

    if display.pressed(badger2040.BUTTON_UP):
        button(badger2040.BUTTON_UP)
    if display.pressed(badger2040.BUTTON_DOWN):
        button(badger2040.BUTTON_DOWN)

    if changed:
        badger_os.state_save("launcher", state)
        changed = False

    display.halt()
