//-----------------------------------------------------------------------------
// AScript tar module
// http://www.gnu.org/software/tar/manual/html_node/Standard.html
//-----------------------------------------------------------------------------
#ifndef __MODULE_TAR_H__
#define __MODULE_TAR_H__

#include <ascript.h>
#include "ascript/ZLibHelper.h"
#include "ascript/BZLibHelper.h"

AScript_BeginModule(tar)

class Header;

//-----------------------------------------------------------------------------
// utilities
//-----------------------------------------------------------------------------
Header *ReadHeader(Signal sig, Stream *pStream, void *buffBlock, bool skipBodyFlag);
Stream *DecorateStream(Signal sig, Stream *pStream, const char *name);
unsigned long OctetToULong(Signal sig, const char *octet, size_t len);

inline bool IsGZippedTar(const char *name) {
	return (EndsWith(name, ".tar.gz", true) || EndsWith(name, ".tgz", true));
}

inline bool IsBZippedTar(const char *name) {
	return (EndsWith(name, ".tar.bz2", true));
}

//-----------------------------------------------------------------------------
// TAR format data types
//-----------------------------------------------------------------------------
extern const int BLOCKSIZE;

struct posix_header
{						// byte offset
	char name[100];		//   0
	char mode[8];		// 100
	char uid[8];		// 108
	char gid[8];		// 116
	char size[12];		// 124
	char mtime[12];		// 136
	char chksum[8];		// 148
	char typeflag;		// 156
	char linkname[100];	// 157
	char magic[6];		// 257
	char version[2];	// 263
	char uname[32];		// 265
	char gname[32];		// 297
	char devmajor[8];	// 329
	char devminor[8];	// 337
	char prefix[155];	// 345
						// 500
};

struct star_header
{						// byte offset
	char name[100];		//   0
	char mode[8];		// 100
	char uid[8];		// 108
	char gid[8];		// 116
	char size[12];		// 124
	char mtime[12];		// 136
	char chksum[8];		// 148
	char typeflag;		// 156
	char linkname[100];	// 157
	char magic[6];		// 257
	char version[2];	// 263
	char uname[32];		// 265
	char gname[32];		// 297
	char devmajor[8];	// 329
	char devminor[8];	// 337
	char prefix[131];	// 345
	char atime[12];		// 476
	char ctime[12];		// 488
						// 500
};

//-----------------------------------------------------------------------------
// Header declaration
//-----------------------------------------------------------------------------
class Header {
private:
	size_t _offset;
	char _name[100 + 1];
	char _linkname[100 + 1];
	char _uname[32 + 1];
	char _gname[32 + 1];
	unsigned long _mode;
	unsigned long _uid;
	unsigned long _gid;
	size_t _size;
	DateTime _mtime;
	DateTime _atime;
	DateTime _ctime;
	unsigned long _chksum;
	char _typeflag;
	unsigned long _devmajor;
	unsigned long _devminor;
public:
	Header();
	Header(const Header &hdr);
	void Initialize();
	bool SetRawHeader(Signal sig, const star_header &hdr);
	void GetRawHeader(star_header &hdr);
	inline void SetOffset(size_t offset) { _offset = offset; }
	inline size_t GetOffset() const { return _offset; }
	inline void SetName(const char *name) {
		::memset(_name, 0x00, sizeof(_name)), ::strcpy(_name, name);
	}
	inline const char *GetName() const { return _name; }
	inline void SetLinkName(const char *linkname) {
		::memset(_linkname, 0x00, sizeof(_linkname)), ::strcpy(_linkname, linkname);
	}
	inline const char *GetLinkName() const { return _linkname; }
	inline void SetUName(const char *uname) {
		::memset(_uname, 0x00, sizeof(_uname)), ::strcpy(_uname, uname);
	}
	inline const char *GetUName() const { return _uname; }
	inline void SetGName(const char *gname) {
		::memset(_gname, 0x00, sizeof(_gname)), ::strcpy(_gname, gname);
	}
	inline const char *GetGName() const { return _gname; }
	inline void SetMode(unsigned long mode) { _mode = mode; }
	inline unsigned long GetMode() const { return _mode; }
	inline void SetUid(unsigned long uid) { _uid = uid; }
	inline unsigned long GetUid() const { return _uid; }
	inline void SetGid(unsigned long gid) { _gid = gid; }
	inline unsigned long GetGid() const { return _gid; }
	inline void SetSize(size_t size) { _size = size; }
	inline size_t GetSize() const { return _size; }
	inline void SetMTime(const DateTime &mtime) { _mtime = mtime; }
	inline DateTime GetMTime() const { return _mtime; }
	inline void SetATime(const DateTime &atime) { _atime = atime; }
	inline DateTime GetATime() const { return _atime; }
	inline void SetCTime(const DateTime &ctime) { _ctime = ctime; }
	inline DateTime GetCTime() const { return _ctime; }
	inline void SetChksum(unsigned long chksum) { _chksum = chksum; }
	inline unsigned long GetChksum() const { return _chksum; }
	inline void SetTypeFlag(char typeflag) { _typeflag = typeflag; }
	inline char GetTypeFlag() const { return _typeflag; }
	inline void SetDevMajor(unsigned long devmajor) { _devmajor = devmajor; }
	inline unsigned long GetDevMajor() const { return _devmajor; }
	inline void SetDevMinor(unsigned long devminor) { _devminor = devminor; }
	inline unsigned long GetDevMinor() const { return _devminor; }
	inline unsigned long CalcBlocks() const {
		return (_size + BLOCKSIZE - 1) / BLOCKSIZE;
	}
};

//-----------------------------------------------------------------------------
// Stream_TAR declaration
//-----------------------------------------------------------------------------
class Stream_TAR : public Stream {
protected:
	Stream *_pStreamSrc;
	Header _hdr;
	String _name;
	size_t _bytesSrc;
	size_t _offsetTop;
public:
	Stream_TAR(Signal sig, Stream *pStreamSrc, const Header &hdr);
	virtual ~Stream_TAR();
	virtual const char *GetName() const;
	virtual size_t DoRead(Signal sig, void *buff, size_t bytes);
	virtual bool DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode);
	virtual size_t DoWrite(Signal sig, const void *buff, size_t len);
	virtual bool DoFlush(Signal sig);
	virtual bool DoClose(Signal sig);
	virtual size_t DoGetSize();
	virtual Object *DoGetStatObj(Signal sig);
	inline Stream &GetSource() { return *_pStreamSrc; }
	inline const Stream &GetSource() const { return *_pStreamSrc; }
};

//-----------------------------------------------------------------------------
// Directory_TAR declaration
//-----------------------------------------------------------------------------
class Record_TAR;

class Directory_TAR : public Directory {
private:
	DirBuilder::Structure *_pStructure;
	Record_TAR *_pRecord;
public:
	Directory_TAR(Directory *pParent, const char *name,
		Type type, DirBuilder::Structure *pStructure, Record_TAR *pRecord);
	virtual ~Directory_TAR();
	virtual Directory *DoNext(Environment &env, Signal sig);
	virtual Stream *DoOpenStream(Environment &env, Signal sig,
									unsigned long attr, const char *encoding);
	virtual Object *DoGetStatObj(Signal sig);
};

//-----------------------------------------------------------------------------
// DirectoryFactory_TAR declaration
//-----------------------------------------------------------------------------
class DirectoryFactory_TAR : public DirectoryFactory {
public:
	virtual bool IsResponsible(Environment &env, Signal sig,
					const Directory *pParent, const char *pathName);
	virtual Directory *DoOpenDirectory(Environment &env, Signal sig,
		Directory *pParent, const char **pPathName, Directory::NotFoundMode notFoundMode);
};

//-----------------------------------------------------------------------------
// Record_TAR declaration
//-----------------------------------------------------------------------------
class Record_TAR : public DirBuilder::Record {
private:
	Header *_pHdr;
public:
	inline Record_TAR(DirBuilder::Structure *pStructure, Record_TAR *pParent,
									const char *name, bool containerFlag) :
		DirBuilder::Record(pStructure, pParent, name, containerFlag), _pHdr(NULL) {}
	virtual DirBuilder::Record *DoGenerateChild(const char *name, bool containerFlag);
	virtual Directory *DoGenerateDirectory(Directory *pParent, Directory::Type type);
	inline void SetHeader(Header *pHdr) { _pHdr = pHdr; }
	inline const Header *GetHeader() const { return _pHdr; }
};

//-----------------------------------------------------------------------------
// Object_Tar declaration
//-----------------------------------------------------------------------------
AScript_DeclarePrivClass(Tar);

class Object_Tar : public Object {
private:
	Signal _sig;
	String _pathName;
	Stream *_pStreamSrc;
	Stream *_pStreamDst;
	OAL::Memory _memBlock;
public:
	AScript_DeclareObjectAccessor(Tar)
public:
	Object_Tar(Signal sig);
	inline const char *GetPathName() const { return _pathName.c_str(); }
	virtual ~Object_Tar();
	virtual Object *Clone() const;
	virtual String ToString(Signal sig, bool exprFlag);
	bool Open(Environment &env, Signal sig, const char *pathName);
	bool Create(Environment &env, Signal sig, const char *pathName);
	bool Add(Signal sig, Stream &streamSrc, const char *fileName);
	bool Close();
	inline Stream *GetStreamSrc() { return _pStreamSrc; }
	inline Stream *GetStreamDst() { return _pStreamDst; }
	Header *NextHeader(Signal sig, bool skipBodyFlag);
};

//-----------------------------------------------------------------------------
// Iterator_Each declaration
//-----------------------------------------------------------------------------
class Iterator_Each : public Iterator {
private:
	Object_Tar *_pObjTar;
public:
	inline Iterator_Each(Object_Tar *pObjTar);
	virtual ~Iterator_Each();
	virtual bool DoNext(Environment &env, Signal sig, Value &value);
	virtual String ToString(Signal sig) const;
};

}}

#endif
