# unpack.py
# Author: Meghan Clark

from msgtypes import *

# Creates a LIFX Message out of packed binary data
# If the message type is not one of the officially released ones above, it will create just a Message out of it
# If it's not in the LIFX protocol format, uhhhhh...we'll put that on a to-do list.
def unpack_lifx_message(packed_message):
	header_str = packed_message[0:HEADER_SIZE_BYTES]
	payload_str = packed_message[HEADER_SIZE_BYTES:]

	size = struct.unpack("H", header_str[0:2])[0]
	flags = struct.unpack("H", header_str[2:4])[0]
	origin = (flags >> 14) & 3
	tagged = (flags >> 13) & 1
	addressable = (flags >> 12) & 1
	protocol = flags & 4095
	source_id = struct.unpack("I", header_str[4:8])[0]
	target_addr = ":".join([('%02x' % b) for b in struct.unpack("B"*6, header_str[8:14])])
	response_flags = struct.unpack("B", header_str[22:23])[0]
	ack_requested = response_flags & 2
	response_requested = response_flags & 1
	seq_num = struct.unpack("B", header_str[23:24])[0]
	message_type = struct.unpack("H", header_str[32:34])[0]

	message = None
	if message_type == MSG_IDS[GetService]:
		message = GetService(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateService]:
		service = struct.unpack("B", payload_str[0:1])[0]
		port = struct.unpack("I", payload_str[1:5])[0]
		payload = {"service": service, "port": port}
		message = StateService(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetHostInfo]:
		message = GetHostInfo(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateHostInfo]:
		signal = struct.unpack("f", payload_str[0:4])[0]
		tx = struct.unpack("I", payload_str[4:8])[0]
		rx = struct.unpack("I", payload_str[8:12])[0]
		reserved1 = struct.unpack("h", payload_str[12:14])[0]
		payload = {"signal": signal, "tx": tx, "rx": rx, "reserved1": reserved1}
		message = StateHostInfo(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetHostFirmware]:
		message = GetHostFirmware(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateHostFirmware]:
		build = struct.unpack("Q", payload_str[0:8])[0]
		reserved1 = struct.unpack("Q", payload_str[8:16])[0]
		version = struct.unpack("I", payload_str[16:20])[0]
		payload = {"build": build, "reserved1": reserved1, "version": version}
		message = StateHostFirmware(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetWifiInfo]:
		message = GetWifiInfo(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateWifiInfo]:
		signal = struct.unpack("f", payload_str[0:4])[0]
		tx = struct.unpack("I", payload_str[4:8])[0]
		rx = struct.unpack("I", payload_str[8:12])[0]
		reserved1 = struct.unpack("h", payload_str[12:14])[0]
		payload = {"signal": signal, "tx": tx, "rx": rx, "reserved1": reserved1}
		message = StateWifiInfo(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetWifiFirmware]:
		message = GetWifiFirmware(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateWifiFirmware]:
		build = struct.unpack("Q", payload_str[0:8])[0]
		reserved1 = struct.unpack("Q", payload_str[8:16])[0]
		version = struct.unpack("I", payload_str[16:20])[0]
		payload = {"build": build, "reserved1": reserved1, "version": version}
		message = StateWifiFirmware(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetPower]:
		message = GetPower(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[SetPower]:
		power_level = struct.unpack("H", payload_str[0:2])[0]
		payload = {"power_level": power_level}
		message = SetPower(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[StatePower]:
		power_level = struct.unpack("H", payload_str[0:2])[0]
		payload = {"power_level": power_level}
		message = StatePower(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetLabel]:
		message = GetLabel(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[SetLabel]:
		label = "".join([chr(b) for b in struct.unpack("b"*32, payload_str[0:32])])
		payload = {"label": label}
		message = SetLabel(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateLabel]:
		label = "".join([chr(b) for b in struct.unpack("b"*32, payload_str[0:32])])
		payload = {"label": label}
		message = StateLabel(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetVersion]:
		message = GetVersion(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateVersion]:
		vendor = struct.unpack("I", payload_str[0:4])[0]
		product = struct.unpack("I", payload_str[4:8])[0]
		version = struct.unpack("I", payload_str[8:12])[0]
		payload = {"vendor": vendor, "product": product, "version": version}
		message = StateVersion(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[GetInfo]:
		message = GetInfo(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[StateInfo]:
		time = struct.unpack("Q", payload_str[0:8])[0]
		uptime = struct.unpack("Q", payload_str[8:16])[0]
		downtime = struct.unpack("Q", payload_str[16:24])[0]
		payload = {"time": time, "uptime": uptime, "downtime": downtime}
		message = StateInfo(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[Acknowledgement]:
		message = Acknowledgement(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[EchoRequest]:
		byte_array_len = len(payload_str)
		byte_array = [b for b in struct.unpack("B"*byte_array_len, payload_str[0:byte_array_len])]
		payload = {"byte_array": byte_array}
		message = EchoRequest(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[EchoResponse]:
		byte_array_len = len(payload_str)
		byte_array = [b for b in struct.unpack("B"*byte_array_len, payload_str[0:byte_array_len])]
		payload = {"byte_array": byte_array}
		message = EchoResponse(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[LightGet]:
		message = LightGet(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[LightSetColor]:
		color = struct.unpack("H"*4, payload_str[0:8])
		duration = struct.unpack("I", payload_str[8:12])[0]
		payload = {"color": color, "duration": duration}
		message = LightSetColor(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[LightState]:
		color = struct.unpack("H"*4, payload_str[0:8])
		reserved1 = struct.unpack("H", payload_str[8:10])[0]
		power_level = struct.unpack("H", payload_str[10:12])[0]
		label = "".join([chr(b) for b in struct.unpack("b"*32, payload_str[12:44])])
		reserved2 = struct.unpack("Q", payload_str[44:52])[0]
		payload = {"color": color, "reserved1": reserved1, "power_level": power_level, "label": label, "reserved2": reserved2}
		message = LightState(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[LightGetPower]:
		message = LightGetPower(target_addr, source_id, seq_num, {}, ack_requested, response_requested)

	elif message_type == MSG_IDS[LightSetPower]:
		power_level = struct.unpack("H", payload_str[0:2])[0]
		duration = struct.unpack("I", payload_str[2:6])[0]
		payload = {"power_level": power_level, "duration": duration}
		message = LightSetPower(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	elif message_type == MSG_IDS[LightStatePower]:
		power_level = struct.unpack("H", payload_str[0:2])[0]
		payload = {"power_level": power_level}
		message = LightStatePower(target_addr, source_id, seq_num, payload, ack_requested, response_requested)

	else:
		message = Message(message_type, target_addr, source_id, seq_num, ack_requested, response_requested)

	message.size = size
	message.origin = origin
	message.tagged = tagged
	message.addressable = addressable
	message.protocol = protocol
	message.source_id = source_id
	message.header = header_str
	message.payload = payload_str
	message.packed_message = packed_message

	return message