#  Ham'n'Cheese
#  Copyright (C) 2023-2024 Scott Duensing <scott@kangaroopunch.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>


extends Node


const MODE_READ  := "r"
const MODE_WRITE := "w"


var _edge_pid    := -1
var _udp         := PacketPeerUDP.new()
var _sequence    := 1
var _password    := "n2n"
var _data_buffer := {}
var _data_type   := {}
var _timer       : Timer


signal data_recieved(type: String, data: Array)
signal network_died


func _on_timer_timeout():
	if !OS.is_process_running(_edge_pid):
		stop_edge()
		network_died.emit()
	if _udp.is_socket_connected():
		_send("info")
		_send("edges")


func _process(_delta):
	if _udp.is_socket_connected():
		var data = _udp.get_packet().get_string_from_ascii()
		if _udp.get_packet_error() == OK:
			var json = JSON.new()
			var error = json.parse(data)
			if error == OK:
				var received = json.data
				# Is this the start of a sequence?
				if received["_type"] == "begin":
					# Nuke anything currently in this sequence's slot.
					_data_buffer[received["_tag"]] = []  # This is a dictionary of tags->arrays of dictionaries.
					_data_type[received["_tag"]] = received["cmd"]  # This is a dictionary of tags->strings
				# Is this the end of a squence?
				elif received["_type"] == "end":
					data_recieved.emit(_data_type[received["_tag"]], _data_buffer[received["_tag"]])
				elif received["_type"] == "error":
					_data_buffer[received["_tag"]] = []
					_data_buffer[received["_tag"]].append(received)
					data_recieved.emit("error", _data_buffer[received["_tag"]])
				else:
					_data_buffer[received["_tag"]].append(received)


func _ready():
	# Extract the edge binary.
	var binary = OS.get_user_data_dir() + "/edge"
	if OS.get_name() == "Windows":
		binary = binary + ".exe"
	if !FileAccess.file_exists(binary):
		print("Extracting edge")
		var extract = Embedded.new()
		extract.save_edge(binary)
	if OS.get_name() != "Windows":
		OS.execute("chmod", ["+x", binary])


func _send(what: String, mode: String = MODE_READ):
	if _udp.is_socket_connected():
		var packet = mode + " " + str(_sequence) + ":1:" + _password + " " + what
		_udp.put_packet(packet.to_ascii_buffer())
		_sequence += 1
		if _sequence > 999:
			_sequence = 1


func start_edge(community: String, key: String, supernode: String, port: int = 7777, my_port: int = 5643, management_port: int = 5644):
	_password = Util.generate_password()
	_edge_pid = await Process.elevate(OS.get_user_data_dir() + "/edge", ["--management-password", _password, "-c", community, "-k", key, "-l", supernode + ":" + str(port), "-f"])
	if _edge_pid < 0:
		await Dialog.alert("Error", "Unable to start network layer!")
		return false
	_udp.bind(my_port, "127.0.0.1")
	_udp.connect_to_host("127.0.0.1", management_port)
	_timer = Timer.new()
	_timer.wait_time = 3
	_timer.autostart = true
	_timer.timeout.connect(_on_timer_timeout)
	Engine.get_main_loop().current_scene.add_child(_timer)
	_timer.timeout.emit()
	return true


func stop_edge():
	if _timer != null:
		_timer.queue_free()
	_send("stop", MODE_WRITE)
	_udp.close()
	_edge_pid = -1
