//-----------------------------------------------------------------------------
// AScript os module
//-----------------------------------------------------------------------------
#include "Module_os.h"

AScript_BeginModule(os)

AScript_BeginSubModule(path)

// os.path.match(pattern:string, filename:string):map
AScript_DeclareFunction(match)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pattern"), VTYPE_String);
	DeclareArg(env, _T("filename"), VTYPE_String);
	DeclareAttr(AScript_Symbol(icase));
}

AScript_ImplementFunction(match)
{
	const TCHAR *pattern = context.GetString(0);
	const TCHAR *fileName = context.GetString(1);
	bool ignoreCaseFlag = context.IsSet(AScript_Symbol(icase));
	return Value(File::IsMatchName(pattern, fileName, ignoreCaseFlag));
}

// os.path.split(pathname:string):map
AScript_DeclareFunction(split)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pathname"), VTYPE_String);
}

AScript_ImplementFunction(split)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	valList.push_back(Value(env, File::ExtractDirName(context.GetString(0)).c_str()));
	valList.push_back(Value(env, File::ExtractBaseName(context.GetString(0)).c_str()));
	return result;
}

// os.path.basename(pathname:string):map
AScript_DeclareFunction(basename)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pathname"), VTYPE_String);
}

AScript_ImplementFunction(basename)
{
	return Value(env, File::ExtractBaseName(context.GetString(0)).c_str());
}

// os.path.dirname():map
AScript_DeclareFunction(dirname)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pathname"), VTYPE_String);
}

AScript_ImplementFunction(dirname)
{
	return Value(env, File::ExtractDirName(context.GetString(0)).c_str());
}

// os.path.abspath(filename:string):map
AScript_DeclareFunction(absname)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("filename"), VTYPE_String);
}

AScript_ImplementFunction(absname)
{
	return Value(env, File::MakeAbsPath(context.GetString(0)).c_str());
}

// os.path.join(path+:string):map
AScript_DeclareFunction(join)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("path"), VTYPE_String, false, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(join)
{
	String str;
	const ValueList &valList = context.GetList(0);
	foreach_const (ValueList, pValue, valList) {
		if (pValue != valList.begin()) str += File::Separator;
		str += pValue->GetString();
	}
	return Value(env, str.c_str());
}

// os.path.exists(pathname:string):map
AScript_DeclareFunction(exists)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pathname"), VTYPE_String);
}

AScript_ImplementFunction(exists)
{
	return Value(File::IsExist(context.GetString(0)));
}

AScript_ModuleEntry()
{
	AScript_AssignFunction(match);
	AScript_AssignFunction(split);
	AScript_AssignFunction(dirname);
	AScript_AssignFunction(basename);
	AScript_AssignFunction(absname);
	AScript_AssignFunction(join);
	AScript_AssignFunction(exists);
}

AScript_EndSubModule(path)

// os.listdir(pathname:string):map {block?}
AScript_DeclareFunction(listdir)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pathname"), VTYPE_String);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(listdir)
{
	File::EntryList entries;
	if (!File::ListDir(sig, context.GetString(0), NULL, false, entries)) {
		return Value::Null;
	}
	Value result;
	ResultListComposer resultListComposer(env, context, result);
	// pFuncBlock is set to NULL without error if block is not specified.
	const Function *pFuncBlock = GetBlockFunction(env, sig, context);
	if (sig.IsSignalled()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	foreach (File::EntryList, pEntry, entries) {
		Value value(env, pEntry->GetFileName());
		if (pFuncBlock != NULL) {
			size_t idx = pEntry - entries.begin();
			value = pFuncBlock->Eval(envBlock, sig,
					Context(ValueList(value, Value(static_cast<Number>(idx)),
												Value(pEntry->IsDirectory()))));
			AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
		}
		resultListComposer.Store(value);
	}
	return result;
}

// os.glob(pattern:string):map {block?}
AScript_DeclareFunction(glob)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pattern"), VTYPE_String);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(icase));
}

AScript_ImplementFunction(glob)
{
	File::EntryList entries;
	bool ignoreCaseFlag = context.IsSet(AScript_Symbol(icase));
	File::Glob(sig, context.GetString(0), ignoreCaseFlag, entries);
	if (sig.IsSignalled()) return Value::Null;
	Value result;
	ResultListComposer resultListComposer(env, context, result);
	// pFuncBlock is set to NULL without error if block is not specified.
	const Function *pFuncBlock = GetBlockFunction(env, sig, context);
	if (sig.IsSignalled()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	foreach (File::EntryList, pEntry, entries) {
		Value value(env, pEntry->GetFileName());
		if (pFuncBlock != NULL) {
			size_t idx = pEntry - entries.begin();
			value = pFuncBlock->Eval(envBlock, sig,
					Context(ValueList(value, Value(static_cast<Number>(idx)),
												Value(pEntry->IsDirectory()))));
			AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
		}
		resultListComposer.Store(value);
	}
	return result;
}

// os.walk(pathname:string):map {block?}
AScript_DeclareFunctionBegin(walk)
private:
	static bool DoWalk(Environment &envBlock, Signal sig, const Function *pFuncBlock,
		const TCHAR *pathName, ResultListComposer &resultListComposer, int &index);
AScript_DeclareFunctionEnd(walk)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pathname"), VTYPE_String);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(walk)
{
	const Function *pFuncBlock = GetBlockFunction(env, sig, context);
	if (sig.IsSignalled()) return false;
	// pFuncBlock is set to NULL without error if block is not specified.
	Environment envBlock(&env, ENVTYPE_Block);
	Value result;
	ResultListComposer resultListComposer(envBlock, context, result);
	int index = 0;
	DoWalk(env, sig, pFuncBlock, context.GetString(0), resultListComposer, index);
	return result;
}

bool AScript_Function(walk)::DoWalk(Environment &envBlock, Signal sig, const Function *pFuncBlock,
	const TCHAR *pathName, ResultListComposer &resultListComposer, int &index)
{
	File::EntryList entries;
	if (!File::ListDir(sig, pathName, NULL, false, entries)) return false;
	foreach (File::EntryList, pEntry, entries) {
		String pathNameEntry = pathName;
		pathNameEntry += File::Separator;
		pathNameEntry += pEntry->GetFileName();
		if (pEntry->IsDirectory()) {
			if (!DoWalk(envBlock, sig, pFuncBlock,
						pathNameEntry.c_str(), resultListComposer, index)) {
				return false;
			}
		} else {
			
			Value value(envBlock, File::MakeAbsPath(pathNameEntry.c_str()).c_str());
			if (pFuncBlock != NULL) {
				value = pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(value, Value(static_cast<Number>(index)))));
				AScript_BlockSignalHandlerInLoop(sig, value, false)
			}
			resultListComposer.Store(value);
			index++;
		}
	}
	return true;
}

// os.stat(pathname:string):map
AScript_DeclareFunction(stat)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("pathname"), VTYPE_String);
}

AScript_ImplementFunction(stat)
{
	FileStat fileStat = File::GetFileStat(sig, context.GetString(0));
	if (sig.IsSignalled()) return Value::Null;
	Object_FileStat *pObj = new Object_FileStat(env.GetClass_FileStat(), fileStat);
	return Value(pObj, VTYPE_FileStat);
}

// Module entry
AScript_ModuleEntry()
{
	do {
		TCHAR str[2];
		str[0] = File::Separator;
		str[1] = _T('\0');
		AScript_AssignValue(Symbol::Add(_T("sep")), Value(env, str));
	} while (0);
	AScript_AssignFunction(listdir);
	AScript_AssignFunction(glob);
	AScript_AssignFunction(walk);
	AScript_AssignFunction(stat);
	AScript_AssignSubModule(path);
}

AScript_EndModule(os)

AScript_DLLModuleEntry(os)
