/*
cl_custom.c - downloading custom resources
Copyright (C) 2018 Uncle Mike

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.
*/

#include "common.h"
#include "client.h"
#include "net_encode.h"

qboolean CL_CheckFile( sizebuf_t *msg, resource_t *pResource )
{
	char	filepath[MAX_QPATH];

	switch( pResource->type )
	{
	case t_sound:
	case t_model:
		// built-in resources not needs to be downloaded
		if( pResource->szFileName[0] == '*' )
			return true;
		break;
	}

	// resource was missed on server
	if( pResource->nDownloadSize == -1 )
	{
		ClearBits( pResource->ucFlags, RES_FATALIFMISSING );
		return true;
	}

	if( pResource->type == t_sound )
		Q_strncpy( filepath, va( "%s%s", DEFAULT_SOUNDPATH, pResource->szFileName ), sizeof( filepath ));
	else Q_strncpy( filepath, pResource->szFileName, sizeof( filepath ));
 
	if( !COM_IsSafeFileToDownload( filepath ))
	{
		Con_Reportf( "refusing to download %s\n", filepath );
		return true;
	}

	if( !cl_allow_download.value )
	{
		Con_Reportf( "Download refused, cl_allow_download is 0\n" );
		return true;
	}

	if( cls.state == ca_active && !cl_download_ingame.value )
	{
		Con_Reportf( "In-game download refused...\n" );
		return true;
	}

	// don't request downloads from local client it's silly
	if( Host_IsLocalClient() || FS_FileExists( filepath, false ))
		return true;

	if( cls.demoplayback )
	{
		Con_Reportf( S_WARN "file %s missing during demo playback.\n", filepath );
		return true;
	}

	MSG_BeginClientCmd( msg, clc_stringcmd );
	MSG_WriteString( msg, va( "dlfile %s", filepath ));
	host.downloadcount++;

	return false;
}

void CL_AddToResourceList( resource_t *pResource, resource_t *pList )
{
	if( pResource->pPrev != NULL || pResource->pNext != NULL )
	{
		Con_Reportf( S_ERROR "Resource already linked\n" );
		return;
	}

	if( pList->pPrev == NULL || pList->pNext == NULL )
		Host_Error( "Resource list corrupted.\n" );

	pResource->pPrev = pList->pPrev;
	pResource->pNext = pList;
	pList->pPrev->pNext = pResource;
	pList->pPrev = pResource;
}

void CL_RemoveFromResourceList( resource_t *pResource )
{
	if( pResource->pPrev == NULL || pResource->pNext == NULL )
		Host_Error( "mislinked resource in CL_RemoveFromResourceList\n" );

	if ( pResource->pNext == pResource || pResource->pPrev == pResource )
		Host_Error( "attempt to free last entry in list.\n" );

	pResource->pPrev->pNext = pResource->pNext;
	pResource->pNext->pPrev = pResource->pPrev;
	pResource->pPrev = NULL;
	pResource->pNext = NULL;
}

void CL_MoveToOnHandList( resource_t *pResource )
{
	if( !pResource )
	{
		Con_Reportf( "Null resource passed to CL_MoveToOnHandList\n" );
		return;
	}

	CL_RemoveFromResourceList( pResource );
	CL_AddToResourceList( pResource, &cl.resourcesonhand );
}

void CL_ClearResourceList( resource_t *pList )
{
	resource_t	*p, *n;

	for( p = pList->pNext; p != pList && p; p = n )
	{
		n = p->pNext;

		CL_RemoveFromResourceList( p );
		Mem_Free( p );
	}

	pList->pPrev = pList;
	pList->pNext = pList;
}

void CL_ClearResourceLists( void )
{
	CL_ClearResourceList( &cl.resourcesneeded );
	CL_ClearResourceList( &cl.resourcesonhand );
}