#!/usr/bin/python

import subprocess
import sys
import threading
import time

from datamap_daemon_common import *

# Node State
last_heartbeat = 0
last_heartbeat_lock = threading.Lock()
state = STATE_DISCONNECTED
state_lock = threading.Lock()
error = ""
error_lock = threading.Lock()
vermont_pid = -1;

# Handler for heartbeat messages from the central server. Tracks the time that the last heartbeat occured.
def handle_heartbeat(conn):
	global last_heartbeat, last_heartbeat_lock, state, state_lock
	logging.debug("Handling heartbeat message")
	last_heartbeat_lock.acquire()
	last_heartbeat = time.time()
	last_heartbeat_lock.release()
	state_lock.acquire()
	if (state == STATE_ERROR):
		error_lock.acquire()
		msg = ACK_MSG + state + ":" + error
		error_lock.release()
	else:
		msg = ACK_MSG + state
	state_lock.release()
	conn.send(msg)

# Handler for start messages from the central server. Starts vermont.
def handle_start(conn):
	global state, state_lock
	logging.info("Received start command. Starting vermont")
	subprocess.call("start_capture &", shell=True)
	logging.info("Vermont started")
	state_lock.acquire()
	state = STATE_COLLECTING
	logging.debug("State: " + state)
	state_lock.release()
	conn.send(ACK_MSG)

# Handler for stop messages from the central server. Kills vermont.
def handle_stop(conn):
	global state, state_lock
	logging.info("Received stop command. Stopping vermont")
	subprocess.call("stop_capture", shell=True)
	logging.info("Vermont Stopped")
	state_lock.acquire()
	state = STATE_CONNECTED
	logging.debug("State: " + state)
	state_lock.release()
	conn.send(ACK_MSG)

# Handler for all messages from the central server. Decodes the message and calls the corresponding function
def handle_server_message(conn,addr,msg):
	logging.debug("Handling \"" + msg + "\" from " + str(addr))
	if (msg == HEARTBEAT_MSG):
		handle_heartbeat(conn)
	elif (msg == START_MSG):
		handle_start(conn)
	elif (msg == STOP_MSG):
		handle_stop(conn)
	else:
		logging.warn("Unexpected message from central server: " + msg)

# Thread for sending hello messages to the central server
class HelloThread(StoppableThread):
	def __init__(self):
		StoppableThread.__init__(self, self.init, self.loop, self.uninit)
	def init(self):
		pass
	def loop(self):
		global last_heartbeat, last_heartbeat_lock, state, state_lock
		last_heartbeat_lock.acquire()
		if (time.time()-last_heartbeat > NODE_HEARTBEATS_TIMEOUT*HEARTBEAT_INT):
			last_heartbeat_lock.release()
			logging.info("Heartbeat message not received for long time. Sending hello message")
			state_lock.acquire()
			state = STATE_DISCONNECTED
			logging.debug("State: " + state)
			state_lock.release()
			result = send_and_get_ack(get_config("server_addr"), int(get_config("server_port")), HELLO_MSG)
			if (result == None):
				last_heartbeat_lock.acquire()
				last_heartbeat = time.time()
				last_heartbeat_lock.release()
				logging.info("Successfully connected to central server")
				state_lock.acquire()
				state = STATE_CONNECTED
				state_lock.release()
				logging.debug("State: " + state)
				result = send_and_get_ack(get_config("server_addr"), int(get_config("server_port")),
					NAME_MSG_PREFIX + get_config("node_id") + "|" + get_config("latitude") + "|" + get_config("longitude"))
				if (result == None):
					last_heartbeat_lock.acquire()
					last_heartbeat = time.time()
					last_heartbeat_lock.release()
					logging.info("Successfully exchanged name with central server. Awaiting Instructions...")
				else:
					logging.error("Name exchange with central server failed: " + result)
			else:
				logging.error("Hello with central server failed: " + result)
		else:
			last_heartbeat_lock.release()
		time.sleep(HEARTBEAT_INT)
	def uninit(self):
		pass
		
# Thread for checking if vermont is actually running
class VermontCheckThread(StoppableThread):
	def __init__(self):
		StoppableThread.__init__(self, self.init, self.loop, self.uninit)
	def init(self):
		pass
	def loop(self):
		global state, state_lock, error, error_lock
		state_lock.acquire()
		if (state == STATE_COLLECTING or state == STATE_ERROR):
			process = subprocess.Popen("ps -fe | grep 'vermont -f' | grep -v grep", shell=True, stdout=subprocess.PIPE)
			result = process.stdout.read()
			process.stdout.close()
			process.wait
			if (not "vermont" in result):
				logging.error("Vermont unexpectedly exited. Entering erro state.")
				state = STATE_ERROR
				error_lock.acquire()
				error = "vermont unexpectedly exited, check logs for details"
				error_lock.release()
			else:
				state = STATE_COLLECTING
		state_lock.release()
		time.sleep(HEARTBEAT_INT)		
	def uninit(self):
		pass
			
# Start the required threads
helloThread = HelloThread()
helloThread.start()

centralServerHandlerThread = ConnectionHandlerThread(int(get_config("node_port")), handle_server_message)
centralServerHandlerThread.start()

vermontCheckThread = VermontCheckThread()
vermontCheckThread.start()

# Run forever (unless there is a keyboard interrupt)
while True:
	try:
		time.sleep(1000)
	except KeyboardInterrupt:
		helloThread.stop()
		centralServerHandlerThread.stop()
		exit(1)
