##
# @module    ui
# @brief     UI functions and classes
# @version   1.0
#

import ft8xx as ft
import vts

class UI:
    obj_count = 0

    snd_click = 0
    snd_newlap = 1
    snd_1s_beep = 2
    snd_2s_beep = 3
    snd_generic_beep = 4
    snd_target_speed_beep = 3
    sounds = [
        # click
        [(0, 88)],
        # new lap
        [(100, 72, 60), (100, 72, 60), (0, 72, 60)],
        # 1 second beep
        [(1000, 4, 88)],
        # 2 second beep
        [(2000, 4, 88)],
        # generic beep
        [(500, 4, 88)],
    ]

    def __init__(self, volume=128):
        if UI.obj_count != 0:
            raise RuntimeError('UI object: there can be only one')
        UI.obj_count += 1
        self.sound_list = None
        self.sound_timer = None
        self.set_volume(volume)

    def set_volume(self, volume):
        self.volume = volume
        ft.wr8(ft.REG_VOL_SOUND, self.volume)

    def play_sound(self, index):
        self.sound_start(UI.sounds[index])

    # Play sound from a sound list. A sound list is a list of tuples consisting
    # of:
    #   - note duration in milliseconds (0 to let last note ring out)
    #   - note instrument
    #   - note pitch (optional)
    #   - note volume (optional)
    # Volume is 0-255, relative to the "master" volume.
    def sound_start(self, sl):
        # Copy sound list
        self.sound_list = sl[::-1]
        self.sound_cb()

    # Timer callback for sound
    def sound_cb(self):
        try:
            self.sound_timer.destroy()
        except (TypeError, AttributeError):
            # Catch: timer already destroyed or timer variable is None
            pass
        if not len(self.sound_list):
            # No notes left but timer was set - silence current note and exit.
            # NOTE: Writing 0 to REG_SOUND then 1 to REG_PLAY to stop playing
            # sound, as the programmers guide suggests, sometimes makes the next
            # note played sound double. So we just cut the volume instead.
            ft.wr8(ft.REG_VOL_SOUND, 0)
            return
        note = self.sound_list.pop()
        sound = note[1]
        if len(note) >= 3:
            sound |= note[2] << 8
        if len(note) >= 4:
            vol = (note[3] * self.volume) >> 8
        else:
            vol = self.volume
        ft.wr8(ft.REG_VOL_SOUND, vol)
        ft.wr16(ft.REG_SOUND, sound)
        ft.wr8(ft.REG_PLAY, 1)
        if note[0] != 0:
            self.sound_timer = vts.Timer(note[0], False)
            self.sound_timer.set_callback(self.sound_cb)
