#!/usr/bin/python3
import time, socket, atexit
import queue, threading, select
from libtuer import log, ThreadFunction
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
atexit.register(GPIO.cleanup)

tuerSock = "/run/tuer.sock"
ringPin = 18


# Main classes
class PinWatcher():
	def __init__(self, pin, histlen):
		GPIO.setup(pin, GPIO.IN)
		assert histlen > 1 # otherwise our logic goes nuts...
		self._pin = pin
		self._histlen = histlen
		# state change detection
		self._state = None
		self._newstate = None # != None iff we are currently seeing a state change
		self._newstatelen = 0 # only valid if newstate != None
		# start state change handler thread
		self._callback = ThreadFunction(self.callback)
		self.stop = self._callback.stop
	
	def read(self):
		curstate = GPIO.input(self._pin)
		assert curstate in (0, 1)
		if curstate != self._state:
			# the state is about to change
			if curstate == self._newstate:
				# we already saw this new state
				self._newstatelen += 1
				if self._newstatelen >= self._histlen:
					self._callback(self._state, curstate) # send stuff to the other thread
					self._state = curstate
					self._newstate = None
			else:
				# now check for how long we see this new state
				self._newstate = curstate
				self._newstatelen = 1
		else:
			# old state is preserved
			self._newstate = None

class RingWatcher(PinWatcher):
	def __init__(self):
		super().__init__(ringPin, 2)
		self.last1Event = None
	
	def callback(self, oldstate, newstate):
		if oldstate is None:
			return # ignore the very first state change
		# now (oldstate, newstate) is either (0, 1) or (1, 0)
		if newstate:
			self.last1Event = time.time()
		elif self.last1Event is not None:
			# how long was this pressed?
			timePressed = time.time() - self.last1Event
			log("Ring button pressed for",timePressed)
			if timePressed >= 1.5 and timePressed <= 3:
				self.buzz()
	
	def buzz(self):
		log("Opening door")
		# talk with tuerd
		s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
		s.connect(tuerSock)
		s.send(b'buzz')
		s.close()

# MAIN PROGRAM
pins = [
	RingWatcher(),
]

try:
	log("entering loop")
	while True:
		for pin in pins:
			pin.read()
		time.sleep(0.02)
except KeyboardInterrupt:
	for pin in pins:
		pin.stop()