import time
import Adafruit_BBIO.GPIO as GPIO
import Adafruit_BBIO.PWM as PWM

class Keypad():
    def __init__(self):
        # CONSTANTS 
        self.KEYPAD = [
            ["1","2","3"],
            ["4","5","6"],
            ["7","8","9"],
            ["*","0","#"]
        ]

        self.ROW         = ["P8_8", "P8_10", "P8_12", "P8_14"]
        self.COLUMN      = ["P8_16", "P8_17", "P8_18"]
        self.PASS = "1234"
     
    def getKey(self):
        # Set all columns as output low
        for i in range(len(self.COLUMN)):
            GPIO.setup(self.COLUMN[i], GPIO.OUT)
            GPIO.output(self.COLUMN[i], GPIO.LOW)
         
        # Set all rows as input
        for i in range(len(self.ROW)):
            GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP)
         
        # Scan rows for pushed key/button
        # A valid key press should set "rowVal"  between 0 and 3.
        rowVal = -1
        for i in range(len(self.ROW)):
            tmpRead = GPIO.input(self.ROW[i])
            if tmpRead == 0:
                rowVal = i
                 
        if rowVal <0 or rowVal >3:
            self.reset()
            return
         
        # Convert columns to input
        for j in range(len(self.COLUMN)):
            GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_UP)
         
        # Switch the i-th row found from scan to output
        GPIO.setup(self.ROW[rowVal], GPIO.OUT)
        GPIO.output(self.ROW[rowVal], GPIO.LOW)
 
        # Scan columns for still-pushed key/button
        # A valid key press should set "colVal"  between 0 and 2.
        colVal = -1
        for j in range(len(self.COLUMN)):
            tmpRead = GPIO.input(self.COLUMN[j])
            if tmpRead == 0:
                colVal=j
                 
        if colVal <0 or colVal >2:
            self.reset()
            return
 
        # Return the value of the key pressed
        self.reset()
        return self.KEYPAD[rowVal][colVal]
         
    def reset(self):
        # Reinitialize all rows and columns
        for i in range(len(self.ROW)):
            GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP) 
        for j in range(len(self.COLUMN)):
            GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_UP)
    
    def verify_pass(self, pass_entered):
        return pass_entered == self.PASS

class IR:
    def __init__(self, pin, rot, laut, keypad):
        self.pin = pin
        self.rot = rot
        self.laut = laut
        self.keypad = keypad
        GPIO.setup(self.pin, GPIO.IN)
        GPIO.add_event_detect(self.pin, GPIO.RISING, callback=self.cb_ir)

    def __del__(self):
        print("IR {} deleted.".format(self.pin))
        GPIO.remove_event_detect(self.pin)

    def cb_ir(self, pin):
        print("cb_ir called.")
        self.rot.start()
        time.sleep(1)
        self.laut.start()
        time.sleep(1)
        print("pls enter pass to stop alarm")
        start_time = time.time()
        pass_entered = ""
        digit = None
        while True:
            while not digit and time.time()-start_time < 50:
                digit = self.keypad.getKey()
            pass_entered = pass_entered + digit
            print("{} digit(s) already entered".format(len(pass_entered)))
            digit = None
            if len(pass_entered) >= 4:
                if self.keypad.verify_pass(pass_entered[-4:]):
                    print("alarm cancelled")
                    time.sleep(1)
                    self.rot.stop()
                    time.sleep(1)
                    self.laut.stop()
                    time.sleep(1)
                else:
                    print("wrong password")
                    pass_entered = ""
            if time.time() - start_time > 50:
                print("took too long, reset")
                pass_entered = ""
            if time.time() - start_time > 60:
                print("60s passed, quit for test")
                time.sleep(1)
                self.rot.stop()
                time.sleep(1)
                self.laut.stop()
                time.sleep(1)
                break

class LAUT:
    def __init__(self, pin, pwm_perc=50, pwm_freq=10000):
        self.pin = pin
        self.pwm_perc = pwm_perc
        self.pwm_freq = pwm_freq
        GPIO.setup(self.pin, GPIO.OUT)

    def __del__(self):
        print("PWM {} deleted.".format(self.pin))

    def start(self):
        PWM.start(self.pin, self.pwm_perc, self.pwm_freq)

    def stop(self):
        PWM.stop(self.pin)


class ROT():
    def __init__(self, pin, pwm_perc=50, pwm_freq=1):
        self.pin = pin
        self.pwm_perc = pwm_perc
        self.pwm_freq = pwm_freq

    def __del__(self):
        print("ROT {} deleted.".format(self.pin))

    def start(self):
        PWM.start(self.pin, self.pwm_perc, self.pwm_freq)
    
    def stop(self):
        PWM.stop(self.pin)

if __name__ == "__main__":
    rot = ROT("P9_14")
    laut = LAUT("P8_13")
    keypad = Keypad()
    ir = IR("P9_12", rot, laut, keypad)
    try:
        while True:
            print("...")
            time.sleep(2)
    except Exception as e:
        GPIO.cleanup()
        PWM.cleanup()
        print(e)    
