import RPi.GPIO as GPIO
import time
from threading import Thread
from buzzerJukeBox import BuzzerJukeBox

_ledPins = [18, 23, 24]
_buttonPin = 25
_buzzerPin = 22
_cleaned = False
_jukeBox = BuzzerJukeBox(_buzzerPin)
_holdOffThread = None
_holdOffPeriodSecs = 15

def holdOffTimer():
    print("Hold off begun")
    time.sleep(_holdOffPeriodSecs)
    print("Hold off complete")

def cleanup():
    if _cleaned:
        return
    else:
        GPIO.setwarnings(True)
        GPIO.cleanup()

def configureGPIO():
    """
    Set the default configuration of the GPIO pins of the RaspberryPi.
    Using pins defined in _ledPins for as OUT pins and _buttonPin as IN.
    """
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.cleanup()

    for pin in _ledPins:
        GPIO.setup(pin, GPIO.OUT)

    GPIO.setup(_buttonPin, GPIO.IN)

    GPIO.setup(_buzzerPin, GPIO.OUT)
    GPIO.output(_buzzerPin, GPIO.LOW)

def changeLEDState(state):
    """
    Set the state of the output GPIO pins being used by the LEDs
    to the state provided.
    """
    for pin in _ledPins:
        GPIO.output(pin, state)
    

def lightLED():
    """
    Light all connected LEDs.
    """
    changeLEDState(GPIO.HIGH)

def unlightLED():
    """
    Turn off all connected LEDs.
    """
    changeLEDState(GPIO.LOW)

def performAction():
    unlightLED()

    # global required so a local variable isn't created but the global variable within this module
    # is used instead. Only required for assingment operations.
    global _holdOffThread
    _holdOffThread = Thread(target=holdOffTimer)
    _holdOffThread.start()

    _jukeBox.playRandom()
    
    # Running from 10 down to , light and then unlight the LEDs in an accelerated
    # fashion to provide a simple ten step sequence.
    for i in range(10, 0, -1):
        lightLED()
        time.sleep(i / 10)
        unlightLED()
        time.sleep(i / 10)
    
#########
# start #
#########

try:
    configureGPIO()
    lightLED()

    while True:
        if _holdOffThread is None or _holdOffThread.isAlive() is False:
            lightLED()

            # Only perform check inside once we know the hold off thread is not running
            # as this acts as a debounce (stops multiple button presses).
            # If the action is longer than the hold off the debounce will still work
            # as control will not have been returned to this function.
            if GPIO.input(_buttonPin) == False:
                performAction()
        else:
            unlightLED()
finally:
    cleanup()
