"""Bootstraps the badge by downloading the base software"""

import ugfx, machine, network, json, time, usocket, os, gc
from tilda import Buttons

HOST = "badgeserver.emfcamp.org"
wifi = network.WLAN()
wifi.active(True)

# Helpers
def msg(text):
    ugfx.clear()
    ugfx.text(5, 5, "EMF 2018", ugfx.BLACK)
    ugfx.text(5, 30, "TiLDA Mk4", ugfx.BLACK)
    lines = text.split("\n")
    print(lines[0])
    for i, line in enumerate(lines):
        ugfx.text(5, 65 + i * 20, line, ugfx.BLACK)

def wifi_select():
    msg("Please select your wifi\nConfirm with button A")
    sl = ugfx.List(5, 110, 228, 204)
    aps = {}
    while not Buttons.is_pressed(Buttons.BTN_A):
        for s in (wifi.scan() or []):
            if s[0] not in aps:
                sl.add_item(s[0])
                aps[s[0]] = s
        time.sleep(0.01)
        ugfx.poll()
    ssid = sl.selected_text()
    sl.destroy()

    msg("Wifi: %s\nPlease enter your password\nConfirm with button A" % ssid)
    kb = ugfx.Keyboard(0, 160, 240, 170)
    e = ugfx.Textbox(5, 130, 228, 25, text="")
    while not Buttons.is_pressed(Buttons.BTN_A):
        time.sleep(0.01)
        ugfx.poll()
    pw = e.text()
    e.destroy()
    kb.destroy()
    result = {"ssid":ssid,"pw":pw}
    with open("wifi.json", "wt") as file:
        file.write(json.dumps(result))
        file.flush()
    os.sync()
    return result

def wifi_details():
    try:
        with open("wifi.json") as f:
            return json.loads(f.read())
    except Exception as e:
        print(str(e))
        return wifi_select()

def connect():
    details = wifi_details()
    if 'user' in details:
        wifi.connect(details['ssid'], details['pw'], enterprise=True, entuser=details['user'], entmethod=wifi.EAP_METHOD_PEAP0_MSCHAPv2, entserverauth=False)
    elif 'pw' in details:
        wifi.connect(details['ssid'], details['pw'])
    else:
        wifi.connect(details['ssid'])

    wait_until = time.ticks_ms() + 10000
    while not wifi.isconnected():
        if (time.ticks_ms() > wait_until):
            os.remove("wifi.json")
            raise OSError("Wifi timeout");
        time.sleep(0.1)

def addrinfo(host, port, retries_left = 20):
    try:
        return usocket.getaddrinfo(host, port)[0][4]
    except OSError as e:
        if ("-15" in str(e)) and retries_left:
            # [addrinfo error -15]
            # This tends to happen after startup and goes away after a while
            time.sleep_ms(200)
            return addrinfo(host, port, retries_left - 1)
        else:
            raise e

def get(path):
    s = usocket.socket()
    s.connect(addrinfo(HOST, 80))
    body = b""
    status = None
    try:
        s.send('GET /2018/%s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, HOST))
        state = 1
        hbuf = b""
        clen = 9999999
        headers = {}
        while len(body) < clen:
            buf = s.recv(1024)
            if state == 1: # Status
                nl = buf.find(b"\n")
                if nl > -1:
                    hbuf += buf[:nl - 1]
                    status = int(hbuf.split(b' ')[1])
                    state = 2
                    hbuf = b"";
                    buf = buf[nl + 1:]
                else:
                    hbuf += buf

            if state == 2: # Headers
                hbuf += buf
                nl = hbuf.find(b"\n")
                while nl > -1:
                    if nl < 2:
                        buf = hbuf[2:]
                        hbuf = None
                        state = 3
                        clen = int(headers["content-length"])
                        break

                    header = hbuf[:nl - 1].decode("utf8").split(':', 3)
                    headers[header[0].strip().lower()] = header[1].strip()
                    hbuf = hbuf[nl + 1:]
                    nl = hbuf.find(b"\n")

            if state == 3: # Content
                body += buf

    finally:
        s.close()
    if status != 200:
        raise Exception("HTTP %d for %s" % (status, path))
    return body

# os.path bits
def split(path):
    if path == "":
        return ("", "")
    r = path.rsplit("/", 1)
    if len(r) == 1:
        return ("", path)
    head = r[0]
    if not head:
        head = "/"
    return (head, r[1])

def dirname(path):
    return split(path)[0]

def exists(path):
    try:
        os.stat(path)[0]
        return True
    except OSError:
        return False

def makedirs(path):
    sub_path = split(path)[0]
    if sub_path and (not exists(sub_path)):
        makedirs(sub_path)
    if not exists(path):
        os.mkdir(path)

# Steps
def step_wifi():
    while not wifi.isconnected():
        msg("Connecting to wifi...");
        try:
            connect()
        except Exception as e:
            print(str(e))
            msg("Couldn't connect\nPlease check wifi details")
            time.sleep(1)

def step_download():
    msg("Connecting to server...")
    files = list(json.loads(get("bootstrap")).keys())
    for i, file in enumerate(files):
        msg("Downloading - %d%%\n%s" % (100 * i // len(files), file))
        makedirs(dirname(file))
        with open(file, 'wb') as f:
            f.write(get("download?repo=emfcamp/Mk4-Apps&path=%s" % file))
    os.sync()

def step_goodbye():
    msg("All done!\n\nRestarting badge...")
    os.remove("bootstrap.py")
    time.sleep(2)
    machine.reset()

ugfx.init()
machine.Pin(machine.Pin.PWM_LCD_BLIGHT).on()
try:
    step_wifi()
    step_download()
    step_goodbye()
except Exception as e:
    msg("Error\nSomething went wrong :(\n\n" + str(e))
    raise e
