/*
 * Kangaroo Punch MultiPlayer Game Server Mark II
 * Copyright (C) 2020-2021  Scott Duensing
 *
 * 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 <https://www.gnu.org/licenses/>.
 *
 */


#include "gui.h"
#include "task.h"
#include "comport.h"
#include "config.h"
#include "timer.h"

#include "network.h"


typedef struct NetworkPacketS {
	uint8_t             key;
	PacketDecodeDataT **value;
} NetworkPacketT;


static NetworkPacketT *_packets    = NULL;
static uint8_t         _netRunning = 0;


uint8_t netGetChannelFree(void) {
	uint8_t  channel = 0;  // We reserve 0 for system stuff.  Returning 0 means no channel was found.
	uint16_t x       = 0;

	for (x=1; x<255; x++)	{
		if (hmgeti(_packets, x) < 0) {
			channel = x;
			break;
		}
	}

	return channel;
}


PacketDecodeDataT *netGetPacket(uint8_t channel) {
	int32_t            r      = 0;
	PacketDecodeDataT *packet = NULL;

	r = hmgeti(_packets, channel);
	if (r < 0) return NULL;
	if (arrlen(_packets[r].value) == 0) return NULL;
	packet = _packets[r].value[0];
	arrdel(_packets[r].value, 0);

	return packet;
}


void netStop(void) {
	_netRunning = 0;
}


void taskNetwork(void *data) {
	int32_t             r            = 0;
	int8_t              pingTimeout  = 0;
	char                buffer[1024] = { 0 };
	PacketDecodeDataT  *packet       = NULL;
	PacketDecodeDataT   decoded      = { 0 };
	PacketEncodeDataT   encoded      = { 0 };

	(void)data;

	_netRunning = 1;

	do {

		//***TODO*** Detect disconnection.  Maybe have callbacks registered that can notify tasks?

		// Read pending bytes.
		r = comRead(__configData.serialCom - 1, buffer, 1024);
		if (r > 0) {
			// Decode them into packets.
			if (packetDecode(__packetThreadData, &decoded, buffer, r)) {
				// Is this a PONG?  If so, ignore it.
				if (decoded.packetType == PACKET_TYPE_PONG) continue;

				// Copy the packet out.
				NEW(PacketDecodeDataT, packet);
				packet->channel    = decoded.channel;
				packet->length     = decoded.length;
				packet->packetType = decoded.packetType;
				packet->data = (char *)malloc(decoded.length);
				memcpy(packet->data, decoded.data, decoded.length);

				// Is there a list of packets for this channel already?
				r = hmgeti(_packets, packet->channel);
				if (r < 0) {
					// No.  Create dynamic array for this channel.
					hmput(_packets, packet->channel, NULL);
					r = hmgeti(_packets, packet->channel);
				}
				// Add new packet to existing list.
				arrput(_packets[r].value, packet);
			}
		} else {
			// No bytes pending, yield to UI.
			taskYield();
		}

		// Send a ping to the server every 5 seconds, because, ping.
		if (__timerSecondTick) {
			pingTimeout++;
			if (pingTimeout > 5) {
				pingTimeout = 0;
				encoded.control    = PACKET_CONTROL_DAT;
				encoded.packetType = PACKET_TYPE_PING;
				encoded.channel    = 0;
				encoded.encrypt    = 0;
				packetEncode(__packetThreadData, &encoded, NULL, 0);  // Must encode each packet - no reusing encoded data.
				packetSend(__packetThreadData, &encoded);
			}
		}

	} while (!guiHasStopped() && _netRunning);

	// Free any unclaimed packets.
	while (hmlenu(_packets) > 0) {
		// Free any packets for this channel.
		while (arrlenu(_packets[0].value) > 0) {
			packetDecodeDataDestroy(&_packets[0].value[0]);
			arrdel(_packets[0].value, 0);
		}
		arrfree(_packets[0].value);
		hmdel(_packets, _packets[0].key);
	}
	hmfree(_packets);
}
