"""A Sequencer!

Annoy your friends! Annoy your enemies! Annoy yourself! Maybe (maybe) make music!
"""

___title___        = "Sequencer"
___license___      = "MIT"
___categories___   = ["Sound"]
___dependencies___ = ["speaker", "buttons", "ugfx_helper", "app", "shared/sequencer_info.png"]

import ugfx, speaker, ugfx_helper
from tilda import Buttons
from buttons import *
from app import restart_to_default

ugfx_helper.init()
speaker.enabled(True)

rows_per_block = 4
cols_per_block = 3
num_v_blocks = 2
num_h_blocks = 4
total_rows = rows_per_block*num_v_blocks
total_cols = cols_per_block*num_h_blocks
line_width = 5
row_height = int(ugfx.height() / (rows_per_block*num_v_blocks))
col_width = int(ugfx.width() / (cols_per_block*num_h_blocks))
block_height = row_height*rows_per_block
block_width = col_width*cols_per_block

active = True

notes = [
        "C",
        "C#",
        "D",
        "D#",
        "E",
        "F",
        "F#",
        "G",
        "G#",
        "A",
        "A#",
        "B"]

active_colour = ugfx.html_color(0x800080)
inactive_colour = ugfx.html_color(0xd29cd1)
current_active_colour = ugfx.html_color(0x00cf3a)
current_inactive_colour = ugfx.html_color(0x88c89a)

start_time = time.ticks_ms()
time_per_row = 500 # mS
previous_current_row = 0 # TODO: find less stupid variable name

debounce_time = 100
active_states = [[False for col in range(total_cols)] for row in range(total_rows)]
last_pressed = [[time.ticks_ms() for col in range(total_cols)] for row in range(total_rows)]
joy_last_pressed = [time.ticks_ms() for col in range(6)]

active_v_block = 0
active_h_block = 0

def mode_buttons():
    global previous_current_row
    global active_states
    global active
    global start_time
    global active_v_block
    global active_h_block

    print("mode: buttons")
    coords = {
        Buttons.BTN_1: (0,0),
        Buttons.BTN_2: (0,1),
        Buttons.BTN_3: (0,2),
        Buttons.BTN_4: (1,0),
        Buttons.BTN_5: (1,1),
        Buttons.BTN_6: (1,2),
        Buttons.BTN_7: (2,0),
        Buttons.BTN_8: (2,1),
        Buttons.BTN_9: (2,2),
        Buttons.BTN_Star: (3,0),
        Buttons.BTN_0: (3,1),
        Buttons.BTN_Hash: (3,2),
    }
    render_ui()

    alive = True
    while alive:
        ui_changed = False
        row_changed = False

        current_row = int((time.ticks_ms() - start_time) / time_per_row) % total_rows
        if current_row != previous_current_row:
            previous_current_row = current_row
            row_changed = True

        for btn, coord in coords.items():
            if is_pressed(btn):
                row = active_v_block*rows_per_block + coord[0]
                col = active_h_block*cols_per_block + coord[1]
                if (last_pressed[row][col] + debounce_time < time.ticks_ms()):
                    last_pressed[row][col] = time.ticks_ms()
                    active_states[row][col] = not active_states[row][col]

                    # only one note per frame
                    if active_states[row][col]:
                        for check_col in range(total_cols):
                            if check_col != col:
                                active_states[row][check_col] = False

                    ui_changed = True
                    break

        if is_triggered(Buttons.JOY_Center):
            if (joy_last_pressed[5] + debounce_time < time.ticks_ms()):
                joy_last_pressed[5] = time.ticks_ms()
                active = not active
                if not active:
                    speaker.stop()
                else:
                    start_time = time.ticks_ms()
                ui_changed = True
        if is_triggered(Buttons.JOY_Up):
            if (joy_last_pressed[0] + debounce_time < time.ticks_ms()):
                joy_last_pressed[0] = time.ticks_ms()
                active_v_block -= 1
                if active_v_block < 0:
                    active_v_block += num_v_blocks
                ui_changed = True
        if is_triggered(Buttons.JOY_Down):
            if (joy_last_pressed[1] + debounce_time < time.ticks_ms()):
                joy_last_pressed[1] = time.ticks_ms()
                active_v_block += 1
                active_v_block %= num_v_blocks
                ui_changed = True
        if is_triggered(Buttons.JOY_Left):
            if (joy_last_pressed[2] + debounce_time < time.ticks_ms()):
                joy_last_pressed[2] = time.ticks_ms()
                active_h_block -= 1
                if active_h_block < 0:
                    active_h_block += num_h_blocks
                ui_changed = True
        if is_triggered(Buttons.JOY_Right):
            if (joy_last_pressed[3] + debounce_time < time.ticks_ms()):
                joy_last_pressed[3] = time.ticks_ms()
                active_h_block += 1
                active_h_block %= num_h_blocks
                ui_changed = True
        if is_triggered(Buttons.BTN_B):
            if (joy_last_pressed[4] + debounce_time < time.ticks_ms()):
                joy_last_pressed[4] = time.ticks_ms()
                for row in range(total_rows):
                    for col in range(total_cols):
                        active_states[row][col] = False
                ui_changed = True
        if is_triggered(Buttons.BTN_A):
            if (joy_last_pressed[5] + debounce_time < time.ticks_ms()):
                joy_last_pressed[5] = time.ticks_ms()
                speaker.stop()
                display_help()
                ui_changed = True
        if is_triggered(Buttons.BTN_Menu):
            break

        if ui_changed or (active and row_changed):
            render_ui()
        if active and row_changed:
            play_notes(current_row)

def render_ui():
    ugfx.clear(ugfx.html_color(0xffffff))
    # draw squares
    current_row = int((time.ticks_ms() - start_time) / time_per_row) % total_rows

    for row in range(total_rows):
        for col in range(total_cols):
            colour = inactive_colour
            if active and row == current_row:
                if active_states[row][col] == True:
                    colour = current_active_colour
                else:
                    colour = current_inactive_colour
            elif active_states[row][col] == True:
                colour = active_colour

            ugfx.area(col_width*col + line_width, row_height*row + line_width, col_width - line_width, row_height - line_width, colour)

    # highlight working area
    ugfx.area(active_h_block*block_width, active_v_block*block_height, line_width, block_height, ugfx.RED)
    ugfx.area((active_h_block+1)*block_width, active_v_block*block_height, line_width, block_height, ugfx.RED)
    ugfx.area(active_h_block*block_width, active_v_block*block_height, block_width, line_width, ugfx.RED)
    ugfx.area(active_h_block*block_width, (active_v_block+1)*block_height, block_width+line_width, line_width, ugfx.RED)

def play_notes(row):
    note = ""
    for col in range(total_cols):
        if active_states[row][col] == True:
            note = notes[col]

    if note == "":
        speaker.stop()
    else:
        speaker.stop()
        speaker.note("{}{}".format(note, 5))

def display_help():
    global start_time
    ugfx.display_image(0, 0, "shared/sequencer_info.png")
    wait_until = time.ticks_ms() + 5000
    while time.ticks_ms() < wait_until:
        time.sleep(0.1)
        if Buttons.is_pressed(Buttons.BTN_A) or Buttons.is_pressed(Buttons.BTN_B) or Buttons.is_pressed(Buttons.BTN_Menu):
            break

    start_time = time.ticks_ms()

display_help()
mode_buttons() # Todo: Allow different modes and allow users to switch between them via joystick or something
restart_to_default()
