//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=======================================================================================//

#ifndef CL_DOWNLOADER_H
#define CL_DOWNLOADER_H
#ifdef _WIN32
#pragma once
#endif

//----------------------------------------------------------------------------------------

#include "platform.h"
#include "engine/http.h"
#include "interface.h"
#include "tier0/threadtools.h"

//----------------------------------------------------------------------------------------

struct RequestContext_t;
class CHttpDownloader;
class KeyValues;

//----------------------------------------------------------------------------------------

class IDownloadHandler
{
public:
	virtual void		OnConnecting( CHttpDownloader *pDownloader ) = 0;
	virtual void		OnFetch( CHttpDownloader *pDownloader ) = 0;

	// Called when the download is done successfully, errors out, or is aborted.
	// NOTE: pDownloader should NOT be deleted from within OnDownloadComplete().
	// pData contains the downloaded data for processing.
	virtual void		OnDownloadComplete( CHttpDownloader *pDownloader, const unsigned char *pData ) = 0;
};

//----------------------------------------------------------------------------------------

//
// Generic downloader class - downloads a single file on its own thread, and maintains
// state for that data.
//
// TODO: Derive from CBaseThinker and remove explicit calls to Think() - will make this
// class less bug-prone (easy to forget to call Think()).
//
class CHttpDownloader
{
public:
	// Pass in a callback 
	CHttpDownloader( IDownloadHandler *pHandler = NULL );
	~CHttpDownloader();

	//
	// Download the file at the given URL (HTTP/HTTPS support only)
	// pGamePath - Game path where we should put the file - can be NULL if we don't
	//    want to save to the file to disk
	// pUserData - Passed back to IDownloadHandler, if one has been set - can be NULL
	// pBytesDownloaded - If non-NULL, # of bytes downloaded written.
	// Returns true on success.
	//
	bool				BeginDownload( const char *pURL, const char *pGamePath = NULL,
									   void *pUserData = NULL, uint32 *pBytesDownloaded = NULL );

	//
	// Abort the download (if there is one), wait for the download to shutdown and
	// do cleanup.
	//
	void				AbortDownloadAndCleanup();

	inline bool			IsDone() const			{ return m_bDone; }	// Download done?
	inline bool			CanDelete() const		{ return m_pThreadState == NULL; }	// Can free?
	inline HTTPStatus_t	GetStatus() const		{ return m_nHttpStatus; }
	inline HTTPError_t	GetError() const		{ return m_nHttpError; }
	inline void			*GetUserData() const	{ return m_pUserData; }
	inline uint32		GetBytesDownloaded() const	{ return m_uBytesDownloaded; }
	inline uint32		GetSize() const			{ return m_uSize; }	// File size in bytes - NOTE: Not valid until the download is complete, aborted, or errored out
	inline const char	*GetURL() const			{ return m_szURL; }

	void				Think();

	KeyValues			*GetOgsRow( int nErrorCounter ) const;

	static const char	*GetHttpErrorToken( HTTPError_t nError );

private:
	bool				CleanupThreadIfDone();
	void				InvokeHandler();

	RequestContext_t	*m_pThreadState;
	float				m_flNextThinkTime;
	bool				m_bDone;
	HTTPError_t			m_nHttpError;
	HTTPStatus_t		m_nHttpStatus;
	uint32				m_uBytesDownloaded;
	uint32				*m_pBytesDownloaded;	// Passed into BeginDownload()
	uint32				m_uSize;
	IDownloadHandler	*m_pHandler;
	void				*m_pUserData;
	char				m_szURL[512];

	// Use this to make sure that AbortDownloadAndCleanup isn't executed simultaneously
	// by two threads. This was causing use-after-free crashes during shutdown.
	CThreadMutex		m_lock;
};

//----------------------------------------------------------------------------------------

#endif // CL_DOWNLOADER_H