class AuditClientHandler(threading.Thread):
	''' This is an active thread that is responsible for serving all
	messages that come in from audit proxy.
	'''

	def __init__(self, serv, params, keyMgr):
		''' Initialize the client handler with the parent server (AuditProxy)
		'''
		threading.Thread.__init__(self)
		self.server = serv
		self.clientList = []
		self.running = True

		# Setup the Python logger
		logFile = 'abls.log'
		logging.basicConfig(filename=logFile,level=logging.DEBUG)

		# Persist the key manager and params
		self.keyMgr = keyMgr
		self.params = params

		# Maintain login state of the user is logged in for this session
		self.loggedIn = False

	def run(self):
		''' The main loop for this client handler thread. Strip out a message,
		parse it according to the protocol, and then invoke the necessary commands.
		'''
		global MSG_LOGIN

		self.shim = DBShim(self.params["AUDIT_USER_DB"], self.keyMgr)
		self.log = DBShim(self.params["LOG_DB"], self.keyMgr)
		while self.running:
			for client in self.clientList:
				message = client.sock.recv(self.server.BUFFSIZE)
				if message != None and message != "":
					try:
						parsedMsg = json.loads(message)
						if (int(parsedMsg['command']) == MSG_LOGIN):
							if (self.login(parsedMsg['parameters'])):
								client.sock.send('{"result":True,"message":"Login successful."}')
						elif (self.loggedIn == True):
							client.sock.send(self.parseMessage(parsedMsg))
					except Exception as e:
						client.sock.send(str(e))

	def login(self, params):
		''' Handle the user login process.
		'''
		# Strip out the username and password
		userNameIndex = params.find(',')
		if (userNameIndex != -1):
			userName = params[0:userNameIndex]
			password = params[userNameIndex + 1:] # format: username,password

			# Fetch the salt from the database (there should only be one user by this name)
			record = self.shim.executeQuery("audit_users", "userName", userName, False)
			if (len(record) == 1):
				salt = record[0]["salt"]
				hashed_password = hashlib.sha512(password + salt).hexdigest()
				if (hashed_password == record[0]["password"]):
					self.loggedIn = True
					return True
				else:
					raise Exception("Invalid password for user: "******"Could not find the specified user: "******"client message: " + str(message)) # debug

		# Verify that the incoming message conforms to the protocol
		if (len(message) != 2):
			raise Exception("Invalid JSON string retrieved from client.")
		if not (('command' in message) and ('parameters' in message)):
			raise Exception("Invalid JSON string retrieved from client.")

		# Let it rip
		try:
			return self.execute(int(message['command']), message['parameters'])
		except:
			print("Error occured when executing the command")

	def execute(self, command, parameters):
		''' Execute the command (if possible), or throw an exception if it's invalid.
		'''

		# Bring the protocol event IDs into scope
		global MSG_SELECT_BY_USER
		global MSG_SELECT_BY_USER_SESSION

		result = ""

		# Handle the incoming message
		if (command == MSG_SELECT_BY_USER):
			#print("MSG_SELECT_BY_USER")
			#print(parameters[0])
			valueMap = {"userId" : parameters[0]}
			rowMasks = ["userId"]

			try:
				results = self.log.executeMultiQuery("log", valueMap, rowMasks)

				# Format the results to return only the ciphertext
				logList = []
				for i in range(0, len(results)):
					logList.append(results[i][4]) # these are encoded in "hex" - decode with .decode("hex")
				return json.dumps({"result" : True, "message" : json.dumps(logList)})
			except Exception as e:
				print(e)
		elif (command == MSG_SELECT_BY_USER_SESSION):
			#print("MSG_SELECT_BY_USER_SESSION")
			#print(parameters[0])
			valueMap = {"userId" : entry.userId, "sessionId" : entry.sessionId}
			rowMasks = ["userId", "sessionId"]

			try:
				results = self.log.executeMultiQuery("log", valueMap, rowMasks)

				# Format the results to return only the ciphertext
				logList = []
				for i in range(0, len(results)):
					logList.append(results[i][4]) # these are encoded in "hex" - decode with .decode("hex")
				return json.dumps({"result" : True, "message" : json.dumps(logList)})
			except:
				print(e)
		else: 
			raise Exception("Unsupported audit command ID: " + str(command))

		# Default return (if other messages don't work)
		return json.dumps({"result" : False, "message" : "Error parsing message: " + str(command)})