"""
Attempt of a really useful clock and alarm app
2019-09 by Hraban

inspired by and based on several other apps,
most notably digiclk/watch++ and engelsystem.
(Everything event related not yet implemented!)
"""
import buttons
import display
import leds
import os
import sys
import utime
import ujson
import bme680
import bhi160
import light_sensor
from fiee import betterbutton as bb
from fiee import bigdigits
from fiee import battery
# docs: https://firmware.card10.badge.events.ccc.de/

CONFIG = {}
LANG = {}
TIME_MODE = 1
TEMP_MODE = 2
EVENT_MODE = 3
DEBUG_MODE = 99
MODE = TIME_MODE
FLASHLIGHT = 0
BUTTONS = []
PREV_SEC = 0
LAST_ACTION = 0
LIGHT = 0


def render_text(d, text, c=(128,128,128), blankidx=None, x=5, y=54):
    bs = bytearray(text)

    if blankidx is not None:
        bs[blankidx:blankidx + 1] = b'_'
    d.print(bs.decode(), fg=c, bg=None, posx=x, posy=y)


def switch_flashlight(status):
    if status:
        led_brightness = 8
        for led in range(0,15):
            leds.prep(led, CONFIG['flashlight_color'])
    else:
        led_brightness = CONFIG['led_brightness']
        for led in range(0,15):
            leds.prep(led, (0,0,0))
    leds.update()
    leds.dim_top(led_brightness)
    leds.dim_bottom(led_brightness)


def init_sensors(d):
    global MODE
    try:
        sens = {
            'gyro': bhi160.BHI160Gyroscope(),
            #'accel': bhi160.BHI160Accelerometer(),
            #'orien': bhi160.BHI160Orientation()
        }
    except ValueError as ex:
        print(ex)
        d.print(str(ex))
        d.update()
        utime.sleep(2)
        MODE = TIME_MODE
        return None
    return sens

def close_sensors(sens):
    for v in sens.values:
        v.close()

def init_buttons():
    global BUTTONS, LAST_ACTION
    LAST_ACTION = utime.time()
    for b in (buttons.BOTTOM_LEFT, buttons.TOP_RIGHT, buttons.BOTTOM_RIGHT):
        BUTTONS.append(bb.Button(b))


def check_buttons():
    global BUTTONS, MODE, FLASHLIGHT, LAST_ACTION
    for b in BUTTONS:
        b.checkButton()
    if BUTTONS[0].pressed():
        LAST_ACTION = utime.time()
        MODE = TEMP_MODE if MODE != TEMP_MODE else TIME_MODE
    if BUTTONS[1].pressed():
        LAST_ACTION = utime.time()
        FLASHLIGHT = 0 if FLASHLIGHT else 1
        switch_flashlight(FLASHLIGHT)
    # bmi160 won't initialize :-((
    if BUTTONS[2].pressed():
        LAST_ACTION = utime.time()
        MODE = DEBUG_MODE if MODE != DEBUG_MODE else TIME_MODE
        FLASHLIGHT = 0


def ctrl_backlight(d):
    global CONFIG, LIGHT, FLASHLIGHT
    if FLASHLIGHT:
        # don't interfere
        d.backlight(1)
        return
    if utime.time() - LAST_ACTION > CONFIG['idle_secs']:
        # nothing happened, make dark
        d.backlight(0)
        leds.dim_top(0)
        return
    LIGHT = light_sensor.get_reading()
    # values between 0 and 100 (direkt bright light); room 20-40
    display_brightness = 2
    if LIGHT > 50:
        display_brightness = min(100, (LIGHT // 4)+1)
    d.backlight(display_brightness)
    led_brightness = min((display_brightness // 25)+1, CONFIG['led_brightness'])
    leds.dim_top(led_brightness)


def render(d):
    global MODE, PREV_SEC, LIGHT
    if utime.time() - LAST_ACTION > CONFIG['idle_secs']:
        # do nothing, user sleeps
        return
    year, month, mday, hour, min, sec, wday, yday = utime.localtime()
    if PREV_SEC == sec:
        utime.sleep(0.25)
        return
    PREV_SEC = sec
    d.clear()
    leds.set_rocket(2, 0)
    if MODE == TIME_MODE:
        if sec % 2:
            bigdigits.render_colon(d, CONFIG['clock_color'])
        bigdigits.render_num(d, hour, 1, CONFIG['clock_color'])
        bigdigits.render_num(d, min, 13, CONFIG['clock_color'])
        formatted_date = "{}.{:02}.{:02}.{:02}".format(LANG['days'][wday], mday,month,year-2000)
        render_text(d, formatted_date, c=CONFIG['date_color'])
        battery.render_battery(d)
    elif MODE == TEMP_MODE:
        with bme680.Bme680() as sens:
            render_text(d, '''{:2} {:.2f}'C {:2} {:.2f}%  {:2} {:4}hPa {:2} {:6}Om'''.format(
            LANG['temperature'], sens.temperature(),
            LANG['humidity'], sens.humidity(),
            LANG['pressure'], int(sens.pressure()),
            LANG['resistance'], int(sens.gas_resistance())),
            c=CONFIG['date_color'], x=0, y=0)
    elif MODE == DEBUG_MODE:
        sens = init_sensors(d)
        if not sens:
            return
        leds.set_rocket(2, 8)
        # docs: https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BHI160B-DS000.pdf
        try:
            data = sens['gyro'].read()
        except ValueError as ex:
            print(ex)
            d.print(str(ex))
            d.update()
            MODE = TIME_MODE
            return
        if not data:
            d.print("No data!")
            d.update()
            MODE = TIME_MODE
            return
        for (x,y,z,s) in data:
            d.clear()
            out = 'x{:10}y{:10}z{:10}s{}'.format(int(x),int(y),int(z),int(s))
            print(out)
            d.print(out, fg=(255,255,255), posx=0, posy=0)
            d.update()
            utime.sleep(0.5)
        close_sensors(sens)
    else:
        d.print("ERROR: mode {}".format(MODE))
        d.update()
        utime.sleep(2)
    if not FLASHLIGHT:
        # LED time
        for x in range(0, 11):
            rgb = [0,0,0]
            if hour % 12 == x+1:
                rgb[0] = 63
            if (min // 10) == x+1:
                rgb[1] = 63
                rgb[0] += 4
            if min % 10 == x+1:
                rgb[1] += 64
            if sec % 10 == x:
                rgb[2] = 63
            if LIGHT < 10:
                rgb[2] += 1
            leds.prep(x,rgb)
    leds.update()
    d.update()


def init_config():
    global CONFIG, LANG
    # only absolute path works. why?
    p = '/apps/fiee/'
    #p = ''
    try:
        f = open(p+'config.json', mode='r')
    except Exception as ex:
        print(ex)
        f = open(p+'config_default.json', mode='r')
    CONFIG = ujson.loads(f.read())[0]
    f.close()
    f = open(p+'lang.json', mode='r')
    LANG = ujson.loads(f.read())[0]
    LANG = LANG[CONFIG['lang']]
    f.close()


def main():
    init_config()
    init_buttons()
    light_sensor.start()
    bme680.init()
    with display.open() as d:
        while True:
            check_buttons()
            ctrl_backlight(d)
            render(d)
            if utime.time() - LAST_ACTION > CONFIG['idle_secs']:
                utime.sleep(1)
                if utime.time() - LAST_ACTION > 5*CONFIG['idle_secs']:
                    utime.sleep(4)


main()
