/*
 * Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#define MODSERVER_REQ_LIST 100
#define MODSERVER_REQ_ITEM 101

#define ModServer_URI_Callback URI_Get_Callback

var int g_iModServerLoading;
var int g_iModServerReqCount;

int game_getpackageid(string pkgname);

void*
memrealloc(__variant *oldptr, int elementsize, int old_num, int new_num)
{
	void *n = memalloc(elementsize * new_num);

	if (!n) {
		print("^1memrealloc^7: Out of memory\n");
		return 0;
	}

	memcpy(n, oldptr, elementsize * old_num);
	memfree(oldptr);
	return n;
}

void
ModServer_Refresh(void)
{
	uri_get("http://www.frag-net.com/mods/_list.txt", MODSERVER_REQ_LIST);
	g_iModServerLoading = TRUE;
	g_iModServerReqCount = 0;
}

void
ModServer_ParseList(string data)
{
	int c = 0;
	int finalcount = 0;
	c = tokenize(data);

	for (int i = 0; i < c; i++) {
		int skip = 0;
		string gamedir = argv(i);

		for (int x = 0; x < gameinfo_count; x++) {
			/* skip mods we already have. */
			if (gamedir == games[x].gamedir) {
				skip = 1;
				break;
			}
		}

		if (skip) {
			print(sprintf("^2ModServer_ParseList^7: Skipping %s\n", gamedir));
			continue;
		}

		print(sprintf("^2ModServer_ParseList^7: Querying mod-data for %s\n", gamedir));
		uri_get(sprintf("http://www.frag-net.com/mods/%s.fmf",uri_escape(gamedir)), MODSERVER_REQ_ITEM);
		finalcount++;
	}

	/* only count the ones that'll actually be queried */
	g_iModServerReqCount = finalcount;

	/* URI_GET never happens, so we can't count them down */
	if (g_iModServerReqCount == 0) {
		g_iModServerLoading = FALSE;
	}
}

void
ModServer_ParseItem(string data)
{
	int id;
	int c = tokenize(data);

	/* new id */
	id = gameinfo_count;
	gameinfo_count++;
	games = memrealloc(games, sizeof(gameinfo_t), id, gameinfo_count);

	if (!games) {
		print("No more memory. Bye bye.\n");
		return;
	}

	/* set up defaults */
	games[id].url_info = "";
	games[id].url_dl = "";
	games[id].version = "1.0";
	games[id].size = 0;
	games[id].type = "";
	games[id].nomodels = 0;
	games[id].mpentity = "info_player_deathmatch";
	games[id].gamedll = "progs.dat";
	games[id].startmap = "c0a0";
	games[id].trainingmap = "t0a0";
	games[id].cldll = 1;
	games[id].hlversion = "1110";
	games[id].svonly = 0;
	games[id].installed = 0;
	games[id].pkgid = -1;

	for (int i = 0; i < c; i++) {
		switch(argv(i)) {
		case "gameinfo_game":
			games[id].game = argv(i+1);
			break;
		case "gameinfo_gamedir":
			games[id].gamedir = argv(i+1);
			break;
		case "gameinfo_fallback_dir":
			games[id].fallback_dir = argv(i+1);
			break;
		case "gameinfo_url_info":
			games[id].url_info = argv(i+1);
			break;
		case "gameinfo_url_dl":
			games[id].url_dl = argv(i+1);
			break;
		case "gameinfo_version":
			games[id].version = argv(i+1);
			break;
		case "gameinfo_size":
			games[id].size = stof(argv(i+1));
			break;
		case "gameinfo_svonly":
			games[id].svonly = stof(argv(i+1));
			break;
		case "gameinfo_cldll":
			games[id].cldll = stof(argv(i+1));
			break;
		case "gameinfo_type":
			games[id].type = argv(i+1);
			break;
		case "gameinfo_hlversion":
			games[id].hlversion = argv(i+1);
			break;
		case "gameinfo_nomodels":
			games[id].nomodels = stof(argv(i+1));
			break;
		case "gameinfo_mpentity":
			games[id].mpentity = argv(i+1);
			break;
		case "gameinfo_gamedll":
			games[id].gamedll = argv(i+1);
			break;
		case "gameinfo_startmap":
			games[id].startmap = argv(i+1);
			break;
		case "gameinfo_trainingmap":
			games[id].trainingmap = argv(i+1);
			break;
		case "gameinfo_pkgname":
			games[id].pkgname = argv(i+1);
			games[id].pkgid = game_getpackageid(games[id].pkgname);
			break;
		default:
			break;
		}
	}
}

/* Called as an eventual result of the uri_get builtin. */
void
ModServer_URI_Callback(float id, float code, string data, int resourcebytes)
{
	/* count our requests down */
	if (id == MODSERVER_REQ_ITEM) {
		g_iModServerReqCount--;

		/* free the loading screen */
		if (g_iModServerReqCount <= 0) {
			g_iModServerLoading = FALSE;
		}
	}

	/* unvailable */
	if (code && id == MODSERVER_REQ_LIST) {
		print("^1ModServer_URI_Callback^7: Unable to retrieve list.\n");
		g_iModServerLoading = FALSE;
		return;
	}
	if (code && id == MODSERVER_REQ_ITEM) {
		print("^1ModServer_URI_Callback^7: Unable to retrieve mod entry.\n");
		return;
	}

	switch (id) {
	case MODSERVER_REQ_LIST:
		ModServer_ParseList(data);
		break;
	case MODSERVER_REQ_ITEM:
		ModServer_ParseItem(data);
		break;
	default:
		print(sprintf("^1ModServer_URI_Callback^7: Unknown request id %d with code %d\n", id, code));
	}
}
