#ifndef _LIB_ZIPFILE_H_
#define _LIB_ZIPFILE_H_

// based upon zlib, see http://www.gzip.org/zlib/ for more information

#include "c4d_library.h"

#define   ZIP_FLAG_DIRECTORY      16

struct ZipFileTime
{
	UINT nSec; // seconds (0..59)
	UINT nMin; // minutes (0..59)
	UINT nHour; // hour (0..23)
	UINT nMDay; // day of month (1..31)
	UINT nMonth; // month (0..11)
	UINT nYear; // (1980..2044)
};

struct ZipFileGlobalInfo
{
	ULONG lEntries;       // the number of entries in the zip file
	ULONG lCommentSize;   // the size of the global comment
};

struct ZipFileInfo
{
	ULONG lVersion;
	ULONG lVersionNeeded;
	ULONG lFlags;
	ULONG lCompressionMethod;
	ULONG lDOSDate;
	ULONG lCRC32;
	ULONG lCompressedSize;
	ULONG lUncompressedSize;
	ULONG lSizeFilename;
	ULONG lSizeExtraField;
	ULONG lSizeFileComment;
	ULONG lDiskNumberStart;
	ULONG lInternalAttr;
	ULONG lExternalAttr;
	ZipFileTime t;
};

struct ZipWriteInfo
{
	ZipFileTime ti; // the time of the file in the zip file
	ULONG lInternalAttr, lExternalAttr; // currently private
};

enum ZipMethod { ZipMethodDeflate, ZipMethodStore };

typedef Bool (*ExtractDirectoryCallback)(const Filename &fnSrc, const Filename &fnDest, void* pData); // return FALSE if you want to cancel

class ZipFile
{
private:
	ZipFile();

public:
	static ZipFile* Alloc();
	static void Free(ZipFile *&p);

	static Bool CreateLocalFileName(const Filename &fn, String& str);
	static Bool CreateFilename(const String& str, Filename &fn);
	static Bool GetFileCRC(const Filename &fn, ULONG &ulCRC);
	static ULONG CalcCRC32(void* pBuffer, LONG lBufferLen, ULONG ulOldCRC = 0);

	// set bAppend = TRUE, if you want to append the zip file to an existing file
	Bool Open(const Filename &fn, const Bool bRead, const Bool bAppend = FALSE);

	Bool ExtractToDirectory(const Filename& fnZip, const Filename& fnDir, Bool bCreateSubdirectory = TRUE, ExtractDirectoryCallback fn = NULL, 
		void* pData = NULL, char* pChPassword = NULL);

	// closes the file
	Bool Close();
	// comments are only written if the file is saved
	Bool Close(const char* pchGlobalComment);
	Bool Close(const String& strGlobalComment);

	// only for writing zip files

	// creates a new file in the current zip archive. Directories must be separated by a slash /
	Bool CreateFileInZip(const String& strName, ZipWriteInfo* pInfo, void* pExtraFieldLocal, ULONG lExtraSizeLocal, void* pExtraFieldGlobal, ULONG lExtraSizeGlobal,
											 String* pstrComment, ZipMethod method, INT lLevel, char* pchPassword = NULL, ULONG ulCryptingCRC = 0);  // level may range from 0 to 9
	Bool WriteInFileInZip(const void* pBuffer, const ULONG lLen, const LONG lExpectedSize = -1);
	Bool CopyInFileInZip(const Filename& fn, const String& str, char* pchPassword = NULL);
	Bool CloseFileInZip();
	Bool CreateDirectoryInZip(const String& strName, ZipFileTime* pTime); // a slash is added if necessary

	// only for reading
	Bool GetGlobalInfo(ZipFileGlobalInfo &i);
	Bool GetGlobalComment(String& str);
	Bool GoToFirstFile();
	Bool GoToNextFile();
	Bool LocateFile(const String& strName);
	Bool GetCurrentFileInfo(ZipFileInfo &i);
	Bool GetCurrentFileInfo(String* pstrName, String* pstrComment = NULL, void* pExtraField = NULL, ULONG lExtraFieldSize = 0);
	Bool ExtractCurrentFile(const Filename& fnDir, Bool bCreateSubdirectory = TRUE, const char* pchPassword = NULL, Filename* pfnDest = NULL);
	Bool OpenCurrentFile(char* pChPassword = NULL);
	Bool CloseCurrentFile();
	LONG ReadCurrentFile(void* pBuffer, ULONG lBufferSize); // returns the number of read bytes, < 0 if there was an error
	LONG GetCurrentFileReadPosition();
	Bool EndOfCurrentFile();
};

#define FILETIME_CREATED			0
#define FILETIME_MODIFIED			1
#define FILETIME_ACCESS				2
Bool GetFileTime(const Filename &fn, UINT &nYear, UINT &nMonth, UINT &nDay, UINT &nHour, UINT &nMinute, UINT &nSec, LONG lType, Bool bIsDir);
Bool SetFileTime(const Filename &fn, UINT nYear, UINT nMonth, UINT nDay, UINT nHour, UINT nMinute, UINT nSec, LONG lType, Bool bIsDir);
// level may range from 0 to 9, plDestLen must be 0.1% larger than lSrcLen + 12 Bytes, plDestLen is filled with the compressed data length
// pDestData is an array of *plDestLen bytes
Bool CompressDataRaw(const void* pSrcData, LONG lSrcLen, void* pDestData, LONG* plDestLen, LONG lLevel); 
// plDestLen must have the same value as lSrcLen in CompressData. pDestData is an array of *plDestLen bytes
Bool UncompressDataRaw(const void* pSrcData, LONG lSrcLen, void* pDestData, LONG* plDestLen, LONG* plRead = NULL);

// Similar to the functions above, but the array is created internally. Use GeFree to delete it
Bool CompressData(const void* pSrcData, LONG lSrcLen, void *&pDestData, LONG &lDestLen, LONG lLevel);
Bool UncompressData(const void* pSrcData, LONG lSrcLen, void *&pDestData, LONG &lDestLen, LONG* plRead = NULL);


// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF

#define LIBRARY_ZIPFILE     1011188

class iZipFile;

struct ZipFileLib : public C4DLibrary
{
	ZipFile*  (*ZipFile_Alloc)();
	void      (*ZipFile_Free)(ZipFile *&p);
	Bool      (*ZipFile_CreateLocalFileName)(const Filename &fn, String& str);
	Bool      (*ZipFile_CreateFilename)(const String& str, Filename &fn);
	Bool      (*ZipFile_GetFileCRC)(const Filename &fn, ULONG &ulCRC);
	ULONG     (*ZipFile_CalcCRC32)(void* pBuffer, LONG lBufferLen, ULONG ulOldCRC);
	
	Bool      (iZipFile::*Open)(const Filename& fn, const Bool bRead, const Bool bAppend);
	Bool      (iZipFile::*ExtractToDirectory)(const Filename& fnZip, const Filename& fnDir, Bool bCreateSubdirectory, ExtractDirectoryCallback fn, void* pData, char* pChPassword);
	Bool      (iZipFile::*CloseNC)();
	Bool      (iZipFile::*CloseCC)(const char* pComment);
	Bool      (iZipFile::*CloseSC)(const String& strComment);

	// write
	Bool      (iZipFile::*CreateFileInZip)(const String& strName, ZipWriteInfo* pInfo, void* pExtraFieldLocal, ULONG lExtraSizeLocal, void* pExtraFieldGlobal, ULONG lExtraSizeGlobal,
											 String* pstrComment, ZipMethod method, INT lLevel, char* pchPassword, ULONG ulCryptingCRC);
	Bool      (iZipFile::*WriteInFileInZip)(const void* pBuffer, const ULONG lLen, const LONG lExpectedSize);
	Bool      (iZipFile::*CopyInFileInZip)(const Filename& fn, const String& str, char* pchPassword);
	Bool      (iZipFile::*CloseFileInZip)();
	Bool      (iZipFile::*CreateDirectoryInZip)(const String& strName, ZipFileTime* pTime);

	// read
	Bool      (iZipFile::*GetGlobalInfo)(ZipFileGlobalInfo &i);
	Bool      (iZipFile::*GetGlobalComment)(String& str);
	Bool      (iZipFile::*GoToFirstFile)();
	Bool      (iZipFile::*GoToNextFile)();
	Bool      (iZipFile::*LocateFile)(const String& strName);
	Bool      (iZipFile::*GetCurrentFileInfoA)(ZipFileInfo &i);
	Bool      (iZipFile::*GetCurrentFileInfoB)(String* pstrName, String* pstrComment, void* pExtraField, ULONG lExtraFieldSize);
	Bool      (iZipFile::*OpenCurrentFile)(char* pChPassword);
	Bool      (iZipFile::*CloseCurrentFile)();
	LONG      (iZipFile::*ReadCurrentFile)(void* pBuffer, ULONG lBufferSize);
	LONG      (iZipFile::*GetCurrentFileReadPosition)();
	Bool      (iZipFile::*EndOfCurrentFile)();

	// other stuff
	Bool      (*GetFileTime)(const Filename &fn, UINT &nYear, UINT &nMonth, UINT &nDay, UINT &nHour, UINT &nMinute, UINT &nSec, LONG lType, Bool bIsDir);
	Bool      (*SetFileTime)(const Filename &fn, UINT nYear, UINT nMonth, UINT nDay, UINT nHour, UINT nMinute, UINT nSec, LONG lType, Bool bIsDir);
	Bool      (*CompressDataRaw)(const void* pSrcData, LONG lSrcLen, void* pDestData, LONG* plDestLen, LONG lLevel); 
	Bool      (*UncompressDataRaw)(const void* pSrcData, LONG lSrcLen, void* pDestData, LONG* plDestLen, LONG* plRead);
	Bool      (*CompressData2)(const void* pSrcData, LONG lSrcLen, void *&pDestData, LONG &lDestLen, LONG lLevel); 
	Bool      (*UncompressData2)(const void* pSrcData, LONG lSrcLen, void *&pDestData, LONG &lDestLen, LONG* plRead);

	Bool      (iZipFile::*ExtractCurrentFile)(const Filename& fnDir, Bool bCreateSubdirectory, const char* pchPassword, Filename* pfnDest);
};

// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF
// INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF -- INTERNAL STUFF


#endif // _LIB_ZIPFILE_H_
