#ifndef _LIB_ZIPFILE_H_
#define _LIB_ZIPFILE_H_

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

#include "c4d_library.h"

// external attributes
#define ZIP_FLAG_READONLY							0x00000001
#define ZIP_FLAG_DIRECTORY						0x00000010

#define ZIP_FILE_FLAG_OWNER_R					0x01000000
#define ZIP_FILE_FLAG_OWNER_W					0x00800000
#define ZIP_FILE_FLAG_OWNER_X					0x00400000
#define ZIP_FILE_FLAG_GROUP_R					0x00200000
#define ZIP_FILE_FLAG_GROUP_W					0x00100000
#define ZIP_FILE_FLAG_GROUP_X					0x00080000
#define ZIP_FILE_FLAG_PUBLIC_R				0x00040000
#define ZIP_FILE_FLAG_PUBLIC_W				0x00020000
#define ZIP_FILE_FLAG_PUBLIC_X				0x00010000
#define ZIP_FILE_FLAG_UNIX_MASK				0x01ff0000
#define ZIP_FILE_FLAG_USE_UNIX_FLAGS	0x80000000

// internal attributes
#define ZIP_FILE_FLAG_UNIX_ATTR				0x00000001

#define ZIP_EXTRACT_CREATE_SUBDIR			0x00000001
#define ZIP_EXTRACT_IGNORE_READONLY		0x00000002
#define ZIP_EXTRACT_IGNORE_PATH_CASE	0x00000004 // ignore path case on Linux (if the path or parts of it already exist)

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 (*ExtractDirectoryCallbackEx)(const Filename &fnSrc, const Filename &fnDest, void* pData); // return FALSE if you want to cancel

typedef LONG (*ExtractDirectoryCallback)(const Filename &fnSrc, const Filename &fnDest, void* pData, LONG lInfo);
#define EXTRACT_INFO_SUCCESS													0
#define EXTRACT_INFO_FAILED														1
#define EXTRACT_INFO_CANT_WRITE												2
	#define EXTRACT_INFO_CANT_WRITE_R_FORCE_OVERWRITE		2
	#define EXTRACT_INFO_CANT_WRITE_R_IGNORE						3
#define EXTRACT_INFO_R_CANCEL													1

#define ZIP_APPEND_CREATE					0
#define ZIP_APPEND_CREATEAFTER		1
#define ZIP_APPEND_ADDINZIP				2

class ZipFile
{
private:
	ZipFile();
	~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(const void* pBuffer, LONG lBufferLen, ULONG ulOldCRC = 0);

	// set lAppend = ZIP_APPEND_*, if you want to append the zip file to an existing file
	Bool Open(const Filename &fn, const Bool bRead, const LONG lAppend = ZIP_APPEND_CREATE);
	// OpenEncrypted uses AES encryption
	Bool OpenEncrypted(const Filename &fn, const Bool bRead, const char* pchKey, LONG lKeyLength, LONG lBlockLength, ULONG lAESFlags, const LONG lAppend = ZIP_APPEND_CREATE);
	Bool SetSpanning(ULONG ulSpan, Bool bOverwrite); // call immediately after opening the file for writing

	Bool ExtractToDirectory(const Filename& fnZip, const Filename& fnDir, LONG lFlags = ZIP_EXTRACT_CREATE_SUBDIR, ExtractDirectoryCallback fn = NULL, 
		void* pData = NULL, const char* pChPassword = NULL);
	Bool ExtractToDirectoryEx(const Filename& fnZip, const Filename& fnDir, LONG lFlags = ZIP_EXTRACT_CREATE_SUBDIR, ExtractDirectoryCallbackEx fn = NULL, 
		void* pData = NULL, const 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, const 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, const char* pchPassword = NULL);
	Bool CopyInFileInZip(const Filename& fn, const String& str, ULONG ulInternalAttributes, ULONG ulExternalAttributes, const 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, BaseThread* pThread, LONG lFlags = ZIP_EXTRACT_CREATE_SUBDIR, const char* pchPassword = NULL, Filename* pfnDest = NULL);
	Bool ExtractCurrentFile(const Filename& fnDir, LONG lFlags = ZIP_EXTRACT_CREATE_SUBDIR, const char* pchPassword = NULL, Filename* pfnDest = NULL);
	Bool OpenCurrentFile(const 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();

	// call before open/init
	Bool SetCompressionLevel(LONG lLevel);
	LONG GetCompressionLevel() const;
};

#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. lSrcLen may not be greater than 2 GiB
Bool CompressDataRaw(const void* pSrcData, VLONG lSrcLen, void* pDestData, VLONG* plDestLen, LONG lLevel); 
// plDestLen must have the same value as lSrcLen in CompressData. pDestData is an array of *plDestLen bytes, lSrcLen may not be greater than 2 GiB
Bool UncompressDataRaw(const void* pSrcData, VLONG lSrcLen, void* pDestData, VLONG* plDestLen, VLONG* plRead = NULL);

// Similar to the functions above, but the array is created internally. Use GeFree to delete it
#define COMPRESS_DATA_32_BYTE_PADDING		0x00000100 // may be ORed with lLevel to make lDestLen a multiple of 32
Bool CompressData(const void* pSrcData, VLONG lSrcLen, void *&pDestData, VLONG &lDestLen, LONG lLevel); // lSrcLen may not be greater than 2 GiB
Bool UncompressData(const void* pSrcData, VLONG lSrcLen, void *&pDestData, VLONG &lDestLen, VLONG* plRead = NULL); // lSrcLen may not be greater than 2 GiB

struct ZHandle;
ZHandle* CompressDataInit(LONG lLevel);
Bool CompressDataDoIt(ZHandle *handle, const void* pSrcData, VLONG lSrcLen, void *&pDestData, VLONG &lDestLen, LONG lFlags);
void CompressDataFree(ZHandle *&handle);

// 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)(const void* pBuffer, LONG lBufferLen, ULONG ulOldCRC);
	
	Bool      (*Open)(iZipFile *this_ptr, const Filename& fn, const Bool bRead, const LONG lAppend);
	Bool      (*ExtractToDirectoryEx)(iZipFile *this_ptr, const Filename& fnZip, const Filename& fnDir, LONG lFlags, ExtractDirectoryCallbackEx fn, void* pData, const char* pChPassword);
	Bool      (*CloseNC)(iZipFile *this_ptr);
	Bool      (*CloseCC)(iZipFile *this_ptr, const char* pComment);
	Bool      (*CloseSC)(iZipFile *this_ptr, const String& strComment);

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

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

	// 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, VLONG lSrcLen, void* pDestData, VLONG* plDestLen, LONG lLevel); 
	Bool      (*UncompressDataRaw)(const void* pSrcData, VLONG lSrcLen, void* pDestData, VLONG* plDestLen, VLONG* plRead);
	Bool      (*CompressData2)(const void* pSrcData, VLONG lSrcLen, void *&pDestData, VLONG &lDestLen, LONG lLevel); 
	Bool      (*UncompressData2)(const void* pSrcData, VLONG lSrcLen, void *&pDestData, VLONG &lDestLen, VLONG* plRead);

	Bool      (*ExtractCurrentFile)(iZipFile *this_ptr, const Filename& fnDir, LONG lFlags, const char* pchPassword, Filename* pfnDest);
	Bool      (*ExtractToDirectory)(iZipFile *this_ptr, const Filename& fnZip, const Filename& fnDir, LONG lFlags, ExtractDirectoryCallback fn, void* pData, const char* pChPassword);

	Bool      (*SetSpanning)(iZipFile *this_ptr, ULONG ulSpan, Bool bOverwrite);
	Bool      (*OpenEncrypted)(iZipFile *this_ptr, const Filename &fn, const Bool bRead, const char* pKey, LONG lKeyLength, LONG lBlockLength, ULONG lAESFlags, const LONG lAppend);

	Bool      (*ExtractCurrentFileT)(iZipFile *this_ptr, const Filename& fnDir, BaseThread* pThread, LONG lFlags, const char* pchPassword, Filename* pfnDest);

	ZHandle*	(*CompressDataInit)(LONG lLevel);
	Bool			(*CompressDataDoIt)(ZHandle *handle, const void* pSrcData, VLONG lSrcLen, void *&pDestData, VLONG &lDestLen, LONG flags);
	void			(*CompressDataFree)(ZHandle *&handle);

	Bool			(*CopyInFileInZipB)(iZipFile *this_ptr, const Filename& fn, const String& str, ULONG ulInternalAttributes, ULONG ulExternalAttributes, const char* pchPassword);

	Bool			(*SetCompressionLevel)(iZipFile *this_ptr, LONG lLevel);
	LONG			(*GetCompressionLevel)(const iZipFile *this_ptr);

	Bool      (*WriteInFileInZip)(iZipFile *this_ptr, const void* pBuffer, VULONG lLen, LLONG lExpectedSize);
};

// 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_
