#include "quakedef.h"
#ifndef MINIMAL

#define ROOTDOWNLOADABLESSOURCE "http://127.0.0.1/downloadables.txt"
#define INSTALLEDFILES	"installed.lst"	//the file that resides in the quakedir (saying what's installed).

#define DPF_HAVEAVERSION	1	//any old version
#define DPF_WANTTOINSTALL	2	//user selected it
#define DPF_DISPLAYVERSION	4	//some sort of conflict, the package is listed twice, so show versions so the user knows what's old.
#define DPF_DELETEONUNINSTALL 8	//for previously installed packages, remove them from the list

extern char	com_basedir[];

char *downloadablelist[256] = {

	"http://127.0.0.1/downloadables.txt"

};	//note: these are allocated for the life of the exe
int numdownloadablelists = 1;

typedef struct package_s {
	char fullname[256];
	char *name;

	char src[256];
	char dest[64];
	unsigned int version;	//integral.
	int flags;
	struct package_s *next;
} package_t;

typedef struct {
	menucustom_t *list;
	char intermediatefilename[MAX_QPATH];
	int parsedsourcenum;

	int firstpackagenum;
	int highlightednum;
} dlmenu_t;

package_t *availablepackages;
int numpackages;

package_t *BuildPackageList(FILE *f, int flags)
{
	char line[1024];
	package_t *p;
	package_t *first = NULL;
	char *sl;

	do
	{
		fgets(line, sizeof(line)-1, f);
		while((sl=strchr(line, '\n')))
			*sl = '\0';
		while((sl=strchr(line, '\r')))
			*sl = '\0';
		Cmd_TokenizeString (line, false, false);
	} while (!feof(f) && !Cmd_Argc());

	if (strcmp(Cmd_Argv(0), "version"))
		return NULL;	//it's not the right format.

	if (atoi(Cmd_Argv(1)) != 0)
		return NULL;	//it's not the right version.

	while(!feof(f))
	{
		if (!fgets(line, sizeof(line)-1, f))
			break;
		while((sl=strchr(line, '\n')))
			*sl = '\0';
		while((sl=strchr(line, '\r')))
			*sl = '\0';
		Cmd_TokenizeString (line, false, false);
		if (Cmd_Argc())
		{
			if (Cmd_Argc() != 4 && Cmd_Argc() != 3)
			{
				if (!strcmp(Cmd_Argv(0), "sublist"))
				{
					int i;
					sl = Cmd_Argv(1);

					for (i = 0; i < sizeof(downloadablelist)/sizeof(downloadablelist[0])-1; i++)
					{
						if (!strcmp(downloadablelist[i], sl))
							break;
					}
					if (!downloadablelist[i])
					{
						downloadablelist[i] = BZ_Malloc(strlen(sl)+1);
						strcpy(downloadablelist[i], sl);
						i++;
					}
					continue;
				}

				Con_Printf("Package list is bad - %s\n", line);
				continue;	//but try the next line away
			}

			p = BZ_Malloc(sizeof(*p));

			Q_strncpyz(p->fullname, Cmd_Argv(0), sizeof(p->fullname));
			p->name = p->fullname;
			while((sl = strchr(p->name, '/')))
				p->name = sl+1;

			Q_strncpyz(p->src, Cmd_Argv(1), sizeof(p->src));
			Q_strncpyz(p->dest, Cmd_Argv(2), sizeof(p->dest));
			p->version = atoi(Cmd_Argv(3));
			p->flags = flags;

			p->next = first;
			first = p;
		}
	}
	fclose(f);

	return first;
}

qboolean ComparePackages(package_t **l, package_t *p)
{
	int v = strcmp((*l)->fullname, p->fullname);
	if (v < 0)
	{
		p->next = (*l);
		(*l) = p;
		return true;
	}
	else if (v == 0)
	{
		if (p->version == (*l)->version)
		if (!strcmp((*l)->dest, p->dest))
		{ /*package matches, free, don't add*/
			strcpy((*l)->src, p->src);	//use the source of the new package (existing packages are read FIRST)
			(*l)->flags |= p->flags;
			BZ_Free(p);
			return true;
		}

		p->flags |= DPF_DISPLAYVERSION;
		(*l)->flags |= DPF_DISPLAYVERSION;
	}
	return false;
}

void InsertPackage(package_t **l, package_t *p)
{
	package_t *lp;
	if (!*l)	//there IS no list.
	{
		*l = p;
		p->next = NULL;
		return;
	}
	if (ComparePackages(l, p))
		return;
	for (lp = *l; lp->next; lp=lp->next)
	{
		if (ComparePackages(&lp->next, p))
			return;
	}
	lp->next = p;
	p->next = NULL;
}
void ConcatPackageLists(package_t *l2)
{
	package_t *n;
	while(l2)
	{
		n = l2->next;
		InsertPackage(&availablepackages, l2);
		l2 = n;

		numpackages++;
	}
}

void M_Download_Draw (int x, int y, struct menucustom_s *c, struct menu_s *m)
{
	int pn;

	int lastpathlen = 0;
	char *lastpath="";

	package_t *p;
	dlmenu_t *info = m->data;

	if (!cls.downloadmethod && (info->parsedsourcenum==-1 || downloadablelist[info->parsedsourcenum]))
	{	//done downloading
		FILE *f;
		char basename[64];
		char *absolutename;

		if (info->parsedsourcenum>=0)
		{
			sprintf(basename, "../dlinfo_%i.inf", info->parsedsourcenum);
			absolutename = va("%s/%s", com_basedir, basename+3);

			f = fopen(absolutename, "rb");
			if (f)
			{
				ConcatPackageLists(BuildPackageList(f, 0));
				fclose(f);
				unlink(absolutename);
			}
		}
		info->parsedsourcenum++;

		if (downloadablelist[info->parsedsourcenum])
		{
			sprintf(basename, "../dlinfo_%i.inf", info->parsedsourcenum);
			if (!HTTP_CL_Get(downloadablelist[info->parsedsourcenum], basename, NULL))
				Con_Printf("Could not contact server\n");
		}
	}
	if (!availablepackages)
	{
		Draw_String(x+8, y+8, "Could not obtain a package list");
		return;
	}

	y+=8;
	Draw_Alt_String(x+4, y, "I H");
	(info->highlightednum==0?Draw_Alt_String:Draw_String)(x+40, y, "Apply changes");
	y+=8;

	for (pn = 1, p = availablepackages; p && pn < info->firstpackagenum ; p=p->next, pn++)
		;
	for (; p; p = p->next, y+=8, pn++)
	{
		if (lastpathlen != p->name - p->fullname || strncmp(p->fullname, lastpath, lastpathlen))
		{
			lastpathlen = p->name - p->fullname;
			lastpath = p->fullname;

			
			if (!lastpathlen)
				Draw_FunStringLen(x+40, y, "/", 1);
			else
				Draw_FunStringLen(x+40, y, p->fullname, lastpathlen);
			y+=8;
		}
		Draw_Character (x, y, 128);
		Draw_Character (x+8, y, 130);
		Draw_Character (x+16, y, 128);
		Draw_Character (x+24, y, 130);

		//if you want it
		if (p->flags&DPF_WANTTOINSTALL)
			Draw_Character (x+4, y, 131);
		else
			Draw_Character (x+4, y, 129);

		//if you have it already
		if (p->flags&DPF_HAVEAVERSION)
			Draw_Character (x+20, y, 131);
		else
			Draw_Character (x+20, y, 129);

		if (pn == info->highlightednum)
			Draw_Alt_String(x+40, y, p->name);
		else
			Draw_String(x+40, y, p->name);

		if (p->flags & DPF_DISPLAYVERSION)
		{
			Draw_String(x+40+strlen(p->name)*8, y, va(" (%i.%i)", p->version/1000, p->version%1000));
		}
	}
}
qboolean M_Download_Key (struct menucustom_s *c, struct menu_s *m, int key)
{
	int pn;
	package_t *p, *p2;
	dlmenu_t *info = m->data;

	switch (key)
	{
	case K_UPARROW:
		if (info->highlightednum>0)
			info->highlightednum--;
		return true;
	case K_DOWNARROW:	//cap range when drawing
		if (info->highlightednum < numpackages)
			info->highlightednum++;
		return true;
	case K_ENTER:
		if (!info->highlightednum)
		{	//do it
			//uninstall packages first
			package_t *last = NULL;

			for (p = availablepackages; p ; p=p->next)
			{
				if (!(p->flags&DPF_WANTTOINSTALL) && (p->flags&DPF_HAVEAVERSION))
				{	//if we don't want it but we have it anyway:
					char *fname = va("%s/%s", com_basedir, p->dest);
					unlink(fname);
					p->flags&=~DPF_HAVEAVERSION;	//FIXME: This is error prone.

					if (p->flags & DPF_DELETEONUNINSTALL)
					{
						last->next = p->next;
						BZ_Free(p);

						return M_Download_Key(c, m, key);	//I'm lazy.
					}
				}
				last = p;
			}

			for (p = availablepackages; p ; p=p->next)
			{
				if ((p->flags&DPF_WANTTOINSTALL) && !(p->flags&DPF_HAVEAVERSION))
				{	//if we want it and don't have it:
					if (HTTP_CL_Get(p->src, va("../%s", p->dest), NULL))
						p->flags|=DPF_HAVEAVERSION;	//FIXME: This is error prone.
				}
			}
		}
		else
		{
			for (pn = 1, p = availablepackages; p && pn < info->highlightednum ; p=p->next, pn++)
				;
			if (p)
			{
				p->flags = (p->flags&~DPF_WANTTOINSTALL) | DPF_WANTTOINSTALL - (p->flags&DPF_WANTTOINSTALL);

				if (p->flags&DPF_WANTTOINSTALL)
				{
					for (p2 = availablepackages; p2; p2 = p2->next)
					{
						if (p == p2)
							continue;
						if (!strcmp(p->dest, p2->dest))
							p2->flags &= ~DPF_WANTTOINSTALL;
					}
				}
			}
		}
		return true;
	}
	return false;
}

void Menu_DownloadStuff_f (void)
{
	menu_t *menu;
	dlmenu_t *info;

	key_dest = key_menu;
	m_state = m_complex;
	m_entersound = true;

	menu = M_CreateMenu(sizeof(dlmenu_t));
	info = menu->data;

	menu->selecteditem = (menuoption_t *)(info->list = MC_AddCustom(menu, 0, 32, NULL));
	info->list->draw = M_Download_Draw;
	info->list->key = M_Download_Key;

	info->parsedsourcenum = -1;

	MC_AddWhiteText(menu, 24, 8, "Downloads", false);
	MC_AddWhiteText(menu, 0, 16, "Probably buggy, press escape now and avoid this place!", false);
	MC_AddWhiteText(menu, 16, 24, "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37", false);

	{
		static qboolean loadedinstalled;
		char *fname = va("%s/%s", com_basedir, INSTALLEDFILES);
		FILE *f = loadedinstalled?NULL:fopen(fname, "rb");
		loadedinstalled = true;
		if (f)
		{
			ConcatPackageLists(BuildPackageList(f, DPF_DELETEONUNINSTALL|DPF_HAVEAVERSION|DPF_WANTTOINSTALL));
			fclose(f);
		}
	}
}
#endif