import cPickle
import datetime
import itertools
import os
import socket
import sys
import time

import RPIO

from bfcslib.relay import Relay
from bfcslib.sensor import Sensor
from bfcslib.sensor_reading import SensorReading
from bfcslib.seven_seg import SevenSegmentDisplay
from bfcslib.status_led import LED
from bfcslib.tmp512 import TMP512

# Global variables
NUM_SENSORS = 4
sensor_iterator = itertools.cycle(range(0, NUM_SENSORS))
CURRENT_IDX = sensor_iterator.next()
SENSORS = None

THRESHOLD_TEMP = 82

HOST = '10.0.0.4' # phallanx for testing
PORT = 2243
REMOTE_DATA_INTERVAL = 20 # in seconds

def button_isr(gpio_id, val):
	global CURRENT_IDX
	global SENSORS

	# Turn off previous sensor indicator LED
	SENSORS[CURRENT_IDX].led.off()

	# Update current index
	# print 'CURRENT_IDX: %s' % CURRENT_IDX
	CURRENT_IDX = sensor_iterator.next()

	# Turn on new sensor indicator LED
	SENSORS[CURRENT_IDX].led.on()

def main():
	global CURRENT_IDX
	global SENSORS

	# Disable "channel already in use" warnings
	RPIO.setwarnings(False)

	print 'Beer Fermentation Control System - RPI Client'

	# Make sure the script is being run as root before continuing
	if os.geteuid() != 0:
		print 'ERROR: You must run the client as root!'
		return

	# Attempt to connect to the server, make 5 attempts
	print '\n\nAttempting to connect to server...',
	sys.stdout.flush()

	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	tries = 5
	while tries > 0:
		try:
			s.connect((HOST, PORT))
			break
		except socket.error:
			tries -= 1
			if tries == 0:
				print 'FAILED'
				print 'Unable to connect to %s:%s check your connection and ' \
					  'try again.' % (HOST, PORT)
				return
		time.sleep(1)

	# Connection was successful
	print 'SUCCESS'
	print 'Connected to %s:%s' % (HOST, PORT)

	# Setup the sensor status LEDs
	sensor1_led = LED(24)
	sensor2_led = LED(25)
	sensor3_led = LED(8)
	sensor4_led = LED(7)

	# Setup both TMP512's
	tmp512_1 = TMP512(0x5d)
	tmp512_2 = TMP512(0x5c)

	# Create the sensor objects to encapsulate a status LED and temperature
	# sensor channel
	sensor1 = Sensor(sensor1_led, tmp512_1.temp_sensor2)
	sensor2 = Sensor(sensor2_led, tmp512_1.temp_sensor1)
	sensor3 = Sensor(sensor3_led, tmp512_2.temp_sensor2)
	sensor4 = Sensor(sensor4_led, tmp512_2.temp_sensor1)

	# Create the global list of all the sensor objects
	SENSORS = [sensor1, sensor2, sensor3, sensor4]

	# Turn on initial sensor LED
	SENSORS[CURRENT_IDX].led.on()

	# Setup 7 seg display
	ss_display = SevenSegmentDisplay()

	# Setup relay
	relay = Relay()
	relay_state = False

	# Setup pushbutton
	# Use the BCM addressing scheme, set GPIO 22 as input
	RPIO.setmode(RPIO.BCM)
	RPIO.setup(22, RPIO.IN)

	# Setup switch interrupt with debounce
	RPIO.add_interrupt_callback(22, button_isr, edge='rising', 
								pull_up_down=RPIO.PUD_DOWN, threaded_callback=True, 
								debounce_timeout_ms=150)

	# Wait for interrupts in a non-blocking fashion
	RPIO.wait_for_interrupts(threaded=True)

	# Main loop
	interval_counter = 0
	while True:

		# Reset variables
		temps = []
		if interval_counter == REMOTE_DATA_INTERVAL + 1:
			interval_counter = 0

		# Grab the current date/time
		now = datetime.datetime.now()

		# Iterate over all temperature sensors and read their temp
		for idx in xrange(NUM_SENSORS):
			
			# Get the current temperature reading 
			temp = SENSORS[idx].temp
			temps.append(temp)

			# Display the currently selected temp sensor value
			if idx == CURRENT_IDX:
				ss_display.display_number(temp)

		# Bit 'o debug information
		print now.strftime('%m/%d/%Y %H:%M:%S')
		for idx in xrange(NUM_SENSORS):
			print '\t Sensor %d: %d' % (idx + 1, temps[idx])

		# Sort all the temperature readings for easier manipulation
		temps.sort(reverse=True)

		if relay_state:
			# Relay is on, decide if we need to turn it off
			if temps[0] < THRESHOLD_TEMP and \
			   temps[1] < THRESHOLD_TEMP and \
			   temps[2] < THRESHOLD_TEMP and \
			   temps[3] < THRESHOLD_TEMP:
				relay_state = False
				relay.off()
		else:
			# Relay is off, decide if we need to turn it on
			if temps[0] > THRESHOLD_TEMP and temps[1] > THRESHOLD_TEMP:
				relay_state = True
				relay.on()

		# Send a reading to the server once every minute
		if interval_counter == REMOTE_DATA_INTERVAL:
			print 'Sending reading to server...',
			sys.stdout.flush()
			reading = SensorReading(now, temps[0], temps[1], temps[2],
									temps[3], relay_state)
			try:
				s.sendall(cPickle.dumps(reading))
				print 'DONE'
			except:
				print 'ERROR'

		interval_counter += 1

		# Read once ever second
		time.sleep(1)

	s.close()



if __name__ == '__main__':
	sys.exit(main())