import os
import display
import leds
import buttons
import utime

def ceilDiv(a, b):
    return (a + (b - 1)) // b

def tipHeight(w):
    return ceilDiv(w, 2) - 1

def drawTip(d, x, y, w, c, invert = False, swapAxes = False):
    h = tipHeight(w)
    for dy in range(h):
        for dx in range(dy + 1, w - 1 - dy):
            px = x + dx
            py = y + dy if not invert else y + h - 1 - dy
            if swapAxes:
                px, py = py, px
            d.pixel(px, py, col = c)

def drawSeg(d, x, y, w, h, c, swapAxes = False):
    tip_h = tipHeight(w)
    body_h = h - 2 * tip_h

    drawTip(d, x, y, w, c, invert = True, swapAxes = swapAxes)

    px1, px2 = x, x + w
    py1, py2 = y + tip_h, y + tip_h + body_h
    if swapAxes:
        px1, px2, py1, py2 = py1, py2, px1, px2
    d.rect(px1, py1, px2, py2, col = c)

    drawTip(d, x, y + tip_h + body_h, w, c, invert = False, swapAxes = swapAxes)

def drawVSeg(d, x, y, w, l, c):
    drawSeg(d, x, y, w, l, c)

def drawHSeg(d, x, y, w, l, c):
    drawSeg(d, y, x, w, l, c, swapAxes = True)

def drawGridSeg(d, x, y, w, l, c, swapAxes = False):
    sw = w - 2
    tip_h = tipHeight(sw)

    x = x * w
    y = y * w
    l = (l - 1) * w
    drawSeg(d, x + 1, y + tip_h + 3, sw, l - 3, c, swapAxes = swapAxes)

def drawGridVSeg(d, x, y, w, l, c):
    drawGridSeg(d, x, y, w, l, c)

def drawGridHSeg(d, x, y, w, l, c):
    drawGridSeg(d, y, x, w, l, c, swapAxes = True)

def drawGrid(d, x1, y1, x2, y2, w, c):
    for x in range(x1 * w, x2 * w):
        for y in range(y1 * w, y2 * w):
            if x % w == 0 or x % w == w - 1 or y % w == 0 or y % w == w - 1:
                d.pixel(x, y, col = c)

def drawGrid7Seg(d, x, y, w, segs, c):
    if segs[0]:
        drawGridHSeg(d, x, y, w, 4, c)
    if segs[1]:
        drawGridVSeg(d, x + 3, y, w, 4, c)
    if segs[2]:
        drawGridVSeg(d, x + 3, y + 3, w, 4, c)
    if segs[3]:
        drawGridHSeg(d, x, y + 6, w, 4, c)
    if segs[4]:
        drawGridVSeg(d, x, y + 3, w, 4, c)
    if segs[5]:
        drawGridVSeg(d, x, y, w, 4, c)
    if segs[6]:
        drawGridHSeg(d, x, y + 3, w, 4, c)

DIGITS = [
    (True, True, True, True, True, True, False),
    (False, True, True, False, False, False, False),
    (True, True, False, True, True, False, True),
    (True, True, True, True, False, False, True),
    (False, True, True, False, False, True, True),
    (True, False, True, True, False, True, True),
    (True, False, True, True, True, True, True),
    (True, True, True, False, False, False, False),
    (True, True, True, True, True, True, True),
    (True, True, True, True, False, True, True)
]

def renderNum(d, num, x):
    drawGrid7Seg(d, x, 0, 7, DIGITS[num // 10], (255, 255, 255))
    drawGrid7Seg(d, x + 5, 0, 7, DIGITS[num % 10], (255, 255, 255))

def renderColon(d):
    drawGridVSeg(d, 11, 2, 7, 2, (255, 255, 255))
    drawGridVSeg(d, 11, 4, 7, 2, (255, 255, 255))

def renderText(d, text, blankidx = None):
    bs = bytearray(text)

    if blankidx != None:
        bs[blankidx:blankidx+1] = b'_'

    d.print(MODES[MODE] + ' ' + bs.decode(), fg = (255, 255, 255), bg = None, posx = 0, posy = 7 * 8)

def renderBar(d, num):
    d.rect(20, 78, 20 + num * 2, 80, col = (255, 255, 255))

def render(d):
    ltime = utime.localtime()
    years = ltime[0]
    months = ltime[1]
    days = ltime[2]
    hours = ltime[3]
    mins = ltime[4]
    secs = ltime[5]

    d.clear()

    if MODE == CHANGE_YEAR:
        renderNum(d, years // 100, 1)
        renderNum(d, years % 100, 13)
    elif MODE == CHANGE_MONTH:
        renderNum(d, months, 13)
    elif MODE == CHANGE_DAY:
        renderNum(d, days, 13)
    else:
        renderNum(d, hours, 1)
        renderNum(d, mins, 13)

    if MODE not in (CHANGE_YEAR, CHANGE_MONTH, CHANGE_DAY) and secs % 2 == 0:
        renderColon(d)

    renderText(d, NAME, None)
    renderBar(d, secs)

    d.update()

BUTTON_SEL = 1 << 0
BUTTON_UP = 1 << 1
BUTTON_DOWN = 1 << 2
BUTTON_SEL_LONG = 1 << 3
BUTTON_UP_LONG = 1 << 4
BUTTON_DOWN_LONG = 1 << 5
pressed_prev = 0
button_sel_time = 0
button_up_time = 0
button_down_time = 0
def checkButtons():
    global pressed_prev, button_sel_time, button_up_time, button_down_time

    t = utime.time()
    pressed = buttons.read(buttons.BOTTOM_LEFT | buttons.TOP_RIGHT | buttons.BOTTOM_RIGHT)
    cur_buttons = 0

    if pressed & buttons.BOTTOM_LEFT and not pressed_prev & buttons.BOTTOM_LEFT:
        button_sel_time = t
    elif not pressed & buttons.BOTTOM_LEFT and pressed_prev & buttons.BOTTOM_LEFT:
        if button_sel_time < t:
            cur_buttons |= BUTTON_SEL_LONG
        else:
            cur_buttons |= BUTTON_SEL

    if pressed & buttons.TOP_RIGHT and not pressed_prev & buttons.TOP_RIGHT:
        button_sel_time = t
    elif not pressed & buttons.TOP_RIGHT and pressed_prev & buttons.TOP_RIGHT:
        if button_sel_time < t:
            cur_buttons |= BUTTON_UP_LONG
        else:
            cur_buttons |= BUTTON_UP

    if pressed & buttons.BOTTOM_RIGHT and not pressed_prev & buttons.BOTTOM_RIGHT:
        button_sel_time = t
    elif not pressed & buttons.BOTTOM_RIGHT and pressed_prev & buttons.BOTTOM_RIGHT:
        if button_sel_time < t:
            cur_buttons |= BUTTON_DOWN_LONG
        else:
            cur_buttons |= BUTTON_DOWN

    pressed_prev = pressed
    return cur_buttons

def modTime(yrs, mth, day, hrs, mns, sec):
    ltime = utime.localtime()
    new = utime.mktime((ltime[0] + yrs, ltime[1] + mth, ltime[2] + day, ltime[3] + hrs, ltime[4] + mns, ltime[5] + sec, None, None))
    utime.set_time(new + WORKAROUND_OFFSET)

def ctrl_display(bs):
    global MODE
    if bs & BUTTON_SEL_LONG:
        MODE = CHANGE_HOURS

def ctrl_chg_hrs(bs):
    global MODE
    if bs & BUTTON_SEL_LONG:
        MODE = DISPLAY
    if bs & BUTTON_SEL:
        MODE = CHANGE_MINUTES
    if bs & BUTTON_UP or bs & BUTTON_UP_LONG:
        modTime(0, 0, 0, 1, 0, 0)
    if bs & BUTTON_DOWN or bs & BUTTON_DOWN_LONG:
        modTime(0, 0, 0, -1, 0, 0)

def ctrl_chg_mns(bs):
    global MODE
    if bs & BUTTON_SEL_LONG:
        MODE = DISPLAY
    if bs & BUTTON_SEL:
        MODE = CHANGE_SECONDS
    if bs & BUTTON_UP or bs & BUTTON_UP_LONG:
        modTime(0, 0, 0, 0, 1, 0)
    if bs & BUTTON_DOWN or bs & BUTTON_DOWN_LONG:
        modTime(0, 0, 0, 0, -1, 0)

def ctrl_chg_sec(bs):
    global MODE
    if bs & BUTTON_SEL_LONG:
        MODE = DISPLAY
    if bs & BUTTON_SEL:
        MODE = CHANGE_YEAR
    if bs & BUTTON_UP or bs & BUTTON_UP_LONG:
        modTime(0, 0, 0, 0, 0, 1)
    if bs & BUTTON_DOWN or bs & BUTTON_DOWN_LONG:
        modTime(0, 0, 0, 0, 0, -1)

def ctrl_chg_yrs(bs):
    global MODE
    if bs & BUTTON_SEL_LONG:
        MODE = DISPLAY
    if bs & BUTTON_SEL:
        MODE = CHANGE_MONTH
    if bs & BUTTON_UP or bs & BUTTON_UP_LONG:
        modTime(1, 0, 0, 0, 0, 0)
    if bs & BUTTON_DOWN or bs & BUTTON_DOWN_LONG:
        modTime(-1, 0, 0, 0, 0, 0)

def ctrl_chg_mth(bs):
    global MODE
    if bs & BUTTON_SEL_LONG:
        MODE = DISPLAY
    if bs & BUTTON_SEL:
        MODE = CHANGE_DAY
    if bs & BUTTON_UP or bs & BUTTON_UP_LONG:
        modTime(0, 1, 0, 0, 0, 0)
    if bs & BUTTON_DOWN or bs & BUTTON_DOWN_LONG:
        modTime(0, -1, 0, 0, 0, 0)

def ctrl_chg_day(bs):
    global MODE
    if bs & BUTTON_SEL_LONG:
        MODE = DISPLAY
    if bs & BUTTON_SEL:
        MODE = CHANGE_HOURS
    if bs & BUTTON_UP or bs & BUTTON_UP_LONG:
        modTime(0, 0, 1, 0, 0, 0)
    if bs & BUTTON_DOWN or bs & BUTTON_DOWN_LONG:
        modTime(0, 0, -1, 0, 0, 0)

WORKAROUND_OFFSET = None
def detect_workaround_offset():
    global WORKAROUND_OFFSET

    old = utime.time()
    utime.set_time(old)
    new = utime.time()

    WORKAROUND_OFFSET = old - new
    utime.set_time(old + WORKAROUND_OFFSET)

NAME = None
FILENAME = 'nickname.txt'
def load_nickname():
    global NAME
    if FILENAME in os.listdir('.'):
        with open("nickname.txt", "rb") as f:
            name = f.read().strip()
    else:
        name = b'no nick'

    if len(name) > 7:
        name = name[0:7]
    else:
        name = b' ' * (7 - len(name)) + name

    NAME = name

# MODE values
DISPLAY = 0
CHANGE_HOURS = 1
CHANGE_MINUTES = 2
CHANGE_SECONDS = 3
CHANGE_YEAR = 4
CHANGE_MONTH = 5
CHANGE_DAY = 6

MODE = DISPLAY
MODES = {
    DISPLAY: '---',
    CHANGE_HOURS: 'HRS',
    CHANGE_MINUTES: 'MNS',
    CHANGE_SECONDS: 'SEC',
    CHANGE_YEAR: 'YRS',
    CHANGE_MONTH: 'MTH',
    CHANGE_DAY: 'DAY',
}

CTRL_FNS = {
    DISPLAY: ctrl_display,
    CHANGE_HOURS: ctrl_chg_hrs,
    CHANGE_MINUTES: ctrl_chg_mns,
    CHANGE_SECONDS: ctrl_chg_sec,
    CHANGE_YEAR: ctrl_chg_yrs,
    CHANGE_MONTH: ctrl_chg_mth,
    CHANGE_DAY: ctrl_chg_day,
}

def main():
    try:
        detect_workaround_offset()
        load_nickname()
        with display.open() as d:
            while True:
                bs = checkButtons()
                CTRL_FNS[MODE](bs)
                render(d)
    except KeyboardInterrupt:
        pass

main()