#!/usr/bin/python2
# -*- coding: latin-1 -*-

import RPi.GPIO as GPIO
import sys
import signal
import time
import datetime
import threading
import MySQLdb

gpio_pins = [7, 11, 12, 13, 15, 16, 18, 22 ]
gpio_desc = [ "", "", "", "", "", "", "", "Garageport, vänster", "", "", "", "Garageport, höger", "Entredörr", "Garagedörr", "", "Brevlådeinkast", "Brevlådedörr", "", "Entrekamera", "", "", "", "<not in use>" ]
gpio_orde = [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 4, 0, 5, 6, 0, 7, 0, 0, 0, 8]

global gpio_stat
gpio_stat = [ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" ]

traceenabled = False
logenabled   = True
dbtimeout    = 300
sleeptime    = 60
dbto         = 0

lock = threading.Lock()

def TracePrint(text):
  global traceenabled
  if traceenabled == True:
    print(str(datetime.datetime.now()) + " " + text)
    sys.stdout.flush()

def LogPrint(text):
  global logenabled
  global logenabled
  if logenabled == True or traceenabled == True:
    print(str(datetime.datetime.now()) + " " + text)
    sys.stdout.flush()

def ConnectDB():
  global db
  global dbto
  db = None
  TracePrint("ConnectDB(): Attempting to connect to MySQL")
  try:
    db = MySQLdb.connect("rpi1", "pi", "", "onewire")
    TracePrint("ConnectDB(): Succeeded to connect!")
    dbto = dbtimeout
  except MySQLdb.Error as e:
    LogPrint("ConnectDB(): ERROR: " + str(e))

def InsertToDB( property, value ):
  global db
  global dbto
  sqlstmt="INSERT INTO data VALUES ( NOW(), 200, " + str(property) + ", " + str(value) + ", 1, 1 )"

  if ( db is None ):
    TracePrint("InsertToDB(): We're not connected to database, lets reconnect")
    ConnectDB()

  if ( db is None ):
    LogPrint("InsertToDB(): ERROR: No DB connection to execute " + sqlstmt)
  else:
    try:
      TracePrint("InsertToDB(): About to execute " + sqlstmt )
      cursor=db.cursor()
      cursor.execute(sqlstmt)
      TracePrint("InsertToDB(): Committing" )
      db.commit()
      cursor.close()
      dbto = dbtimeout
    except MySQLdb.Error as e:
      LogPrint("InsertToDB(): ERROR: " + str(e))
      db.close()
      db = None

def GPIOCallback(gpiopin):
  lock.acquire(True)
  tmp  = GPIO.input(gpiopin)
  tmp2 = GPIO.input(gpiopin)
  if tmp != tmp2: 
    TracePrint("GPIOCallback(): 1st verify read failed, new value: " + str(tmp2))
    tmp = tmp2
  tmp2 = GPIO.input(gpiopin)
  if tmp != tmp2: 
    TracePrint("GPIOCallback(): 2nd verify read failed, new value: " + str(tmp2))
    tmp = tmp2
  tmp2 = GPIO.input(gpiopin)
  if tmp != tmp2: 
    TracePrint("GPIOCallback(): 3rd verify read failed, new value: " + str(tmp2))
    tmp = tmp2
  if tmp != gpio_stat[gpiopin]:
    TracePrint("GPIOCallback(): Pin# " + str(gpiopin) + " (" + gpio_desc[gpiopin] + ") = " + str(tmp))
    gpio_stat[gpiopin] = tmp
    if tmp == True:
      InsertToDB( gpio_orde[gpiopin], 1 )
    else:
      InsertToDB( gpio_orde[gpiopin], 0 )
  else:
    TracePrint("GPIOCallback(): Got stale value for pin # " + str(gpiopin))
  lock.release()

def PollRead():
  lock.acquire(True)
  TracePrint("PollRead(): Verifying GPIO states")
  for x in gpio_pins:
    tmp = GPIO.input(x)
    if tmp != gpio_stat[x]:
      LogPrint("PollRead(): Verify failed for Pin #" + str(x) + " (" + gpio_desc[x] + ") last known state: " + str(gpio_stat[x]) + " new state: " + str(tmp))
      gpio_stat[x] = tmp
      InsertToDB( gpio_orde[x], tmp )
    else:
      TracePrint("PollRead(): Pin #" + str(x) + " Last known: " + str(gpio_stat[x]) + " current: " + str(tmp))
  lock.release()

def signal_handler(signal, frame):
   LogPrint("Program terminated by SIGTERM signal.")
   cleanupandexit();

def cleanupandexit():
   GPIO.cleanup()
   if db != None:
     db.close()
   sys.exit(0)

signal.signal(signal.SIGTERM, signal_handler)
ConnectDB()
GPIO.setmode(GPIO.BOARD)


for x in gpio_pins:
  GPIO.setup(x, GPIO.IN)
  gpio_stat[x] = GPIO.input(x)
  LogPrint("main(): Pin #" + str(x) + " (" + gpio_desc[x] + ") initial state: " + str(gpio_stat[x]))
  InsertToDB( gpio_orde[x], gpio_stat[x] )
  GPIO.add_event_detect(x, GPIO.BOTH, callback=GPIOCallback, bouncetime=200)

LogPrint("main(): Start waiting for callbacks in endless loop with polled reads every "+str(sleeptime)+" seconds.")

while 1:
    try:
      time.sleep(sleeptime)
      PollRead()
      if db != None:
        dbto = dbto - sleeptime 
        if dbto <= 0: 
          TracePrint("main(): Database timeout. Closing connection")
          db.close()
          db = None
  
    except KeyboardInterrupt:
      LogPrint("Program terminated by keyboard interrupt.")
      cleanupandexit()
