/*
 * 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 "database.h"
#include "settings.h"

#include "file.h"


static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data);
static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data);


void clientApiFileRequest(ClientThreadT *client, PacketDecodeDataT *data) {
	FileRequestsT      request      = 0;
	PacketEncodeDataT  encoded      = { 0 };
	char              *packetData   = NULL;
	uint16_t           length       = 0;

	// Must be logged in to do file operations.
	if (client->authenticated) {
		// Extract the request type.  We get more data later.
		packetContentUnpack(data->data, "i", &request);

		switch (request) {
			case FILE_REQUEST_CHECK:
				clientApiFileRequestCheck(client, data);
				break;

			case FILE_REQUEST_NEXT:
				clientApiFileRequestNext(client, data);
				break;

			default:
				logWrite("Got FILE_REQUEST_UNKNOWN [%d] %d\n\r", request, data->length);
				// No idea what they want.
				packetData = packetContentPack(&length, "i", FILE_RESPONSE_UNKNOWN);
				// Build packet.
				encoded.control    = PACKET_CONTROL_DAT;
				encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
				encoded.channel    = data->channel;
				encoded.encrypt    = 0;
				packetEncode(client->packetThreadData, &encoded, packetData, length);
				// Send it.
				packetSend(client->packetThreadData, &encoded);
				DEL(packetData);
				break;
		}

	} // authenticated
}


static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *data) {
	FileRequestsT      request       = 0;
	char              *path          = NULL;
	char              *sha256        = NULL;
	PacketEncodeDataT  encoded       = { 0 };
	char              *packetData    = NULL;
	uint16_t           length        = 0;
	char               shaInDB[128]  = { 0 };
	char               buffer[1024]  = { 0 };
	char               buffer2[2048] = { 0 };
	uint32_t           temp          = 0;

	logWrite("Got FILE_REQUEST_CHECK\n\r");

	// Is something still open?
	if (client->handle) {
		fclose(client->handle);
		client->handle = NULL;
	}

	// Extract the request.
	packetContentUnpack(data->data, "iss", &request, &sha256, &path);
	logWrite("[%s] [%s]\n\r", sha256, path);

	// Look up the entry and compare SHA256.
	dbFileSha256Get(path, shaInDB, FILE_VIRTUAL_PATH_MAX);
	if (strcasecmp(sha256, shaInDB) == 0) {
		logWrite("File is current.  Sending FILE_RESPONSE_OKAY.\n\r");
		// File is already current on client.
		temp               = FILE_RESPONSE_OKAY;
		packetData         = packetContentPack(&length, "i", temp);
		encoded.control    = PACKET_CONTROL_DAT;
		encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
		encoded.channel    = data->channel;
		encoded.encrypt    = 0;
		packetEncode(client->packetThreadData, &encoded, packetData, length);
		packetSend(client->packetThreadData, &encoded);
		DEL(packetData);
	} else {
		logWrite("File needs updated.\n\r");
		// Get real path.
		dbFileRealPathGet(path, buffer, FILE_VIRTUAL_PATH_MAX);
		snprintf(buffer2, 2048, "%s%s", __settingsFile, buffer);
		// Open file & get file size.
		client->handle = fopen(buffer2, "rb");
		if (!client->handle) {
			logWrite("Unable to open [%s]\n", buffer2);
			// ***TODO*** Handle error
			return;
		}
		fseek(client->handle, 0, SEEK_END);
		client->fileSize = ftell(client->handle);
		fseek(client->handle, 0, SEEK_SET);
		logWrite("Size is %d\n\r", client->fileSize);

		// Send file metadata to start transfer
		logWrite("Sending FILE_RESPONSE_SEND.\n\r");
		temp               = FILE_RESPONSE_SEND;
		packetData         = packetContentPack(&length, "iis", temp, client->fileSize, shaInDB);
		encoded.control    = PACKET_CONTROL_DAT;
		encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
		encoded.channel    = data->channel;
		encoded.encrypt    = 0;
		packetEncode(client->packetThreadData, &encoded, packetData, length);
		packetSend(client->packetThreadData, &encoded);
		DEL(packetData);

		// Start sending actual file data.
		clientApiFileRequestNext(client, data);
	}
}


static void clientApiFileRequestNext(ClientThreadT *client, PacketDecodeDataT *data) {
	uint32_t           temp         = 0;
	PacketEncodeDataT  encoded      = { 0 };
	uint16_t           length       = 0;
	char               packetData[PACKET_MAX];

	// Do we have an open file?
	if (client->handle) {
		logWrite("Sending FILE_RESPONSE_DATA.\n\r");
		// Add response type.
		temp = FILE_RESPONSE_DATA;
		memcpy(&packetData[0], &temp, sizeof(int32_t));

		// Add file data.
		// ***TODO*** We can't send quite a full packet for some reason.
		length = client->fileSize - ftell(client->handle);
		if (length > PACKET_MAX - 14) {
			// File is larger than a packet size.
			length = PACKET_MAX - 14; // 4 for integer of response type.  10 more because a full PACKET_MAX causes issues.
			fread(&packetData[4], length, 1, client->handle);
		} else {
			// File remains will fit in this packet.
			fread(&packetData[4], length, 1, client->handle);
			fclose(client->handle);
			client->handle = NULL;
		}
		logWrite("Sending %d bytes of file\n\r", length);

		// Build packet.
		encoded.control    = PACKET_CONTROL_DAT;
		encoded.packetType = PACKET_TYPE_FILE_RESPONSE;
		encoded.channel    = data->channel;
		encoded.encrypt    = 0;
		packetEncode(client->packetThreadData, &encoded, packetData, length + 4);
		// Send it.
		packetSend(client->packetThreadData, &encoded);
	}
}
