/*
 *	@file 	ejsFileSystem.c
 *	@brief 	FileSystem class for the EJ System Object Model
 *			MOB -- this is almost the same as for Windows. Should common up.
 */
/********************************** Copyright *********************************/
/*
 *	Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
 */
/********************************** Includes **********************************/

#include	"ejs.h"
#include	<dirent.h>

/******************************************************************************/
/************************************ Methods *********************************/
/******************************************************************************/
/*
 *	function void access(string path);
 *	MOB - API insufficient. Access for read or write?
 */

static int accessProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	int		rc;

	if (argc != 1 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: access(path)");
		return -1;
	}

	rc = access(argv[0]->string, 04);

	ejsSetReturnValueToBoolean(ejs, (rc == 0) ? 1 : 0);
	return 0;
}

/******************************************************************************/
/*
 *	function void mkdir(string path);
 */

static int mkdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	if (argc != 1 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)");
		return -1;
	}

	if (mprMakeDirPath(ejs, argv[0]->string) < 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant create directory");
		return -1;
	}
	return 0;
}

/******************************************************************************/
/*
 *	function void rmdir(string path);
 */

static int rmdirProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	int		rc;

	if (argc != 1 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: mkdir(path)");
		return -1;
	}

	rc = mprDeleteDir(ejs, argv[0]->string);

	if (rc < 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant remove directory");
		return -1;
	}
	return 0;
}

/******************************************************************************/
/*
 *	function void dirList(string path, [bool enumDirs]);
 *	MOB -- need pattern to match (what about "." and ".." and ".*"
 */

static int dirListProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	DIR				*dir;
	struct dirent	*dirent;
	char			path[MPR_MAX_FNAME];
	EjsVar			*array, *vp;
	uchar			enumDirs;

	if (argc < 1 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: dirList(path)");
		return -1;
	}
	if (argc == 2) {
		enumDirs = ejsVarToBoolean(argv[1]);
	} else {
		enumDirs = 0;
	}
	array = ejsCreateArray(ejs, 0);
	ejsMakeObjPermanent(array, 1);

	/*
	 *	First collect the files
	 */
	mprSprintf(path, sizeof(path), "%s/*.*", argv[0]->string);

	dir = opendir(path);
	if (dir == 0) {
		ejsError(ejs, EJS_ARG_ERROR, "Can't enumerate dirList(path)");
		return -1;
	}

	while ((dirent = readdir(dir)) != 0) {
		if (dirent->d_name[0] == '.') {
			continue;
		}
		if (!enumDirs || (dirent->d_type & DT_DIR)) {
			mprSprintf(path, sizeof(path), "%s/%s", argv[0]->string, 
				dirent->d_name);
			vp = ejsCreateStringVar(ejs, path);
			ejsAddArrayElt(ejs, array, vp, EJS_SHALLOW_COPY);
			ejsFreeVar(ejs, vp);
		}
	}

	closedir(dir);

	ejsSetReturnValue(ejs, array);
	ejsMakeObjPermanent(array, 0);

	/*
	 *	Can free now as the return value holds the reference
 	 */
	ejsFreeVar(ejs, array);

	return 0;
}

/******************************************************************************/
/*
 *	function void getFreeSpace();
 */

static int getFreeSpaceProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
#if UNUSED
	MprApp	*app;
	uint	space;

	app = mprGetApp(ejs);
	space = IFILEMGR_GetFreeSpace(app->fileMgr, 0);
	ejsSetReturnValueToInteger(ejs, space);
#endif

	return 0;
}

/******************************************************************************/
/*
 *	function void writeFile(string path, var data);
 */

static int writeFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	MprFile		*file;
	char		*data, *buf;
	int			bytes, length, rc;

	if (argc != 2 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: writeFile(path, var)");
		return -1;
	}

	if (ejsVarIsString(argv[1])) {
		data = argv[1]->string;
		length = argv[1]->length;
		buf = 0;
	} else {
		buf = data = ejsVarToString(ejs, argv[1]);
		length = strlen(data);
	}

	/*
	 *	Create fails if already present
	 */
	rc = mprDelete(ejs, argv[0]->string);
	file = mprOpen(ejs, argv[0]->string, O_CREAT | O_WRONLY | O_BINARY, 0664);
	if (file == 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[0]->string);
		mprFree(buf);
		return -1;
	}

	rc = 0;
	bytes = mprWrite(file, data, length);
	if (bytes != length) {
		ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string);
		rc = -1;
	}

	mprClose(file);

	mprFree(buf);
	return rc;
}

/******************************************************************************/
/*
 *	function string readFile(string path);
 */

static int readFileProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	MprApp	*app;
	MprFile	*file;
	MprBuf	*buf;
	char	*data;
	int		bytes, rc;

	if (argc != 1 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: readFile(path)");
		return -1;
	}
	buf = mprCreateBuf(ejs, MPR_BUF_INCR, MPR_MAX_BUF);
	if (buf == 0) {
		ejsMemoryError(ejs);
		return -1;
	}

	data = mprAlloc(ejs, MPR_BUFSIZE);
	if (buf == 0) {
		mprFree(buf);
		ejsMemoryError(ejs);
		return -1;
	}

	app = mprGetApp(ejs);
	file = mprOpen(ejs, argv[0]->string, O_RDONLY, 0664);
	if (file == 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string);
		mprFree(buf);
		return -1;
	}

	rc = 0;
	while ((bytes = mprRead(file, data, MPR_BUFSIZE)) > 0) {
		if (mprPutBlockToBuf(buf, data, bytes) != bytes) {
			ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string);
			rc = -1;
			break;
		}
	}

	ejsSetReturnValueToBinaryString(ejs, (uchar*) mprGetBufStart(buf), 
		mprGetBufLength(buf));

	mprClose(file);
	mprFree(data);
	mprFree(buf);

	return rc;
}

/******************************************************************************/
/*
 *	function void remove(string path);
 */

static int removeProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	int		rc;

	if (argc != 1 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: remove(path)");
		return -1;
	}

	rc = unlink(argv[0]->string);
	if (rc < 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant remove file");
		return -1;
	}
	return 0;
}

/******************************************************************************/
/*
 *	function void rename(string from, string to);
 */

static int renameProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	int		rc;

	if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: rename(old, new)");
		return -1;
	}

	unlink(argv[1]->string);
	rc = rename(argv[0]->string, argv[1]->string);
	if (rc < 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant rename file");
		return -1;
	}
	return 0;
}

/******************************************************************************/
/*
 *	function void copy(string old, string new);
 */

static int copyProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	MprFile		*from, *to;
	char		*buf;
	uint		bytes;
	int			rc;

	if (argc != 2 || !ejsVarIsString(argv[0]) || !ejsVarIsString(argv[1])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: copy(old, new)");
		return -1;
	}

	buf = mprAlloc(ejs, MPR_BUFSIZE);
	if (buf == 0) {
		ejsMemoryError(ejs);
		return -1;
	}

	from = mprOpen(ejs, argv[0]->string, O_RDONLY | O_BINARY, 0664);
	if (from == 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant open %s", argv[0]->string);
		mprFree(buf);
		return -1;
	}

	to = mprOpen(ejs, argv[1]->string, O_CREAT | O_BINARY, 0664);
	if (to == 0) {
		ejsError(ejs, EJS_IO_ERROR, "Cant create %s", argv[1]->string);
		mprClose(from);
		mprFree(buf);
		return -1;
	}

	rc = 0;
	while ((bytes = mprRead(from, buf, MPR_BUFSIZE)) > 0) {
		if (mprWrite(to, buf, bytes) != bytes) {
			ejsError(ejs, EJS_IO_ERROR, "Write error to %s", argv[1]->string);
			rc = -1;
			break;
		}
	}

	mprClose(from);
	mprClose(to);
	mprFree(buf);

	return rc;
}

/******************************************************************************/
/*
 *	function FileInfo getFileInfo(string path);
 *
 *	MOB -- should create a real class FileInfo
 */

static int getFileInfoProc(Ejs *ejs, EjsVar *thisObj, int argc, EjsVar **argv)
{
	MprFileInfo	info;
	EjsVar		*fileInfo;
	int			rc;

	if (argc != 1 || !ejsVarIsString(argv[0])) {
		ejsError(ejs, EJS_ARG_ERROR, "Bad usage: getFileInfo(path)");
		return -1;
	}

	fileInfo = ejsCreateObjVar(ejs);
	if (fileInfo == 0) {
		ejsMemoryError(ejs);
		return -1;
	}
	ejsMakeObjPermanent(fileInfo, 1);

	rc = mprGetFileInfo(ejs, argv[0]->string, &info);
	if (rc < 0) {
		ejsMakeObjPermanent(fileInfo, 0);
		ejsFreeVar(ejs, fileInfo);
		ejsError(ejs, EJS_IO_ERROR, "Cant get file info for %s",
			argv[0]->string);
		return -1;
	}

	ejsSetPropertyToInteger(ejs, fileInfo, "created", info.ctime);
	ejsSetPropertyToInteger(ejs, fileInfo, "length", info.size);
	ejsSetPropertyToBoolean(ejs, fileInfo, "isDir", info.isDir);

	ejsSetReturnValue(ejs, fileInfo);
	ejsMakeObjPermanent(fileInfo, 0);

	return 0;
}

/******************************************************************************/
/******************************** Initialization ******************************/
/******************************************************************************/

int ejsDefineFileSystemClass(Ejs *ejs)
{
	EjsVar	*fileSystemClass;

	fileSystemClass = ejsDefineClass(ejs, "FileSystem", "Object", 0);
	if (fileSystemClass == 0) {
		return MPR_ERR_CANT_INITIALIZE;
	}

	/*
	 *	Define the methods
	 */
	ejsDefineCMethod(ejs, fileSystemClass, "access", accessProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "mkdir", mkdirProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "rmdir", rmdirProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "dirList", dirListProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "writeFile", writeFileProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "readFile", readFileProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "remove", removeProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "rename", renameProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "copy", copyProc, 0);
	ejsDefineCMethod(ejs, fileSystemClass, "getFileInfo", getFileInfoProc, 0);

	//	MOB -- should be a property with accessor
	ejsDefineCMethod(ejs, fileSystemClass, "getFreeSpace", getFreeSpaceProc, 0);

	return ejsObjHasErrors(fileSystemClass) ? MPR_ERR_CANT_INITIALIZE: 0;
}

/******************************************************************************/

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim:tw=78
 * vim600: sw=4 ts=4 fdm=marker
 * vim<600: sw=4 ts=4
 */
