# Example of using the MQTT client class to subscribe to a feed and print out
# any changes made to the feed.  Edit the variables below to configure the key,
# username, and feed to subscribe to for changes.

# Import standard python modules.
import sys
from time import sleep #, strftime
import json

import random
import time
import datetime
import logging

#class to operate the treat dispensers
from classTreatDispenser import TreatDispenser
from classEmailServer import EmailServer
from classPerson import Person

# Import Adafruit IO MQTT client.
from Adafruit_IO import MQTTClient
conf = json.load(open('conf.json'))
logging.basicConfig(filename=conf["log"]["logListenMQTT"],level=logging.DEBUG)

# Set to your Adafruit IO key & username below.
ADAFRUIT_IO_KEY      = conf["adafruit"]["ADAFRUIT_IO_KEY"]
ADAFRUIT_IO_USERNAME = conf["adafruit"]["ADAFRUIT_IO_USERNAME"]

# Set to the ID of the feed to subscribe to for updates.
feed = conf["adafruit"]["feed"]

#for the ball launcher, init the # of iterations to 0
iteration = 0

# Define callback functions which will be called when certain events happen.
def connected(client):
    # Connected function will be called when the client is connected to Adafruit IO.
    # This is a good place to subscribe to feed changes.  The client parameter
    # passed to this function is the Adafruit IO MQTT client so you can make
    # calls against it easily.
    print 'Connected to Adafruit IO at {1}!  Listening for {0} changes...'.format(feed,str(datetime.datetime.now()))  
    logging.info('Connected to Adafruit IO!  Listening for {0} changes at {1}...'.format(feed,str(datetime.datetime.now())))
    # Subscribe to changes on a feed.
    client.subscribe(feed)

def disconnected(client):
    # Disconnected function will be called when the client disconnects.
    print 'Disconnected from Adafruit IO!'
    logging.error('Disconnected from Adafruit IO feed {0} at {1}...'.format(feed,str(datetime.datetime.now())))
    sys.exit(1)

def message(client, feed, payload):
    # Message function will be called when a subscribed feed has a new value.
    # The feed_id parameter identifies the feed, and the payload parameter has
    # the new value.
	print 'Feed {0} received new value: {1} at {2}'.format(feed, payload, str(datetime.datetime.now()))

	if payload == 'ping':
		logging.info('Feed {0} received ping at {2}'.format(feed, payload, str(datetime.datetime.now())))
	if payload == 'Stop':
		isManualInterruption = True
	else:
		logging.info('Feed {0} received trigger: {1} at {2}'.format(feed, payload, str(datetime.datetime.now())))

		#if the pyaload was triggered by the RFID reader, dispense the treats and trigger the ball launcher
		if payload == conf["arduino"]["triggerTreat"] and isManualInterruption == False:
			logging.info('RFID triggered treat machine at '.format(str(datetime.datetime.now())))
			dispenser.makeNoise()
			dispenser.dispenseTreats()
			if iteration < conf["arduino"]["maxIterations"]:
				#as long as motion is detected, don't do anything
				while dispenser.isMotionDetected():
					delay(1)
				iteration = iteration + 1
				dispenser.makeNoiseForLaunchBall()
				aio.send(conf["adafruit"]["launchBallFeed"], conf["arduino"]["triggerLaunchBall"]) 
			else: 
				iteration = 0
				
		#Special use case for when we need some alone time and want the dog to eat treats instead of trying to be with us
		if payload == 'AloneTime':
			logging.info('AloneTime triggered at '.format(str(datetime.datetime.now())))
			isManualInterruption = False
			dispenser.makeNoiseForLaunchBall()
			aio.send(conf["adafruit"]["launchBallFeed"], conf["arduino"]["triggerLaunchBall"]) 

		#probably the normal case of just dispensing treats
		#not very good code...
		else:
			payloadPerson(payload)

        print 'waiting for next triggering event...'

##########################################
#PAYLOAD - FROM A PERSON
##########################################
def payloadPerson(payload):
	
	person.setKey (payload)
	if person.getEmail() == 'nothing':	#no email corresponding to the  payload
		print 'No email corresponding to payload: ' + payload
	else:
		emailServer.setMailList(person.getEmail())
		logging.info('At ' + str(datetime.datetime.now()) + ' got email list. Making noise for ' + payload)
		dispenser.makeNoise()
		logging.info('At ' + str(datetime.datetime.now()) + ' made noise. dispensing treats for ' + payload)
		dispenser.dispenseTreats()
		logging.info('At ' + str(datetime.datetime.now()) + ' dispenses treats. detecting motion for ' + payload)
		if dispenser.isMotionDetected():
			logging.info('At ' + str(datetime.datetime.now()) + ' motion detected. taking video for ' + payload)
			dispenser.takeVideo()
			if payload == conf["proactive"]["proactiveTrigger"]:
				emailServer.setProactivelyDelivered() #set variables to indicate that Pickles dispensed his own treats
			else:
				emailServer.setTreatReceived() #set the email server variables to return a response to the server
			logging.info('At ' + str(datetime.datetime.now()) + ' set treat received. sending thank you to ' + payload)
			emailServer.sendThankYou()
			logging.info('At ' + str(datetime.datetime.now()) + ' successfully sent thank you to ' + payload)
		else:
			emailServer.setTreatNotReceived()
			logging.info('At ' + str(datetime.datetime.now()) + ' treat not received. sending thank you to ' + payload)
			emailServer.sendThankYou()
			logging.info('At ' + str(datetime.datetime.now()) + ' treat not received. successfully sent thank you to ' + payload)
			

#Instantiate objects for treat dispensing machine
print 'begin initialization process'
logging.info('At ' + str(datetime.datetime.now()) + ' begin init process')
dispenser = TreatDispenser('dispenser')
print 'dispenser initialized'
logging.info('At ' + str(datetime.datetime.now()) + ' dispenser class initialized')
emailServer = EmailServer('emailServer')
print 'email server initialized'
logging.info('At ' + str(datetime.datetime.now()) + ' email class initialized')
person = Person('person')
print 'person initialized'
logging.info('At ' + str(datetime.datetime.now()) + ' person class initialized')


# Create an MQTT client instance.
client = MQTTClient(ADAFRUIT_IO_USERNAME, ADAFRUIT_IO_KEY)

# Setup the callback functions defined above.
client.on_connect    = connected
client.on_disconnect = disconnected
client.on_message    = message

# Connect to the Adafruit IO server.
client.connect()

# Start a message loop that blocks forever waiting for MQTT messages to be
# received.  Note there are other options for running the event loop like doing
# so in a background thread--see the mqtt_client.py example to learn more.

#trying loop blocking to see if it will auto terminate the program upon disconnect
client.loop_blocking()

#background loop was failing to exit upon disconnect. No try except loop in the disconnect but it does have a hard sys.exit(1)
#no need to connect in advance for background because will connect in the infinite loop
#client.loop_background()


#client.loop()
#print 'Looping to keep connection alive.'
#value = 1

#try:
#    while True:
#	    if client.is_connected():
#		    #do nothing
#		    value = 1
#	    else:
#		    print 'Client not connected, trying to connect at {0}'.format(str(datetime.datetime.now()))
#		    logging.info('Client not connected, trying to connect at {0}'.format(str(datetime.datetime.now())))		    
#            client.connect()
#            time.sleep(10)
#except:
#    print('Unexpected error: {0} at {1}'.format(sys.exc_info()[0], str(datetime.datetime.now())))
#    logging.error('Unexpected error: {0} at {1}'.format(sys.exc_info()[0], str(datetime.datetime.now())))
