#!/usr/bin/python

import daemon   #to deamonize this script
import serial   #serial communication
import re       #regular expressions
import logging  #hmm what could this be for?
import os       #to call external stuff
import signal  #catch kill signall
import time     #for the sleep function
import select  #for select.error
from errno import EINTR #read interrupt
import traceback #for stacktrace

#setup logging
LOG_FILENAME = 'gatekeeper.log'
FORMAT = "%(asctime)-12s: %(levelname)-8s - %(message)s"
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,format=FORMAT)
log = logging.getLogger("GateKeeper")

class Pin:
  #init (activate pin)
  def __init__(self):
    os.system('echo 168 > /sys/class/gpio/export')
    log.debug("initialized pin")

  def high(self):
    os.system('echo "high" > /sys/class/gpio/gpio168/direction')
    log.debug("Pin to high")

  def low(self):
    os.system('echo "low" > /sys/class/gpio/gpio168/direction')
    log.debug("Pin to low")

  def send_pulse(self):
    self.high()
    #keep pulse high for 0.5 second
    time.sleep(0.5)
    self.low()
    log.debug("Pulse done")
    

class GateKeeper:
  def __init__(self):
    self.pin = Pin()
    self.read_whitelist()
    self.enable_caller_id()
    self.data_channel = serial.Serial(port='/dev/ttyUSB1',baudrate=115200)

  def read_whitelist(self):
    self.whitelist = {}
    file = open('whitelist','r')
    entry_pattern = re.compile('([0-9][0-9]+?) (.*)')
    line = file.readline()
    while line:
      entry_match = entry_pattern.match(line)
      if entry_match:
        number = entry_match.group(1)
        name = entry_match.group(2)
        self.whitelist[number] = name
      line = file.readline()
    file.close()
    log.debug("Whitelist " + str(self.whitelist))

  def enable_caller_id(self):
    command_channel = serial.Serial(port='/dev/ttyUSB0',baudrate=115200)
    command_channel.open()
    command_channel.write("AT+CLIP=1" + "\r\n")
    command_channel.close()
    log.debug("Enabled caller ID")

  def start(self):
    try: 
      self.wait_for_call()
    except select.error, v:
      if v[0] == EINTR:
        log.debug("Interrupt while waiting for call, cleanup should be done.")
      else:
        raise
    else:
      log.warning("Unexpected exception, shutting down!")
  
  def wait_for_call(self):
    self.data_channel.open()
    call_id_pattern = re.compile('.*CLIP.*"\+([0-9]+)",.*')
    while True:
      buffer = self.data_channel.readline(self.data_channel.inWaiting())
      call_id_match = call_id_pattern.match(buffer)
      log.debug("Data from data channel: " +buffer.strip())
      if call_id_match:
        number = call_id_match.group(1)
        self.handle_call(number)
  
  def handle_call(self,number):
    if number in self.whitelist:
      self.pin.send_pulse()
      log.info("Opened the gate for " + self.whitelist[number] + " (" + number + ").")
    else:
      log.info("Did not open the gate for "  + number + ", number  is not kown.")
      
  def stop_gatekeeping(self):
    self.data_channel.close()
    #set pin to default state?
    #self.pin.low()
    log.debug("Cleanup finished.") 
    

#make this process a deamon
daemon.daemonize("/tmp/gatekeeper.pid")

logging.info("Started GateKeeper.")

gatekeeper = GateKeeper()

def shutdown_handler(signum, frame):
    gatekeeper.stop_gatekeeping()
    log.info("Stopping GateKeeper.") 

signal.signal(signal.SIGINT, shutdown_handler)
signal.signal(signal.SIGTERM,shutdown_handler)

gatekeeper.start()


