//-----------------------------------------------------------------------------
// AScript fs module
//-----------------------------------------------------------------------------
#ifndef __MODULE_FS_H__
#define __MODULE_FS_H__
#include <ascript.h>

#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif

AScript_BeginModule(fs)

//-----------------------------------------------------------------------------
// FileStat declaration
//-----------------------------------------------------------------------------
class FileStat {
public:
	enum {
		ATTR_Dir	= (1 << 22),
		ATTR_Chr	= (1 << 21),
		ATTR_Blk	= (1 << 20),
		ATTR_Reg	= (1 << 19),
		ATTR_Fifo	= (1 << 18),
		ATTR_Lnk	= (1 << 17),
		ATTR_Sock	= (1 << 16),
	};
private:
	String _pathName;
	unsigned long _attr;
	unsigned long _bytes;
	DateTime _atime;
	DateTime _mtime;
	DateTime _ctime;
	long _uid;
	long _gid;
public:
	inline FileStat() : _attr(0), _bytes(0), _uid(0), _gid(0) {}
	inline FileStat(const char *pathName, unsigned long attr, unsigned long bytes,
				const DateTime &atime, const DateTime &mtime, const DateTime &ctime,
				long uid, long gid) :
		_pathName(pathName), _attr(attr), _bytes(bytes),
		_atime(atime), _mtime(mtime), _ctime(ctime),
		_uid(uid), _gid(gid) {}
	inline FileStat(const FileStat &fileStat) :
		_pathName(fileStat._pathName), _attr(fileStat._attr), _bytes(fileStat._bytes),
		_atime(fileStat._atime), _mtime(fileStat._mtime), _ctime(fileStat._ctime),
		_uid(fileStat._uid), _gid(fileStat._gid) {}
#if defined(HAVE_WINDOWS_H)
	FileStat(const char *pathName, const BY_HANDLE_FILE_INFORMATION &attrData);
	FileStat(const char *pathName, const WIN32_FILE_ATTRIBUTE_DATA &attrData);
	FileStat(const char *pathName, const WIN32_FIND_DATA &findData);
#else
	FileStat(const char *pathName, const struct stat &stat);
#endif
	inline const char *GetPathName() const { return _pathName.c_str(); }
	inline unsigned long GetSize() const { return _bytes; }
	inline const DateTime &GetATime() const { return _atime; }
	inline const DateTime &GetMTime() const { return _mtime; }
	inline const DateTime &GetCTime() const { return _ctime; }
	inline long GetUid() const { return _uid; }
	inline long GetGid() const { return _gid; }
	inline long GetMode() const { return _attr & 0777; }
	inline bool IsDir() const { return (_attr & ATTR_Dir)? true : false; }
	inline bool IsChr() const { return (_attr & ATTR_Chr)? true : false; }
	inline bool IsBlk() const { return (_attr & ATTR_Blk)? true : false; }
	inline bool IsReg() const { return (_attr & ATTR_Reg)? true : false; }
	inline bool IsFifo() const { return (_attr & ATTR_Fifo)? true : false; }
	inline bool IsLnk() const { return (_attr & ATTR_Lnk)? true : false; }
	inline bool IsSock() const { return (_attr & ATTR_Sock)? true : false; }
	static FileStat *Generate(Signal sig, const char *fileName);
};

//-----------------------------------------------------------------------------
// Object_Stat declaration
//-----------------------------------------------------------------------------
AScript_DeclarePrivClass(Stat);

class Object_Stat : public Object {
private:
	FileStat _fileStat;
public:
	AScript_DeclareObjectAccessor(Stat)
public:
	inline Object_Stat(const FileStat &fileStat) :
				Object(AScript_PrivClass(Stat)), _fileStat(fileStat) {}
	inline Object_Stat(const Object_Stat &obj) :
				Object(obj), _fileStat(obj._fileStat) {}
	virtual ~Object_Stat();
	virtual Object *Clone() const;
	virtual Value DoPropGet(Signal sig, const Symbol *pSymbol, bool &evaluatedFlag);
	virtual String ToString(Signal sig, bool exprFlag);
};

//-----------------------------------------------------------------------------
// Stream_File declaration
//-----------------------------------------------------------------------------
class Stream_File : public Stream {
private:
#if defined(HAVE_WINDOWS_H)
	HANDLE _hFile;
	struct {
		HANDLE hFileMappingObject;
		unsigned char *buff;
		size_t offset;
		size_t bytes;
	} _map;
#else
	// FILE instance cannot be passed to DLL's args (see KB190799 in MSDN)
	FILE *_fp;
#endif
	bool _needCloseFlag;
	String _fileName;
public:
	Stream_File(Signal sig);
	~Stream_File();
	bool Open(Signal sig, const char *fileName, unsigned long attr, const char *encoding);
	bool OpenStdin();
	bool OpenStdout();
	bool OpenStderr();
	virtual const char *GetName() const;
	virtual size_t DoRead(Signal sig, void *buff, size_t bytes);
	virtual size_t DoWrite(Signal sig, const void *buff, size_t bytes);
	virtual bool DoSeek(Signal sig, long offset, size_t offsetPrev, SeekMode seekMode);
	virtual bool DoFlush(Signal sig);
	virtual bool DoClose(Signal sig);
	virtual size_t DoGetSize();
	virtual Object *DoGetStatObj(Signal sig);
private:
	inline Stream_File(Signal sig, const Stream_File &file) : Stream(sig, ATTR_None) {}
};

//-----------------------------------------------------------------------------
// Directory_FileSys declaration
//-----------------------------------------------------------------------------
class Directory_FileSys : public Directory {
private:
#if defined(HAVE_WINDOWS_H)
	HANDLE _hFind;
#else
	DIR *_pDir;
#endif
	std::auto_ptr<FileStat> _pFileStat;
	std::auto_ptr<Codec_Decoder> _pDecoder;
	std::auto_ptr<Codec_Encoder> _pEncoder;
public:
	Directory_FileSys(Directory *pParent, const char *name,
										Type type, FileStat *pFileStat);
	inline Directory_FileSys(const Directory_FileSys &directory) : Directory(directory) {}
	virtual ~Directory_FileSys();
	virtual Object *DoGetStatObj(Signal sig);
	static bool IsExist(Signal sig, const char *pathName);
	static bool IsDir(Signal sig, const char *pathName);
protected:
	virtual Directory *DoNext(Environment &env, Signal sig);
	virtual Stream *DoOpenStream(Environment &env, Signal sig,
									unsigned long attr, const char *encoding);
};

//-----------------------------------------------------------------------------
// DirectoryFactory_FileSys declaration
//-----------------------------------------------------------------------------
class DirectoryFactory_FileSys : 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);
};

}}

#endif
