#!/usr/bin/python

# Copyright 2015 Mike Baranski
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

# door.py
#
# Classes for a controller which contains multiple doors
# to abstract the Raspberry Pi hardware.  Requires
# the RPi.GPIO library and assumes an open contact means the door
# is open and a closed contact means the door is closed.  By default
# a .2 sec pulse is sent via the realy hooked up to the switch pin
# to open the door.

import json
import RPi.GPIO as GPIO
import time
import logging
import sys

class Door():
    def __init__(self, switch_pin, contact_pin, open_contact_pin, name, logging_handler = None, pulse_time = .2, lockable = False):
        self.stompClient = None
        self.dbLogger = None
        if logging_handler == None:
            self.l = logging.NullHandler()
        else:
            self.l  = logging_handler

        self.door_logger = logging.getLogger()
        self.door_logger.addHandler(self.l)

        self.switch_pin = switch_pin
        self.contact_pin = contact_pin
        self.open_contact_pin = open_contact_pin
        self.pulse_time = pulse_time
        self.lockable = lockable
        self.name = name

        self.testing = False
        self.testOpenStatus = False

        GPIO.setup(self.switch_pin, GPIO.OUT, initial=GPIO.HIGH)
        GPIO.setup(self.contact_pin, GPIO.IN, pull_up_down = GPIO.PUD_UP)
        if not self.open_contact_pin == None:
            self.door_logger.info("%s has open contact: %d" %(self.name, self.open_contact_pin))
            GPIO.setup(self.open_contact_pin, GPIO.IN, pull_up_down = GPIO.PUD_UP)
        #GPIO.add_event_detect(contact_pin, GPIO.BOTH, callback=cb, bouncetime=2000)

        self.last_status = True == GPIO.input(self.contact_pin)
        self.door_logger.info('Door created: [%s]' %(self.name))
        self.lastCheck = time.time()

    def setStompClient(self, c):
        self.door_logger.info("Setting stomp client")
        self.stompClient = c
        c.send_message("{'name': '%s', 'is_open': '%s'}" %(self.name, self.isOpen()))

    def setDbLogger(self, c):
        self.dbLogger = c
        self.door_logger.info("Setting db logger")
        pass
        
    def setTest(self, inTest):
        self.door_logger.info("Setting %s test to %s" %(self.name, inTest))
        self.testing = inTest

    def statusChanged(self, io):
        if not self.stompClient == None:
            self.stompClient.send_message("{'name': '%s', 'is_open': '%s'}" %(self.name, io))
        if not self.dbLogger == None:
            self.dbLogger.log(self, "changing")
        
    def isOpen(self):
        o = self.testOpenStatus
        self.lastCheck = time.time()
        if not self.testing:
            o = True == GPIO.input(self.contact_pin)
            oo = None
            if not self.open_contact_pin == None:
                oo = True == GPIO.input(self.open_contact_pin)

            self.door_logger.info("Reading [%s] pin %d open is -> %s and open contact pin [%s] status is [%s]", self.name, self.contact_pin, o, self.open_contact_pin, oo)

            if not o == self.last_status:
                self.statusChanged(o)
                self.last_status = o
            if not self.dbLogger == None:
                self.dbLogger.log(self, o)
            return o

    def pushButton(self):
        self.door_logger.root.info("Pushing button for %s open is -> %s", self.name, self.isOpen())
        if not self.testing:
            if not self.dbLogger == None:
                self.dbLogger.log(self, "button")
            GPIO.output(self.switch_pin, False)
            time.sleep(.21)
            GPIO.output(self.switch_pin, True)
        else:
            self.testOpenStatus = not self.testOpenStatus
            return True

    def __repr__(self):
        return "Door %s [switch=%d - contact=%d - isOpen=%s]" %(self.name, self.switch_pin, self.contact_pin, self.isOpen())

    def toString(self):
        return "%s is open: %s" %(self.name, self.isOpen())

class Controller():
    def __init__(self, doorFile, logging_handler, stomp_client = None, db_logger = None):
        self.lastCheck = 0
        self.stomp_client = stomp_client
        self.db_logger = db_logger
        print 'using db logger %s' %db_logger
        if logging_handler == None:
            self.l = logging.NullHandler()
        else:
            self.l  = logging_handler

        self.controller_logger = logging.getLogger()
        self.controller_logger.addHandler(self.l)

        GPIO.setwarnings(True)
        GPIO.setmode(GPIO.BOARD)

        self.controller_logger.addHandler(self.l)

        f = open(doorFile, 'r')
        config = json.loads(f.read())
        self.controller_logger.debug("Door file data %s" %(config))
        self.doors = {}
        for d in config:
            self.controller_logger.debug("Door is %s" %d)
            self.doors[d['name']] = Door(d['switch'], d['contact'], d['open_contact'], d['name'], logging_handler)
            if self.stomp_client != None:
                print 'using stomp'
                self.doors[d['name']].setStompClient(self.stomp_client)
            if self.db_logger != None:
                self.doors[d['name']].setDbLogger(self.db_logger)
            self.controller_logger.debug("Created door %s" %(self.doors[d['name']].toString()))
            pass

    def putInTest(self):
        self.controller_logger.info("Putting in test")
        for d in self.doors.keys():
            self.doors[d].setTest(True)
            return

    def stopTest(self):
        self.controller_logger.info("Stopping test")
        for d in self.doors.keys():
            self.doors[d].setTest(False)
            return

    def getDoors(self):
        return self.doors

    def getDoor(self, name):
        return self.doors[name]

    def getDoorsStatus(self):
        self.lastCheck = time.time()
        json_doors = {}
        for d in self.doors:
            d = self.doors[d]
            json_doors[d.name] = d.isOpen()
        return json_doors
