#include "quakedef.h"
#include "fs.h"

#if defined(FTE_TARGET_WEB)
#include "ftejslib.h"

#define FSWEB_OpenPath VFSOS_OpenPath
#define FSWEB_OpenTemp FS_OpenTemp

typedef struct {
	searchpathfuncs_t pub;
	int depth;
	void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle);
	char rootpath[1];
} webpath_t;
typedef struct {
	vfsfile_t funcs;
	qofs_t offset;
	int handle;
} vfswebfile_t;
static int QDECL VFSWEB_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
	vfswebfile_t *intfile = (vfswebfile_t*)file;
	memset(buffer, 'e', bytestoread);
	int len = emscriptenfte_buf_read(intfile->handle, intfile->offset, buffer, bytestoread);
	intfile->offset += len;
	return len;
}
static int QDECL VFSWEB_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread)
{
	vfswebfile_t *intfile = (vfswebfile_t*)file;
	int len = emscriptenfte_buf_write(intfile->handle, intfile->offset, buffer, bytestoread);
	intfile->offset += len;
	return len;
}
static qboolean QDECL VFSWEB_Seek (struct vfsfile_s *file, qofs_t pos)
{
	vfswebfile_t *intfile = (vfswebfile_t*)file;
//	if (pos < 0)
//		return 0;
	intfile->offset = pos;
	return true;
}
static qofs_t QDECL VFSWEB_Tell (struct vfsfile_s *file)
{
	vfswebfile_t *intfile = (vfswebfile_t*)file;
	return intfile->offset;
}
static void QDECL VFSWEB_Flush(struct vfsfile_s *file)
{
//	vfswebfile_t *intfile = (vfswebfile_t*)file;
}
static qofs_t QDECL VFSWEB_GetSize (struct vfsfile_s *file)
{
	vfswebfile_t *intfile = (vfswebfile_t*)file;
	unsigned long l;
	l = emscriptenfte_buf_getsize(intfile->handle);
	return l;
}
static qboolean QDECL VFSWEB_Close(vfsfile_t *file)
{
	vfswebfile_t *intfile = (vfswebfile_t*)file;
	emscriptenfte_buf_release(intfile->handle);
	Z_Free(file);
	return true;
}
static qboolean QDECL VFSWEB_ClosePersist(vfsfile_t *file)
{
	vfswebfile_t *intfile = (vfswebfile_t*)file;
#ifdef _DEBUG
	Con_DPrintf("Persisting file %s\n", file->dbgname);
#endif
	emscriptenfte_buf_pushtolocalstore(intfile->handle);
	return VFSWEB_Close(file);
}

vfsfile_t *FSWEB_OpenTempHandle(int f)
{
	vfswebfile_t *file;

	if (f == -1)
	{
		Con_Printf("FSWEB_OpenTemp failed\n");
		return NULL;
	}

	file = Z_Malloc(sizeof(vfswebfile_t));
	file->funcs.Close = VFSWEB_Close;
#ifdef _DEBUG
	Q_strncpyz(file->funcs.dbgname, "FSWEB_OpenTemp", sizeof(file->funcs.dbgname));
#endif
	file->funcs.ReadBytes = VFSWEB_ReadBytes;
	file->funcs.WriteBytes = VFSWEB_WriteBytes;
	file->funcs.Seek = VFSWEB_Seek;
	file->funcs.Tell = VFSWEB_Tell;
	file->funcs.GetLen = VFSWEB_GetSize;
	file->funcs.Flush = VFSWEB_Flush;
	file->handle = f;

	return &file->funcs;
}

vfsfile_t *FSWEB_OpenTemp(void)
{
	return FSWEB_OpenTempHandle(emscriptenfte_buf_create());
}

vfsfile_t *VFSWEB_Open(const char *osname, const char *mode, qboolean *needsflush)
{
	int f;
	vfswebfile_t *file;
	//qboolean read = !!strchr(mode, 'r');
	qboolean write = !!strchr(mode, 'w');
	qboolean update = !!strchr(mode, '+');
	qboolean append = !!strchr(mode, 'a');
	qboolean persist = !!strchr(mode, 'p');

	if (needsflush)
		*needsflush = false;
	f = emscriptenfte_buf_open(osname, (write && !update)?2:(write||append));
	if (f == -1)
		return NULL;

	if (write || append)
	{
		if (needsflush)
			*needsflush = true;
	}

	file = Z_Malloc(sizeof(vfswebfile_t));
#ifdef _DEBUG
	Q_strncpyz(file->funcs.dbgname, osname, sizeof(file->funcs.dbgname));
#endif
	file->funcs.ReadBytes = VFSWEB_ReadBytes;
	file->funcs.WriteBytes = VFSWEB_WriteBytes;
	file->funcs.Seek = VFSWEB_Seek;
	file->funcs.Tell = VFSWEB_Tell;
	file->funcs.GetLen = VFSWEB_GetSize;
	if (persist && (write || append))
		file->funcs.Close = VFSWEB_ClosePersist;
	else
		file->funcs.Close = VFSWEB_Close;
	file->funcs.Flush = VFSWEB_Flush;
	file->handle = f;

	if (append)
		file->offset = VFSWEB_GetSize(&file->funcs);

	return &file->funcs;
}

vfsfile_t *VFSOS_Open(const char *osname, const char *mode)
{
	vfsfile_t *f;
	qboolean needsflush;
	f = VFSWEB_Open(osname, mode, &needsflush);
	if (needsflush)
		FS_FlushFSHashFull();
	return f;
}

static vfsfile_t *QDECL FSWEB_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)
{
	vfsfile_t *f;
	webpath_t *sp = (void*)handle;
	qboolean needsflush;

	f = VFSWEB_Open(loc->rawname, mode, &needsflush);
	if (needsflush && sp->AddFileHash)
		sp->AddFileHash(sp->depth, loc->rawname, NULL, sp);
	return f;
}

static void QDECL FSWEB_ClosePath(searchpathfuncs_t *handle)
{
	Z_Free(handle);
}
static qboolean QDECL FSWEB_PollChanges(searchpathfuncs_t *handle)
{
//	webpath_t *np = handle;
	return true;	//can't verify that or not, so we have to assume the worst
}
static int QDECL FSWEB_RebuildFSHash(const char *filename, qofs_t filesize, time_t mtime, void *data, searchpathfuncs_t *spath)
{
	webpath_t *sp = (void*)spath;
	void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle) = data;
	if (filename[strlen(filename)-1] == '/')
	{	//this is actually a directory

		char childpath[256];
		Q_snprintfz(childpath, sizeof(childpath), "%s*", filename);
		Sys_EnumerateFiles(sp->rootpath, childpath, FSWEB_RebuildFSHash, data, spath);
		return true;
	}
	AddFileHash(sp->depth, filename, NULL, sp);
	return true;
}
static void QDECL FSWEB_BuildHash(searchpathfuncs_t *handle, int depth, void (QDECL *AddFileHash)(int depth, const char *fname, fsbucket_t *filehandle, void *pathhandle))
{
	webpath_t *sp = (void*)handle;
	sp->depth = depth;
	sp->AddFileHash = AddFileHash;
	Sys_EnumerateFiles(sp->rootpath, "*", FSWEB_RebuildFSHash, AddFileHash, handle);
}
static qboolean QDECL FSWEB_FLocate(searchpathfuncs_t *handle, flocation_t *loc, const char *filename, void *hashedresult)
{
	webpath_t *sp = (void*)handle;
	int len;
	char netpath[MAX_OSPATH];

	if (hashedresult && (void *)hashedresult != handle)
		return false;

/*
	if (!static_registered)
	{	// if not a registered version, don't ever go beyond base
		if ( strchr (filename, '/') || strchr (filename,'\\'))
			continue;
	}
*/

// check a file in the directory tree
	snprintf (netpath, sizeof(netpath)-1, "%s/%s", sp->rootpath, filename);

	{
		vfsfile_t *f = VFSWEB_Open(netpath, "rb", NULL);
		if (!f)
			return false;
		len = VFS_GETLEN(f);
		VFS_CLOSE(f);
	}
	if (loc)
	{
		loc->len = len;
		loc->offset = 0;
		loc->fhandle = NULL;
		Q_strncpyz(loc->rawname, netpath, sizeof(loc->rawname));
	}
	return true;
}
static void QDECL FSWEB_ReadFile(searchpathfuncs_t *handle, flocation_t *loc, char *buffer)
{
	vfsfile_t *f;
	size_t result;

	f = VFSWEB_Open(loc->rawname, "rb", NULL);
	if (!f)	//err...
		return;
	VFS_SEEK(f, loc->offset);
	result = VFS_READ(f, buffer, loc->len); // do soemthing with result

	if (result != loc->len)
		Con_Printf("FSWEB_ReadFile() fread: Filename: %s, expected %u, result was %u \n",loc->rawname,(unsigned int)loc->len,(unsigned int)result);

	VFS_CLOSE(f);
}
static int QDECL FSWEB_EnumerateFiles (searchpathfuncs_t *handle, const char *match, int (QDECL *func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *spath), void *parm)
{
	webpath_t *sp = (webpath_t*)handle;
	return Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle);
}


searchpathfuncs_t *QDECL FSWEB_OpenPath(vfsfile_t *mustbenull,  searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)
{
	webpath_t *np;
	int dlen = strlen(desc);
	if (mustbenull)
		return NULL;
	np = Z_Malloc(sizeof(*np) + dlen);
	if (np)
	{
		np->depth = 0;
		memcpy(np->rootpath, desc, dlen+1);
	}

	np->pub.fsver			= FSVER;
	np->pub.ClosePath		= FSWEB_ClosePath;
	np->pub.BuildHash		= FSWEB_BuildHash;
	np->pub.FindFile		= FSWEB_FLocate;
	np->pub.ReadFile		= FSWEB_ReadFile;
	np->pub.EnumerateFiles	= FSWEB_EnumerateFiles;
	np->pub.OpenVFS			= FSWEB_OpenVFS;
	np->pub.PollChanges		= FSWEB_PollChanges;
	return &np->pub;
}

#endif

