////////////////////////////////////////////////////////////////////////////////
// FileCache.h
// Copyright Bjoern Ganster 
// Developed since 2005
////////////////////////////////////////////////////////////////////////////////

#ifndef FileCache__H
#define FileCache__H

////////////////////////////////////////////////////////////////////////////////

#include <stdlib.h>
#include <stdio.h>

#include "BGBase.h"
#include "BGCPString.h"

typedef short int16;

void bgmkdir(const char* newDir);
#ifdef _WIN32
void bgmkdir(const wchar_t* newDir);
#endif

#ifdef _WIN32
typedef DWORD FileOffset;
#else
typedef size_t FileOffset;
#endif
typedef FileOffset CacheOffset;
typedef FILE* FileHandle;

//#pragma message("FileCache is deprecated")

////////////////////////////////////////////////////////////////////////////////
// Header definition for a diff file

struct DiffHeader {
	char signature[16];
	DWORD size1, size2, diffs, reserved;
};

////////////////////////////////////////////////////////////////////////////////
// Low-level functions and OS abstractions

// Test if file exists
bool fileExists (const PathChar* FN);

// Copy single file
void copyFile (const PathChar* sourceFN, const PathChar* targetFN);

// Save buffer to file - moved to BGBase.h/cpp

#ifdef _WIN32
#include <direct.h>
#else
inline void mkdir (const TCHAR* newDir)
//{ CreateDirectory (newDir, NULL); } // WIN32 has mkdir
{ mkdir (newDir); }
#endif

#ifdef _WIN32
inline void bgDeleteFile (const wchar_t* fn)
{ DeleteFileW(fn); }
inline void bgDeleteFile (const char* fn)
{ DeleteFileA(fn); }
#else
inline void bgDeleteFile (const char* fn)
{ remove (fn); }
#endif

// Delete File
#ifdef _WIN32
inline int bgrmdir(const PathChar* dirName)
{
   if (sizeof(PathChar) == 2)
      return _wrmdir((wchar_t*) dirName);
   else
      return _rmdir((char*) dirName);
}
#else
inline int bgrmdir(const PathChar* dirName)
{
  return rmdir(dirName);
}
#endif

// Return file size, given the file's name

inline long getFileSize (FileHandle& file)
{
   if (file != NULL) {
      long oldPos = ftell (file);
      fseek (file, 0, SEEK_END);
      long filesize = ftell (file);
      fseek (file, oldPos, SEEK_SET);
      return filesize;
   } else
      return 0;
}

#ifdef _WIN32
inline long getFileSize(const char* fn)
{
   FILE* f = NULL;
   fopen_s(&f, fn, "r");
   if (f != NULL) {
      long result = getFileSize(f);
      fclose(f);
      return result;
   } else
      return 0;
}
inline long getFileSize(const wchar_t* fn)
{
   FILE* f;
   _wfopen_s(&f, fn, L"r");
   if (f != NULL) {
       long result = getFileSize(f);
       fclose(f);
       return result;
   } else
      return 0;
}
#else
inline long getFileSize(const char* fn)
{
   FILE* f = fopen(fn, "r");
   if (f != NULL) {
       long result = getFileSize(f);
       fclose(f);
       return result;
} else
      return 0;
}
#endif

// Patch targetFN using a previously produced diffFile
bool patch(const PathChar* diffFile, const PathChar* targetFN);

// Delete all files in dirName
void deleteTree (const PathChar* dirName);

////////////////////////////////////////////////////////////////////////////////
// Class FileCache
// FilePos could be stored, but would have to be tracked and reset if new 
// operation is read and last was write or vice versa

class FileCache: public VirtualClass {
private:
	// Property variables and private members
	FileOffset m_CacheStartPos;
	CacheOffset m_CacheSize;
	FileOffset m_FileSize;
	char* cache;
	CacheOffset UsedCacheSize;
	bool MustWriteCacheBack;
	#ifdef _WIN32
	HANDLE m_FileHandle;
	#else
	FileHandle m_FileHandle;
	#endif

	// Delay writing back if set to ReadOnly until necessary ...
	// MustWriteCacheBack stores necessary state
	bool m_isReadOnly;

	// Write cache back to file
	bool writeBackCache ();

	// Fill cache
	bool fillCache ();

	// Invalidate Cache
	void invalidateCache ();

	// Copy bytes from buf
	bool copyBytesFromBuf (const char* buf, FileOffset offset, FileOffset count);

	bool enlargeFile (FileOffset NewSize);

	// Check if handle is valid
	inline bool isHandleValid() const
	{
		#ifdef _WIN32
		return (m_FileHandle != INVALID_HANDLE_VALUE);
		#else
		return (m_FileHandle != 0);
		#endif
	}

public:
	// Controls whether the destructor closes the file
	bool CloseFileOnDestroy;

	// Properties
	bool setCacheStartPos (FileOffset newPos);
	FileOffset getCacheStartPos () const 
	{ return m_CacheStartPos; }

	bool setCacheSize (CacheOffset newSize);
	FileOffset getCacheSize () 
	{ return m_CacheSize; }

	FileOffset getFileSize () const 
	{ return m_FileSize; }

	// Constructor, destructor
	FileCache (CacheOffset NewCacheSize = 4096);
	virtual ~FileCache ();

	// Open, close file
	bool openReadOnly (const PathChar* FileName);
	bool create (const PathChar* FileName);
	bool openReadWrite (const PathChar* FileName);
	void close ();

	// Read, write access
	bool read (FileOffset offset, char* buf, FileOffset count);
	bool write (FileOffset offset, const char* buf, FileOffset count);
      inline int readInt32 (FileOffset offset)
      {
         int i = 0;
         read (offset, (char*) (&i), 4);
         return i;
      }
      inline DWORD readDWord (FileOffset offset)
      {
         DWORD result;
         read (offset, (char*) (&result), 4);
         return result;
      }
      template <typename T>
      T read(FileOffset offset)
      {
          T result;
          read (offset, (char*) (&result), sizeof(T));
          return result;
      }

      // Append
      bool append (const char* buf, size_t count);
	  template <typename C>
      bool appendStr (const C* str)
      { return append ((const char*) str, bgstrlen (str) * sizeof(C)); }

      // Is given path absolute?
      static bool isPathAbsolute (const char* path);

      // Flush file
      void flush ();

      bool isReadOnly () const
      { return m_isReadOnly; }

      bool isOpen() const
      {
         #ifdef _WIN32
            return (m_FileHandle != INVALID_HANDLE_VALUE);
         #else
            return (m_FileHandle != 0);
         #endif
      }
};

#endif
