#ifndef IFILESYSTEM_H
#define IFILESYSTEM_H

#include <tier0/annotations.h>
#include <tier0/threadtools.h>
#include <tier1/utlvector.h>
#include <tier1/utlstring.h>
#include <public/ipackedstore.h>
#include <public/appframework/iappsystem.h>

typedef void* FileHandle_t;
typedef void* FileNameHandle_t; // !TODO: Check if this is 4 or 8 bytes (model_t was 4 bytes in mem).
typedef void* FileCacheHandle_t;
typedef void* FileFindHandle_t;

#define FILESYSTEM_INVALID_HANDLE	( FileHandle_t )nullptr

#define GAMEINFOPATH_TOKEN		"|gameinfo_path|"
#define BASESOURCEPATHS_TOKEN	"|all_source_engine_paths|"

//---------------------------------------------------------------------------------
// Purpose: Forward declarations
//---------------------------------------------------------------------------------
class KeyValues;
class CUtlBuffer;

//-----------------------------------------------------------------------------
// Structures used by the interface
//-----------------------------------------------------------------------------

struct FileSystemStatistics
{
	CInterlockedUInt	nReads,
		nWrites,
		nBytesRead,
		nBytesWritten,
		nSeeks;
};

//-----------------------------------------------------------------------------
// File system allocation functions. Client must free on failure
//-----------------------------------------------------------------------------
typedef void* (*FSAllocFunc_t)(const char* pszFilename, unsigned nBytes);


//-----------------------------------------------------------------------------
// Used to display dirty disk error functions
//-----------------------------------------------------------------------------
typedef void (*FSDirtyDiskReportFunc_t)();


//-----------------------------------------------------------------------------
// Used for FileSystem logging
//-----------------------------------------------------------------------------
typedef void (*FileSystemLoggingFunc_t)(const char* fileName, const char* accessType);


//-----------------------------------------------------------------------------
// Asynchronous support types
//-----------------------------------------------------------------------------
DECLARE_POINTER_HANDLE(FSAsyncControl_t);
DECLARE_POINTER_HANDLE(FSAsyncFile_t);
const FSAsyncFile_t FS_INVALID_ASYNC_FILE = (FSAsyncFile_t)(0x0000ffff);

enum FilesystemMountRetval_t
{
	FILESYSTEM_MOUNT_OK = 0,
	FILESYSTEM_MOUNT_FAILED,
};

// search path filtering
enum PathTypeFilter_t
{
	FILTER_NONE               = 0,	// no filtering, all search path types match
	FILTER_CULLPACK           = 1,	// pack based search paths are culled (maps and zips)
	FILTER_CULLNONPACK        = 2,	// non-pack based search paths are culled
	FILTER_CULLLOCALIZED      = 3,	// Ignore localized paths, assumes CULLNONPACK
	FILTER_CULLLOCALIZED_ANY  = 4,	// Ignore any localized paths
};

// search path querying (bit flags)
enum
{
	PATH_IS_NORMAL      = 0x00, // normal path, not pack based
	PATH_IS_PACKFILE    = 0x01, // path is a pack file
	PATH_IS_MAPPACKFILE = 0x02, // path is a map pack file
	PATH_IS_DVDDEV      = 0x04, // path is the dvddev cache
};
typedef uint32 PathTypeQuery_t;

#define IS_PACKFILE( n ) ( n & ( PATH_IS_PACKFILE | PATH_IS_MAPPACKFILE ) )
#define IS_DVDDEV( n )   ( n & PATH_IS_DVDDEV )

enum class SearchPathAdd_t : int
{
	PATH_ADD_TO_HEAD,         // First path searched
	PATH_ADD_TO_TAIL,         // Last path searched
	PATH_ADD_TO_TAIL_ATINDEX, // First path searched
};

enum class FileWarningLevel_t : int
{
	FILESYSTEM_WARNING = -1,                        // A problem!
	FILESYSTEM_WARNING_QUIET = 0,                   // Don't print anything
	FILESYSTEM_WARNING_REPORTUNCLOSED,              // On shutdown, report names of files left unclosed
	FILESYSTEM_WARNING_REPORTUSAGE,                 // Report number of times a file was opened, closed
	FILESYSTEM_WARNING_REPORTALLACCESSES,           // Report all open/close events to console ( !slow! )
	FILESYSTEM_WARNING_REPORTALLACCESSES_READ,      // Report all open/close/read events to the console ( !slower! )
	FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, // Report all open/close/read/write events to the console ( !slower! )
	FILESYSTEM_WARNING_REPORTALLACCESSES_ASYNC      // Report all open/close/read/write events and all async I/O file events to the console ( !slower(est)! )
};

#define FILESYSTEM_MAX_SEARCH_PATHS 128

enum FileMode_t
{
	FM_BINARY,
	FM_TEXT
};

enum FileType_t
{
	FT_NORMAL,
	FT_PACK_BINARY,
	FT_PACK_TEXT,
	FT_MEMORY_BINARY,
	FT_MEMORY_TEXT
};

enum FileSystemSeek_t
{
	FILESYSTEM_SEEK_HEAD = SEEK_SET,
	FILESYSTEM_SEEK_CURRENT = SEEK_CUR,
	FILESYSTEM_SEEK_TAIL = SEEK_END,
};

enum
{
	FILESYSTEM_INVALID_FIND_HANDLE = -1
};

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
abstract_class IBaseFileSystem
{
public:
	virtual int				Read(void* pOutput, int size, FileHandle_t file) = 0;
	virtual int				Write(void const* pInput, int size, FileHandle_t file) = 0;

	// if pathID is NULL, all paths will be searched for the file
	virtual FileHandle_t	Open(const char* pFileName, const char* pOptions, const char* pPathID = 0, int64_t unknown = 0) = 0;
	virtual void			Close(FileHandle_t file) = 0;

	virtual void			Seek(FileHandle_t file, int pos, FileSystemSeek_t seekType) = 0;
	virtual unsigned int	Tell(FileHandle_t file) = 0;
	virtual unsigned int	FSize(const char* pFileName, const char* pPathID = 0) = 0; // Gets optimized away if it isn't named differently or used.
	virtual unsigned int	Size(FileHandle_t file) = 0;

	virtual void			Flush(FileHandle_t file) = 0;
	virtual bool			Precache(const char* pFileName, const char* pPathID = 0) = 0;

	virtual bool			FileExists(const char* pFileName, const char* pPathID = 0) = 0;
	virtual bool			IsFileWritable(char const* pFileName, const char* pPathID = 0) = 0;
	virtual bool			SetFileWritable(char const* pFileName, bool writable, const char* pPathID = 0) = 0;

	virtual long			GetFileTime(const char* pFileName, const char* pPathID = 0) = 0;

	//--------------------------------------------------------
	// Reads/writes files to utlbuffers. Use this for optimal read performance when doing open/read/close.
	//--------------------------------------------------------
	virtual bool			ReadFile(const char* pFileName, const char* pPath, CUtlBuffer& buf, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL) = 0;
	virtual bool			WriteFile(const char* pFileName, const char* pPath, CUtlBuffer& buf) = 0;
	virtual bool			UnzipFile(const char* pFileName, const char* pPath, const char* pDestination) = 0;
};

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
abstract_class IFileSystem : public IAppSystem, public IBaseFileSystem
{
public:
	//--------------------------------------------------------
	virtual bool			IsSteam() const = 0;

	// Supplying an extra app id will mount this app in addition 
	// to the one specified in the environment variable "steamappid"
	// 
	// If nExtraAppId is < -1, then it will mount that app ID only.
	// (Was needed by the dedicated server b/c the "SteamAppId" env var only gets passed to steam.dll
	// at load time, so the dedicated couldn't pass it in that way).
	virtual	FilesystemMountRetval_t MountSteamContent(int nExtraAppId = -1) = 0;

#if !defined(GAMEDLL_S0) && !defined(GAMEDLL_S1) && !defined (GAMEDLL_S2)
	virtual bool InitFeatureFlags() = 0;
	virtual bool InitFeatureFlags(const char* pszFlagSetFile) = 0;
#endif // !GAMEDLL_S0 || !GAMEDLL_S1 || GAMEDLL_S2

	virtual void AddSearchPath(const char* pPath, const char* pPathID, SearchPathAdd_t addType) = 0;
	virtual bool RemoveSearchPath(const char* pPath, const char* pPathID) = 0;
	virtual void			RemoveAllSearchPaths(void) = 0;              // Remove all search paths (including write path?)
	virtual void			RemoveSearchPaths(const char* szPathID) = 0; // Remove search paths associated with a given pathID
	// This is for optimization. If you mark a path ID as "by request only", then files inside it
	// will only be accessed if the path ID is specifically requested. Otherwise, it will be ignored.
	// If there are currently no search paths with the specified path ID, then it will still
	// remember it in case you add search paths with this path ID.
	virtual void			MarkPathIDByRequestOnly(const char* pPathID, bool bRequestOnly) = 0;
	// converts a partial path into a full path
	virtual const char* RelativePathToFullPath(const char* pFileName, const char* pPathID, char* pLocalPath, int localPathBufferSize, PathTypeFilter_t pathFilter = FILTER_NONE, PathTypeQuery_t* pPathType = NULL) = 0;
#if IsGameConsole()
	// Given a relative path, gets the PACK file that contained this file and its offset and size. Can be used to prefetch a file to a HDD for caching reason.
	virtual bool            GetPackFileInfoFromRelativePath(const char* pFileName, const char* pPathID, char* pPackPath, int nPackPathBufferSize, int64& nPosition, int64& nLength) = 0;
#endif
	// Returns the search path, each path is separated by ;s. Returns the length of the string returned
	virtual int				GetSearchPath(const char* pathID, bool bGetPackFiles, char* pPath, int nMaxLen) = 0;
	virtual bool			AddPackFile(const char* fullpath, const char* pathID) = 0; 	// interface for custom pack files > 4Gb

	//--------------------------------------------------------
	// File manipulation operations
	//--------------------------------------------------------
	virtual void			RemoveFile(char const* pRelativePath, const char* pathID = 0) = 0;                  // Deletes a file (on the WritePath)
	virtual bool			RenameFile(char const* pOldPath, char const* pNewPath, const char* pathID = 0) = 0; // Renames a file (on the WritePath)
	virtual void			CreateDirHierarchy(const char* path, const char* pathID = 0) = 0;                   // create a local directory structure
	virtual bool			IsDirectory(const char* pFileName, const char* pathID = 0) = 0;                     // File I/O and info
	virtual void			FileTimeToString(char* pStrip, int maxCharsIncludingTerminator, long fileTime) = 0;

	//--------------------------------------------------------
	// Open file operations
	//--------------------------------------------------------

	virtual void			SetBufferSize(FileHandle_t file, unsigned nBytes) = 0;
	virtual bool			IsOk(FileHandle_t file) = 0;
	virtual bool			EndOfFile(FileHandle_t file) = 0;
	virtual char*			ReadLine(char* pOutput, int maxChars, FileHandle_t file) = 0;
#if ! defined(SWIG)
	// Don't let SWIG see the PRINTF_FORMAT_STRING attribute or it will complain.
	virtual int				FPrintf(FileHandle_t file, PRINTF_FORMAT_STRING const char* pFormat, ...) FMTFUNCTION(3, 4) = 0;
#else
	virtual int				FPrintf(FileHandle_t file, const char* pFormat, ...) FMTFUNCTION(3, 4) = 0;
#endif

	//--------------------------------------------------------
	// Dynamic library operations
	//--------------------------------------------------------
	// load/unload modules
	virtual CSysModule* LoadModule(const char* pFileName, const char* pPathID = 0, bool bValidatedDllOnly = true) = 0;
	virtual void        UnloadModule(CSysModule* pModule) = 0;

	//--------------------------------------------------------
	// File searching operations
	//--------------------------------------------------------
	// FindFirst/FindNext. Also see FindFirstEx.
	virtual const char* FindFirst(const char* pWildCard, FileFindHandle_t* pHandle) = 0;
	virtual const char* FindNext(FileFindHandle_t handle) = 0;
	virtual bool        FindIsDirectory(FileFindHandle_t handle) = 0;
	virtual void        FindClose(FileFindHandle_t handle) = 0;

	// Same as FindFirst, but you can filter by path ID, which can make it faster.
	virtual const char* FindFirstEx(
		const char* pWildCard,
		const char* pPathID,
		FileFindHandle_t* pHandle
	) = 0;

	// Searches for a file in all paths and results absolute path names for the file, works in pack files (zip and vpk) too
	// Lets you search for something like sound/sound.cache and get a list of every sound cache
	virtual void			FindFileAbsoluteList(CUtlVector<CUtlString>& outAbsolutePathNames, const char* pWildCard, const char* pPathID) = 0;

	//--------------------------------------------------------
	// File name and directory operations
	//--------------------------------------------------------

	// FIXME: This method is obsolete! Use RelativePathToFullPath instead!
	// converts a partial path into a full path
	virtual const char* GetLocalPath(const char* pFileName, char* pLocalPath, int localPathBufferSize) = 0;

	// Returns true on success ( based on current list of search paths, otherwise false if 
	//  it can't be resolved )
	virtual bool			FullPathToRelativePath(const char* pFullpath, char* pRelative, int maxlen) = 0;

	// Gets the current working directory
	virtual bool			GetCurrentDirectory(char* pDirectory, int maxlen) = 0;

	//--------------------------------------------------------
	// Filename dictionary operations
	//--------------------------------------------------------

	virtual FileNameHandle_t	FindOrAddFileName(char const* pFileName) = 0;
	virtual bool				String(const FileNameHandle_t& handle, char* buf, int buflen) = 0;

	//--------------------------------------------------------
	// Asynchronous file operations
	//--------------------------------------------------------

//--------------- [ !!! AMOS: !!! ALL ASYNC METHODS ARE UNIMPLEMENTED !!! PURECALL !!! ] ---------------//
	//------------------------------------
	// Global operations
	//------------------------------------
	virtual void PureCall0() = 0;
	virtual void PureCall1() = 0;
	virtual void PureCall2() = 0;
	virtual void PureCall3() = 0;
	virtual void PureCall4() = 0;
	virtual void PureCall5() = 0;

	//--------------------------------------------------------
	// Debugging operations
	//--------------------------------------------------------

	// Dump to printf/OutputDebugString the list of files that have not been closed
	virtual void			PrintOpenedFiles(void) = 0;
	virtual void			PrintSearchPaths(void) = 0;

	// output
	virtual void			SetWarningFunc(void (*pfnWarning)(const char* fmt, ...)) = 0;
	virtual void			SetWarningLevel(FileWarningLevel_t level) = 0;
	virtual void			AddLoggingFunc(void (*pfnLogFunc)(const char* fileName, const char* accessType)) = 0;
	virtual void			RemoveLoggingFunc(FileSystemLoggingFunc_t logFunc) = 0;

	virtual __int64 __fastcall sub_14038C240(__int64 a1) = 0;
	virtual __int64 __fastcall sub_14038C380(__int64 a1) = 0;
	virtual __int64 __fastcall sub_14038C400(__int64 a1, __int64 a2) = 0;

	// Returns the file system statistics retrieved by the implementation.  Returns NULL if not supported.
	virtual const FileSystemStatistics* GetFilesystemStatistics() = 0;

	//--------------------------------------------------------
	// Start of new functions after Lost Coast release (7/05)
	//--------------------------------------------------------

	virtual FileHandle_t	OpenEx(const char* pFileName, const char* pOptions, unsigned flags = 0, const char* pathID = 0, char** ppszResolvedFilename = NULL) = 0;

	// Extended version of read provides more context to allow for more optimal reading
	virtual int				ReadEx(void* pOutput, int sizeDest, int size, FileHandle_t file) = 0;
	virtual int				ReadFileEx(const char* pFileName, const char* pPath, void** ppBuf, bool bNullTerminate = false, bool bOptimalAlloc = false, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL) = 0;

	virtual FileNameHandle_t	FindFileName(char const* pFileName) = 0;

#if defined( TRACK_BLOCKING_IO )
	virtual void			EnableBlockingFileAccessTracking(bool state) = 0;
	virtual bool			IsBlockingFileAccessEnabled() const = 0;

	virtual IBlockingFileItemList* RetrieveBlockingFileAccessInfo() = 0;
#endif

	virtual void SetupPreloadData() = 0;
	virtual void DiscardPreloadData() = 0;

	// Fixme, we could do these via a string embedded into the compiled data, etc...
	enum KeyValuesPreloadType_t
	{
		TYPE_VMT,
		TYPE_SOUNDEMITTER,
		TYPE_SOUNDSCAPE,
		TYPE_SOUNDOPERATORS,
		TYPE_LEVELSETTINGS,
		TYPE_COMMON,
		NUM_PRELOAD_TYPES
	};

	// If the "PreloadedData" hasn't been purged, then this'll try and instance the KeyValues using the fast path of compiled keyvalues loaded during startup.
	// Otherwise, it'll just fall through to the regular KeyValues loading routines
	virtual KeyValues* LoadKeyValues(KeyValuesPreloadType_t type, char const* filename, char const* pPathID = 0) = 0;
	virtual bool		LoadKeyValues(KeyValues& head, KeyValuesPreloadType_t type, char const* filename, char const* pPathID = 0) = 0;

	virtual bool			GetFileTypeForFullPath(char const* pFullPath, wchar_t* buf, size_t bufSizeInBytes) = 0;

	//--------------------------------------------------------
	//--------------------------------------------------------
	virtual bool		ReadToBuffer(FileHandle_t hFile, CUtlBuffer& buf, int nMaxBytes = 0, FSAllocFunc_t pfnAlloc = NULL) = 0;

	//--------------------------------------------------------
	// Optimal IO operations
	//--------------------------------------------------------
	virtual bool GetOptimalIOConstraints(FileHandle_t hFile, unsigned* pOffsetAlign, unsigned* pSizeAlign, unsigned* pBufferAlign) = 0;
	virtual void* AllocOptimalReadBuffer(FileHandle_t hFile, unsigned nSize = 0, unsigned nOffset = 0) = 0;
	virtual void FreeOptimalReadBuffer(void*) = 0;


	virtual bool __fastcall sub_140383E00(__int64 a2) = 0;
	virtual bool sub_1403836A0() = 0;
	virtual __int64 __fastcall sub_140384310(int a1) = 0;
	virtual __int64 __fastcall CheckVPKMode(int nMode) = 0; // Checks if the VPK mode equals the mode input.


	//--------------------------------------------------------
	// Cache/VPK operations
	//--------------------------------------------------------
	virtual bool ReadFromCache(const char* pPath, void* pResult) = 0;

	virtual bool __fastcall sub_14037FFA0(__int64 a1, unsigned int a2, __int64 a3) = 0;

	virtual void SetVPKCacheModeClient() = 0; // g_nVPKCacheMode = 1;
	virtual void SetVPKCacheModeServer() = 0; // g_nVPKCacheMode = 2;
	virtual bool IsVPKCacheEnabled() = 0;     // g_nVPKCacheMode != 0;

	virtual __int64 __fastcall PrecacheTaskItem(__int64 a1) = 0;

	virtual void ResetItemCacheSize(int edx) = 0;
	virtual void __fastcall sub_140380100(__int64 a1) = 0;
	virtual void __fastcall sub_140380230(char a2) = 0;
	virtual void* __fastcall sub_1403801F0(const void* a1, unsigned int a2) = 0;
	virtual void __fastcall sub_140380220(__int64 a1) = 0;
	virtual bool ResetItemCache() = 0;
	virtual char __fastcall sub_1403836D0(int a1, char* a2, unsigned int a3) = 0;
	virtual __int64 __fastcall sub_140383840(unsigned int a1, __int64 a2, char* a3, unsigned int BufferCount) = 0;
	virtual const char** __fastcall sub_140383760(unsigned int a1) = 0;
	virtual __int64 __fastcall sub_140383A20(const char* a1) = 0;

	virtual VPKData_t* MountVPKFile(const char* pVpkPath) = 0;
	virtual const char* UnmountVPKFile(const char* pBasename) = 0;

	virtual void __fastcall sub_140383370() = 0;
	virtual void __fastcall sub_140383560() = 0;

	virtual unsigned __int64 __fastcall PnpCtxRegQueryInfoKey(__int64 a1, char* a2, unsigned int* a3, unsigned __int64 a4, unsigned __int64 a5, void(__fastcall* a6)(unsigned __int64)) = 0;
	virtual char* sub_1403842B0() = 0;
	virtual __int64 __fastcall sub_1403842C0(__int64 a1, unsigned int a2, __int64 a3) = 0;

	virtual char __fastcall LoadMainVPK(const char* pszVPKFile) = 0;

	virtual __int64 sub_140380080() = 0;
	virtual char __fastcall sub_14038B530(const char* a1, unsigned __int8* a2, char* a3, __int64 Count) = 0;
	virtual __int64 __fastcall sub_14038C830(unsigned __int16* a1) = 0;
	virtual __int64 __fastcall sub_140388360(char* a1, __int64 a2) = 0;
	virtual __int64 __fastcall sub_140384C60(char* a1, unsigned __int64 a2) = 0;
	virtual void __fastcall sub_140382A80() = 0;
	virtual __int64 __fastcall sub_14038CC90(int a1, unsigned int a2, __int64 a3, __int64 a4) = 0;
	virtual __int64 __fastcall UserMathErrorFunction() = 0;
};

#endif // IFILESYSTEM_H