##
# @file      main.py
# @brief     Example on using timer callbacks to set the LEDs in the form of a game
#
# In this example we use timer callbacks to set the front panel LEDs randomly,
# cycling through various colors at 1 Hz. When the LED comes up green, press the
# on-screen button as fast as you can. Your reaction time will be measured and
# displayed. After 5 seconds, the game restarts.
#

import gui
import vts

# Global variables #############################################################
class GV:
    # Probability (255 = 100%) of getting a green LED for each sequential turn.
    # If there are more turns than values on this list, the last value is used
    # from then on.
    green_probability = [0, 2, 4, 8, 16, 32]
    # Sequential turn
    turn = 0
    # Button properties
    button_w = 240
    button_h = 60
    button_xy = [[0], [0]]
    # Is the green LED on now?
    is_green = False
    # Was the button pressed?
    pressed = False
    # Chronometer for measuring reaction time
    chrono = vts.Chrono()
    # Reaction time
    reaction_t = None
    # 1 Hz timer
    timer = None

# Utility functions ############################################################
def get_LED_random(rand, green_prob):
    """
    Return a 12-int list suitable for passing to vts.leds()

    rand        random byte [0-255]
    green_prob  probability of green coming up this time
    """
    off = [0, 0, 0]
    green = [0, 255, 0]
    other_cols = [
        [255, 0, 0],
        [0, 0, 255],
        [255, 255, 0],
        [255, 0, 255],
        [0, 255, 255],
        [255, 255, 255],
    ]
    # Use 2 bits for lit LED position
    pos = rand & 3
    rand >>= 2
    # Use 8 bits to determine whether green will come up
    is_green = (rand & 0xff) < green_prob
    rand >>= 8
    # Use the rest to select the color if needed
    col = green if is_green else other_cols[rand % 6]
    # Return flag + a complete 12-int list
    return is_green, off * pos + col + off * (3 - pos)

def get_button_xy_random(rand, minx, maxx, miny, maxy):
    rand_x = (rand & 0xffff) % (maxx - minx)
    rand_y = (rand >> 16) % (maxy - miny)
    return minx + rand_x, miny + rand_y

def update_LEDs(green_prob):
    # Draw a random 32-bit number
    rand = vts.rand32()
    # Get LED values...
    is_green, leds = get_LED_random(rand, green_prob)
    # ... and show them
    vts.leds(*leds)
    # Return if green came up or not
    return is_green

def update_button(button_xy, button_w, button_h):
    # Draw a random 32-bit number
    rand = vts.rand32()
    # Get button position and change the GUI values
    button_xy[0][0], button_xy[1][0] = get_button_xy_random(
        rand,
        10, 790 - button_w,
        10, 470 - button_h
    )

# Callbacks ####################################################################
def timer_cb():
    # This will execute at 1 Hz, changing the game state (button position and
    # LEDs)
    prob = GV.green_probability[
        -1 if GV.turn >= len(GV.green_probability) else GV.turn
    ]
    GV.turn += 1
    GV.is_green = update_LEDs(prob)
    if GV.is_green:
        GV.chrono.start()
    update_button(GV.button_xy, GV.button_w, GV.button_h)
    gui.redraw()

def button_cb(b):
    # When button is pressed, we capture the current chronometer value and set a
    # flag. We also disable the timer callback because the game ended.
    GV.reaction_t = GV.chrono.read() // 1000
    GV.pressed = True
    GV.timer.set_callback(None)

# Main functions ###############################################################
def init():
    vts.leds(*([0] * 12))
    GV.turn = 0
    GV.button_xy = [[(800 - GV.button_w) // 2], [(480 - GV.button_h) // 2]]
    GV.is_green = False
    GV.pressed = False
    gui.show([
        [
            gui.CTRL_BUTTON,
            GV.button_xy[0], GV.button_xy[1],
            GV.button_w, GV.button_h,
            29, 'Press me on GREEN', button_cb
        ]
    ])
    # Destroy any live timers
    vts.Timer.destroy_all()
    # 1 Hz timer, auto-reload
    GV.timer = vts.Timer(1000, True)
    GV.timer.set_callback(timer_cb)

def you_win():
    vts.leds(*([0, 64, 0] * 4))
    game_reaction = 'Reaction time: {} ms'.format(GV.reaction_t)
    gui.show([
        [gui.CTRL_TEXT, 400, 220, 30, gui.OPT_CENTER, 'You win!'],
        [gui.CTRL_TEXT, 400, 260, 30, gui.OPT_CENTER, game_reaction],
    ])

def you_lose():
    vts.leds(*([64, 0, 0] * 4))
    gui.show([[gui.CTRL_TEXT, 400, 240, 30, gui.OPT_CENTER, 'You lose!']])

# Infinite event loop
def run():
    init()
    while True:
        if GV.pressed:
            if GV.is_green:
                you_win()
            else:
                you_lose()
            # Show message for 5 seconds, then restart game
            vts.delay_ms(5000)
            init()

if __name__ == '__main__':
    run()
