#include <GearSrc/Net.h>

#include <GearSrc/Endian.h>
#include <GearSrc/Log.h>

#include <stb_ds.h>

enum states {
	Acknowledged = 0,
	WaitingAcknowledge
};

void GSNetStateCheckRead(GSNetState* state, GSNetSocket sock, GSNetPacket* packet, GSNetAddress* address) {
	if(packet->header.flag & GSNetPacketFlagUnsafe) {
		GSBinary binary;

		GSLog(state->engine, GSLogDebug, "%s: Received unsafe packet", state->name);

		binary.size = 508 - sizeof(packet->header);
		binary.data = malloc(binary.size);

		memcpy(binary.data, packet->data, binary.size);

		arrput(state->rx, binary);
	} else if(packet->header.flag & GSNetPacketFlagAcknowledge) {
		if(arrlen(state->tx) > 0 && state->txstate == WaitingAcknowledge && state->txindex == packet->header.index && state->txseq == packet->header.seq) {
			GSLog(state->engine, GSLogDebug, "%s: Received acknowledge packet %d:%d", state->name, state->txindex, state->txseq);

			state->txstate = Acknowledged;
			state->txseq++;
			if(state->txseq == (state->txtotal + 1)) {
				state->txseq = 0;
				state->txindex++;

				free(state->tx[0].data);
				arrdel(state->tx, 0);
			}
		}
	} else {
		GSNetPacket pkt;
		if(state->rxindex == packet->header.index && state->rxseq == packet->header.seq) {
			GSLog(state->engine, GSLogDebug, "%s: Received packet %d:%d", state->name, state->rxindex, state->rxseq);

			if(state->rxseq == 0) {
				state->rxtotal = GSEndianSwapU32BE(*(GSU32*)packet->data);

				state->buffer.data = malloc(state->rxtotal * (508 - sizeof(packet->header)));
				state->buffer.size = 0;
			} else {
				memcpy(((unsigned char*)state->buffer.data) + state->buffer.size, packet->data, packet->size);

				state->buffer.size += packet->size;
			}

			state->rxseq++;
			if(state->rxseq == (state->rxtotal + 1)) {
				arrput(state->rx, state->buffer);

				state->buffer.data = NULL;

				state->rxseq = 0;
				state->rxindex++;
			}
		}

		/* this is to prevent netcode from blowing up when
		 * acknowledge packet goes missing...
		 */
		pkt.header.flag |= GSNetPacketFlagAcknowledge;
		pkt.header.index = packet->header.index;
		pkt.header.seq	 = packet->header.seq;
		pkt.size	 = 0;

		GSNetPacketWrite(sock, &pkt, address);
	}
}

void GSNetStateCheckWrite(GSNetState* state, GSNetSocket sock, GSNetAddress* address) {
	int i;
	for(i = 0; i < arrlen(state->tx); i++) {
		if(state->tx[i].flag & GSNetPacketFlagUnsafe) {
			GSNetPacket pkt;

			pkt.header.flag	 = GSNetPacketFlagUnsafe;
			pkt.header.index = 0;
			pkt.header.seq	 = 0;
			pkt.size	 = state->tx[i].size;

			memcpy(pkt.data, state->tx[i].data, pkt.size);

			GSNetPacketWrite(sock, &pkt, address);

			free(state->tx[i].data);
			arrdel(state->tx, i);
			i--;

			GSLog(state->engine, GSLogDebug, "%s: Sent unsafe packet", state->name);
		}
	}

	if(arrlen(state->tx) > 0) {
		if(state->txstate == Acknowledged) {
			int	    n = state->tx[0].size;
			GSNetPacket pkt;

			if(state->txseq == 0) {
				GSU32 t;

				state->txtotal = 0;

				while(n > 0) {
					n -= 508 - sizeof(pkt.header);
					state->txtotal++;
				}

				t = GSEndianSwapU32BE(state->txtotal);

				memcpy(pkt.data, &t, 4);
				pkt.size = 4;
			} else {
				int m = 508 - sizeof(pkt.header);
				int n = m * (state->txseq - 1);

				pkt.size = (n > m) ? m : n;
				memcpy(pkt.data, ((unsigned char*)state->tx[0].data) + n, pkt.size);
			}
			pkt.header.flag	 = 0;
			pkt.header.index = state->txindex;
			pkt.header.seq	 = state->txseq;

			GSNetPacketWrite(sock, &pkt, address);

			state->txstate = WaitingAcknowledge;
			if(state->engine->param->get_tick != NULL) state->txtick = state->engine->param->get_tick();

			GSLog(state->engine, GSLogDebug, "%s: Sent packet %d:%d", state->name, state->txindex, state->txseq);
		} else if(state->engine->param->get_tick == NULL) {
		} else if((state->engine->param->get_tick() - state->txtick) >= GSNetAcknowledgeTimeout) {
			GSLog(state->engine, GSLogDebug, "%s: Lost packet %d:%d", state->name, state->txindex, state->txseq);

			state->txstate = Acknowledged;
		}
	}
}

void GSNetStateInit(GSNetState* state, GSEngine engine) {
	memset(state, 0, sizeof(*state));

	state->engine = engine;
}

void GSNetStateDeinit(GSNetState* state) {
	int i;
	for(i = 0; i < arrlen(state->rx); i++) {
		free(state->rx[i].data);
	}
	arrfree(state->rx);

	for(i = 0; i < arrlen(state->rx); i++) {
		free(state->tx[i].data);
	}
	arrfree(state->tx);

	if(state->buffer.data != NULL) free(state->buffer.data);
}

GSBool GSNetStateRead(GSNetState* state, void** data, int* size) {
	if(arrlen(state->tx) <= 0) return GSFalse;

	*data = state->rx[0].data;
	*size = state->rx[0].size;

	arrdel(state->rx, 0);

	return GSTrue;
}

void GSNetStateWrite(GSNetState* state, void* data, int size) {
	GSBinary buffer;

	buffer.flag = 0;
	buffer.size = size;
	buffer.data = malloc(buffer.size);
	memcpy(buffer.data, data, buffer.size);

	arrput(state->tx, buffer);
}

void GSNetStateWriteUnsafe(GSNetState* state, void* data, int size) {
	GSBinary buffer;

	buffer.flag = GSNetPacketFlagUnsafe;
	buffer.size = size;
	buffer.data = malloc(buffer.size);
	memcpy(buffer.data, data, buffer.size);

	arrput(state->tx, buffer);
}
