# server_node.gd
class_name ServerNode
extends Node

signal camera_position_updated(pos_x, pos_y, pos_z)
signal camera_rotation_updated(rot_x, rot_y, rot_z)
signal sensor_point_generated(pos_x, pos_y, pos_z)

var server = UDPServer.new()
var port = 5001
var awaiting_sensors = {}
var invalid_sensors = {}
var valid_sensors = {}

func _ready():
	ServerSignals.sensor_approved.connect(_on_sensor_approved)
	ServerSignals.sensor_declined.connect(_on_sensor_declined)
	server.listen(port, "0.0.0.0")
	if not server.is_listening():
		print('Unable to listen on port: ', port)
	print('Listening on port: ', port)

func _on_sensor_approved(sensor_ip: String):
	valid_sensors[sensor_ip] = awaiting_sensors[sensor_ip]
	if sensor_ip in awaiting_sensors:
		awaiting_sensors.erase(sensor_ip)
	SensorSignals.new_sensor_connected.emit(sensor_ip, valid_sensors[sensor_ip])

func _on_sensor_declined(sensor_ip: String):
	invalid_sensors[sensor_ip] = 0
	if sensor_ip in awaiting_sensors:
		awaiting_sensors.erase(sensor_ip)

func _process(delta):
	_process_connections(delta)

func _process_connections(delta):
	server.poll()
	if server.is_connection_available():
		var peer = server.take_connection()
		var packet = peer.get_packet()
		if not _verify_checksum(packet):
			print('Received packet with incorrect checksum: %s' % peer.get_packet_ip())
			_handle_six_fps_message(peer, packet)
		else:
			_handle_new_connection(delta, peer, packet)

func _handle_new_connection(delta: float, peer: PacketPeerUDP, packet: PackedByteArray):
	if packet.get(0) == 0xD4 and packet.get(1) == 0x53:
		"""
		| 2 bytes      | 2 bytes       | 2 bytes        | variable length | 2 bytes  |
		| packet start | message class | payload length | payload         | checksum |
		First two bytes are the frame start - always 0xD4 and 0x53
		The two bytes for message class and id identify the packet contents
		The two bytes for payload length should be read as an u16 integer
		and indicate the payload size.
		The 2-byte checksuim is calculated over the packet's contents - the message
		class and id bytes, payload length byts, and the payload itself.
		The formula is:
			var ck_a = 0
			var ck_b = 0
			for i in range(2, packet.size()-2):
				ck_a += packet.get(i)
				ck_b += ck_a
			packet[packet.size()-2] = ck_a
			packet[packet.size()-1] = ck_b
		"""
		_handle_six_fps_message(peer, packet)
	elif packet.get(0) == 0xb5 and packet.get(1) == 0x62:
		_handle_racebox_message(delta, packet)
	else:
		print("Received: '%s' %s:%s" % [packet.get_string_from_utf8(), peer.get_packet_ip(), peer.get_packet_port()])

	for i in range(0, valid_sensors.size()):
		pass # Do something with the connected peers.

func _calculate_checksum(packet) -> Array:
	var ck_a = 0
	var ck_b = 0
	for i in range(2, packet.size()-2):
		ck_a += packet.get(i)
		ck_b += ck_a
	return [ck_a % 0x100, ck_b % 0x100]

func _verify_checksum(packet) -> bool:
	var calculated_checksum = _calculate_checksum(packet)
	return packet.get(packet.size()-1) == calculated_checksum[1] and packet.get(packet.size()-2) == calculated_checksum[0]
		
func _handle_six_fps_message(peer: PacketPeerUDP, packet: PackedByteArray):
	var peer_ip = peer.get_packet_ip()
	if peer_ip in invalid_sensors:
		return
	if peer_ip in awaiting_sensors:
		return
	var valid_sensor: bool = peer.get_packet_ip() in valid_sensors
	var message_class = packet.get(2)
	var message_id = packet.get(3)
	if (not valid_sensor) and (message_class == 0x6e and message_id == 0x77):
		_handle_new_peer_request(peer_ip, packet)
	if not valid_sensor:
		return
	elif message_class == 0x6e and message_id == 0x4e:
		_handle_heartbeat_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0xac:
		_handle_time_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0x7c:
		#print('GPS packet received')
		_handle_position_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0x7b:
		#print('Sats packet received')
		_handle_gps_sats_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0xfa:
		#print('Accel packet received')
		_handle_accel_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0x39:
		#print('Mag packet received')
		_handle_magnetic_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0xa7:
		_handle_public_key_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0x5e: # not in use
		_handle_euler_packet(peer_ip, packet)
	elif message_class == 0x2a and message_id == 0xba: # not in use
		_handle_quaternion_packet(peer_ip, packet)
	
func _handle_new_peer_request(peer_ip: String, packet: PackedByteArray):
	"""
	offset | size   | type | description
	4      | 1 byte | bool | peer provides datetime
	5      | 1 byte | bool | peer provides gps
	6      | 1 byte | bool | peer provides accelerometer
	7     | 1 byte | bool | peer provides gyroscope
	8     | 1 byte | bool | peer provides magnetometer
	9     | 1 byte | bool | peer provides euler orientation
	10     | 1 byte | bool | peer provides quaternion orientation
	"""
	awaiting_sensors[peer_ip] = [
		#packet.decode_u8(4),
		packet.decode_u8(5),
		packet.decode_u8(6),
		packet.decode_u8(7),
		packet.decode_u8(8),
		#packet.decode_u8(9),
		#packet.decode_u8(10),
		]
	print('sensor request')
	ServerSignals.sensor_request_received.emit(peer_ip, awaiting_sensors[peer_ip])
	
func _parse_timestamp_slice(packet: PackedByteArray):
	var hour = packet.decode_u8(0)
	var minute = packet.decode_u8(1)
	var second = packet.decode_u8(2)
	var nanosecond = packet.decode_u32(3) * pow(10,-9)
	return [hour, minute, second, nanosecond]
	
func _handle_heartbeat_packet(peer_ip: String, packet: PackedByteArray):
	#print('heartbeat')
	var hour = packet.decode_u8(4)
	var minute = packet.decode_u8(5)
	var second = packet.decode_u8(6)
	#print(hour, ' ', minute, ' ', second)
	
func _handle_time_packet(peer_ip: String, packet: PackedByteArray):
	#print('time')
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	#print(timestamp_list)
	
func _handle_position_packet(peer_ip: String, packet: PackedByteArray):
	#print('gps')
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	var lat: float = (float(packet.decode_u64(11)) / 10.**9) - 90
	var lon: float = (float(packet.decode_u64(19)) / 10.**9) - 180
	var alt_neg = packet.decode_u8(27)
	var alt_m: float = packet.decode_u32(28) / 1000.0 * (-1. ** alt_neg)
	var geoid_neg = packet.decode_u8(32)
	var goid_m: float = packet.decode_u16(33) / 100 * (-1. ** geoid_neg)
	var speed_neg = packet.decode_u8(35)
	var speed_kmh: float = packet.decode_u32(36) / 1000000.0 * (-1 ** speed_neg)
	var pdop: float = packet.decode_u32(40) / 1000.0
	var hdop: float = packet.decode_u32(44) / 1000.0
	var vdop: float = packet.decode_u32(49) / 1000.0
	DataSignals.pnt_data_received.emit(peer_ip, timestamp_list, [lat, lon, alt_m, goid_m], speed_kmh, [pdop, hdop, vdop])
	
func _handle_gps_sats_packet(peer_ip: String, packet: PackedByteArray):
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	var ga = packet.decode_u8(11)
	var gb = packet.decode_u8(12)
	var gi = packet.decode_u8(13)
	var gl = packet.decode_u8(14)
	var gp = packet.decode_u8(15)
	var gq = packet.decode_u8(16)
	var gn = packet.decode_u8(17)
	var num_entries = packet.decode_u8(18)
	var sats_offset = 19
	var el_offset = 4
	var az_offset = 5
	var sat_data = []
	for i in range(num_entries):
		var start_offset = sats_offset + (i * 7)
		var sat_name  = packet.slice(start_offset, start_offset + el_offset).get_string_from_ascii()
		var elevation = packet.decode_u8(start_offset+el_offset)
		var azimuth = packet.decode_u16(start_offset+az_offset)
		sat_data.append([sat_name, elevation, azimuth])
	DataSignals.sat_data_received.emit(peer_ip, timestamp_list, [ga, gb, gi, gl, gp, gq, gn], sat_data)
	
func _handle_accel_packet(peer_ip: String, packet: PackedByteArray):
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	var neg = packet.decode_u8(11)
	var x = (packet.decode_s32(12) / 1000000.0) * (-1 ** neg)
	neg = packet.decode_u8(16)
	var y = (packet.decode_s32(17) / 1000000.0) * (-1 ** neg)
	neg = packet.decode_u8(21)
	var z = (packet.decode_s32(22) / 1000000.0) * (-1 ** neg)
	
	neg = packet.decode_u8(26)
	var lin_x = (packet.decode_s32(27) / 1000000.0) * (-1 ** neg)
	neg = packet.decode_u8(31)
	var lin_y = (packet.decode_s32(32) / 1000000.0) * (-1 ** neg)
	neg = packet.decode_u8(36)
	var lin_z = (packet.decode_s32(37) / 1000000.0) * (-1 ** neg)
	
	neg = packet.decode_u8(41)
	var g_x = (packet.decode_s32(42) / 1000000.0) * (-1 ** neg)
	neg = packet.decode_u8(46)
	var g_y = (packet.decode_s32(47) / 1000000.0) * (-1 ** neg)
	neg = packet.decode_u8(51)
	var g_z = (packet.decode_s32(52) / 1000000.0) * (-1 ** neg)
	
	DataSignals.accelerometer_data_received.emit(peer_ip, timestamp_list, [x, y, z], [lin_x, lin_y, lin_z], [g_x, g_y, g_z])
	
func _handle_magnetic_packet(peer_ip: String, packet: PackedByteArray):
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	var neg = packet.decode_u8(11)
	var x = (packet.decode_u32(12) / 1000.0) * (-1 ** neg)
	neg = packet.decode_u8(16)
	var y = (packet.decode_u32(17) / 1000.0) * (-1 ** neg)
	neg = packet.decode_u8(21)
	var z = (packet.decode_u32(22) / 1000.0) * (-1 ** neg)
	DataSignals.magnetometer_data_received.emit(peer_ip, timestamp_list, [x, y, z])
	
func _handle_public_key_packet(peer_ip: String, packet: PackedByteArray):
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	
	
func _handle_euler_packet(peer_ip: String, packet: PackedByteArray):
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	
	
func _handle_quaternion_packet(peer_ip: String, packet: PackedByteArray):
	var timestamp_list = _parse_timestamp_slice(packet.slice(4, 11))
	

func _handle_racebox_message(delta, packet):
	#print(packet.size())
	var year = packet.decode_u16(10)
	var month = packet.decode_u8(12)
	var day = packet.decode_u8(13)
	var hour = packet.decode_u8(14)
	var minute = packet.decode_u8(15)
	var second = packet.decode_u8(16)
	var dt_valid_flags = _to_binary(packet.decode_u8(17))
	var valid_date = int(dt_valid_flags[-1]) == 1
	var valid_time = int(dt_valid_flags[-2]) == 1
	var dt_fully_resolved = int(dt_valid_flags[-3]) == 1
	var valid_mag_dec = int(dt_valid_flags[-4]) == 1
	var time_accuracy_ns = packet.decode_u32(18)
	var nano_second = packet.decode_u32(22)
	var fix_status_flags = _to_binary(packet.decode_u8(27))
	var dt_flags = _to_binary(packet.decode_u8(28))
	var num_sv = packet.decode_u8(29)
	var lon_deg = float(packet.decode_u32(30)) / (10**7)
	var lat_deg = float(packet.decode_u32(34)) / (10**7)
	var wgs_alt_m = float(packet.decode_u32(38)) / (10**3) + 6378.137
	var msl_alt_m = float(packet.decode_u32(42)) / (10**3) + 6378.137
	var horz_accuracy_m = float(packet.decode_u32(46)) / (10**3)
	var vert_accuracy_m = float(packet.decode_u32(50)) / (10**3)
	var speed_mps = float(packet.decode_s32(54)) / (10**3)
	var heading_deg = float(packet.decode_s32(58)) / (10**5)
	var speed_accuracy_mps = float(packet.decode_u32(62)) / (10**3)
	var heading_accuracy_deg = float(packet.decode_u32(66)) / (10**5)
	var pdop = float(packet.decode_u16(70)) / 100
	var lat_lon_flags = _to_binary(packet.decode_u8(72))
	var battery_status = _to_binary(packet.decode_u8(73))
	var g_force_x = packet.decode_s16(74) # milli-g
	var g_force_y = packet.decode_s16(76) # milli-g
	var g_force_z = packet.decode_s16(78) # milli-g
	var rot_rate_x_dps = float(packet.decode_s16(80)) / 100
	var rot_rate_y_dps = float(packet.decode_s16(82)) / 100
	var rot_rate_z_dps = float(packet.decode_s16(84)) / 100
	if num_sv > 0:
		var lon_rad = deg_to_rad(lon_deg)
		var lat_rad = deg_to_rad(lat_deg)
		var sensor_pos_x = wgs_alt_m * sin(lon_rad) * cos(lat_rad)
		var sensor_pos_y = wgs_alt_m * sin(lon_rad) * sin(lat_rad)
		var sensor_pos_z = wgs_alt_m * cos(lon_rad)
		var camera_pos_x = (wgs_alt_m+1) * sin(lon_rad) * cos(lat_rad)
		var camera_pos_y = (wgs_alt_m+1) * sin(lon_rad) * sin(lat_rad)
		var camera_pos_z = (wgs_alt_m+1) * cos(lon_rad)
		camera_position_updated.emit(camera_pos_x, camera_pos_y, camera_pos_z)
		camera_rotation_updated.emit(deg_to_rad(rot_rate_x_dps) * delta, deg_to_rad(rot_rate_y_dps) * delta, deg_to_rad(rot_rate_z_dps) * delta)
		sensor_point_generated.emit(sensor_pos_x, sensor_pos_y, sensor_pos_z)
	else:
		print(Time.get_datetime_string_from_system())
		print("GPS has no sources")

func _to_binary(intValue: int) -> String:
	var bin_str: String = ""
	while intValue > 0:
		bin_str = str(intValue & 1) + bin_str
		intValue = intValue >> 1
	return bin_str
	
