//
// Object_Iterator
//

#include "ascript/Object_Iterator.h"
#include "ascript/Object_Function.h"
#include "ascript/Object_List.h"
#include "ascript/Expr.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Iterator
//-----------------------------------------------------------------------------
Object_Iterator::Object_Iterator(const Object_Iterator &obj) :
							Object(obj), _pIterator(obj._pIterator->Clone())
{
}

Object_Iterator::~Object_Iterator()
{
	Iterator::Delete(_pIterator);
}

Object *Object_Iterator::Clone() const
{
	return new Object_Iterator(*this);
}

Iterator *Object_Iterator::CreateIterator(Signal sig)
{
	return _pIterator->Clone();
}

String Object_Iterator::ToString(Signal sig, bool exprFlag)
{
	return _pIterator->ToString(sig);
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Iterator
//-----------------------------------------------------------------------------
// iterator#print(stream?:stream)
AScript_DeclareMethod(Iterator, print)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, print)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Stream *pConsole = args.IsInstanceOf(0, VTYPE_Stream)?
				&args.GetStream(0) : env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value value;
	while (pIterator->Next(env, sig, value)) {
		String str(value.ToString(sig, false));
		if (sig.IsSignalled()) return Value::Null;
		pConsole->Print(sig, str.c_str());
		if (sig.IsSignalled()) return Value::Null;
	}
	return Value::Null;
}

// iterator#println(stream?:stream)
AScript_DeclareMethod(Iterator, println)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, println)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Stream *pConsole = args.IsInstanceOf(0, VTYPE_Stream)?
				&args.GetStream(0) : env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value value;
	while (pIterator->Next(env, sig, value)) {
		String str(value.ToString(sig, false));
		if (sig.IsSignalled()) return Value::Null;
		pConsole->Println(sig, str.c_str());
		if (sig.IsSignalled()) return Value::Null;
	}
	return Value::Null;
}

// iterator#printf(format:string, stream?:stream)
AScript_DeclareMethod(Iterator, printf)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareArg(env, "stream", VTYPE_Stream, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, printf)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	const char *format = args.GetString(0);
	Stream *pConsole = args.IsInstanceOf(1, VTYPE_Stream)?
				&args.GetStream(1) : env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value value;
	while (pIterator->Next(env, sig, value)) {
		if (value.IsList()) {
			pConsole->Printf(sig, format, value.GetList());
		} else {
			pConsole->Printf(sig, format, ValueList(value));
		}
		if (sig.IsSignalled()) return Value::Null;
	}
	return Value::Null;
}

// iterator#delay(delay:number) {block?}
AScript_DeclareMethod(Iterator, delay)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "delay", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, delay)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator =
		new Iterator_Delay(pSelf->GetIterator()->Clone(), args.GetNumber(0));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#isinfinite()
AScript_DeclareMethod(Iterator, isinfinite)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Iterator, isinfinite)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	return Value(pSelf->GetIterator()->IsInfinite());
}

// iterator#next()
AScript_DeclareMethod(Iterator, next)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Iterator, next)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Value value;
	if (pSelf->GetIterator()->Next(env, sig, value)) return value;
	return Value::Null;
}

// iterator#len()
AScript_DeclareMethod(Iterator, len)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns the length of the iterator.");
}

AScript_ImplementMethod(Iterator, len)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Value value;
	int cnt;
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	for (cnt = 0; pIterator->Next(env, sig, value); cnt++) ;
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return Value(static_cast<Number>(cnt));
}

// iterator#min():[index,last_index,indices]
AScript_DeclareMethod(Iterator, min)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_Symbol(index));
	DeclareAttr(AScript_Symbol(last_index));
	DeclareAttr(AScript_Symbol(indices));
	SetHelp(
	"Returns the minimum value in the iterator when no attribute is specified.\n"
	"With an attribute :index, it returns an index of the minimum value.\n"
	"With an attribute :last_index, it returns the last index of the minimum value\n"
	"when more than one elements have the same value.\n"
	"With an attribute :indices, it returns a list of indices of elements that\n"
	"has the minimum value.");
}

AScript_ImplementMethod(Iterator, min)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value result = pIterator->MinMax(env, sig, false, args.GetAttrs());
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#max():[index,last_index,indices]
AScript_DeclareMethod(Iterator, max)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareAttr(AScript_Symbol(index));
	DeclareAttr(AScript_Symbol(last_index));
	DeclareAttr(AScript_Symbol(indices));
	SetHelp(
	"Returns the maximum value in the iterator when no attribute is specified.\n"
	"With an attribute :index, it returns an index of the maximum value.\n"
	"With an attribute :last_index, it returns the last index of the maximum value\n"
	"when more than one elements have the same value.\n"
	"With an attribute :indices, it returns a list of indices of elements that\n"
	"has the maximum value.");
}

AScript_ImplementMethod(Iterator, max)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value result = pIterator->MinMax(env, sig, true, args.GetAttrs());
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#sum()
AScript_DeclareMethod(Iterator, sum)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns a sum of values in the iterator.");
}

AScript_ImplementMethod(Iterator, sum)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->Sum(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#average()
AScript_DeclareMethod(Iterator, average)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns an average of values in the iterator.");
}

AScript_ImplementMethod(Iterator, average)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->Average(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#variance()
AScript_DeclareMethod(Iterator, variance)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns a variance of values in the iterator.");
}

AScript_ImplementMethod(Iterator, variance)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->Variance(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#stddev()
AScript_DeclareMethod(Iterator, stddev)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns a standard deviation of values in the iterator.");
}

AScript_ImplementMethod(Iterator, stddev)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	size_t cnt;
	Value result = pIterator->StandardDeviation(env, sig, cnt);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#and()
AScript_DeclareMethodAlias(Iterator, and_, "and")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Iterator, and_)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pIterator->And(env, sig);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#or()
AScript_DeclareMethodAlias(Iterator, or_, "or")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Iterator, or_)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pIterator->Or(env, sig);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#iscontain(value)
AScript_DeclareMethod(Iterator, iscontain)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Any);
	SetHelp(
	"Returns true if a specified value exists in the iterated elements.");
}

AScript_ImplementMethod(Iterator, iscontain)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->CreateIterator(sig);
	if (sig.IsSignalled()) return Value::Null;
	bool result = pIterator->IsContain(env, sig, args.GetValue(0));
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return Value(result);
}

// iterator#filter(criteria) {block?}
AScript_DeclareMethod(Iterator, filter)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, filter)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->GetIterator()->Clone();
	Iterator *pIterator = pIteratorSrc->Filter(env, sig, args.GetValue(0));
	if (sig.IsSignalled()) return Value::Null;
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#while(criteria) {block?}
AScript_DeclareMethodAlias(Iterator, while_, "while")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, while_)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->GetIterator()->Clone();
	Iterator *pIterator = pIteratorSrc->While(env, sig, args.GetValue(0));
	if (sig.IsSignalled()) return Value::Null;
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#since(criteria) {block?}
AScript_DeclareMethod(Iterator, since)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, since)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->GetIterator()->Clone();
	Iterator *pIterator = pIteratorSrc->Since(env, sig, args.GetValue(0));
	if (sig.IsSignalled()) return Value::Null;
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#map(func:Function) {block?}
AScript_DeclareMethod(Iterator, map)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "func", VTYPE_Function);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, map)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = new Iterator_ExplicitMap(env,
			pSelf->GetIterator()->Clone(),
			Object_Function::Reference(args.GetFunctionObj(0)));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#reduce(accum) {block}
AScript_DeclareMethod(Iterator, reduce)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "accum", VTYPE_Any);
	DeclareBlock(OCCUR_Once);
}

AScript_ImplementMethod(Iterator, reduce)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
	if (pFuncBlock == NULL) {
		Iterator::Delete(pIterator);
		return Value::Null;
	}
	Value result = pIterator->Reduce(env, sig, args.GetValue(0), pFuncBlock);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// iterator#count(criteria)
AScript_DeclareMethod(Iterator, count)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "criteria", VTYPE_Any);
}

AScript_ImplementMethod(Iterator, count)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	size_t cnt = pIterator->Count(env, sig, args.GetValue(0));
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return Value(static_cast<unsigned int>(cnt));
}

// iterator#sort(directive?, keys[]?):[stable] {block?}
AScript_DeclareMethod(Iterator, sort)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "directive", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareArg(env, "keys", VTYPE_Any, OCCUR_ZeroOrOnce, true);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, sort)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->GetIterator()->Clone();
	Value value = pIteratorSrc->Eval(env, sig, args);
	Iterator::Delete(pIteratorSrc);
	if (sig.IsSignalled() || value.IsInvalid()) return Value::Null;
	ASSUME(env, value.IsList());
	Object_List *pObj = value.GetListObj()->SortRank(sig, args.GetValue(0),
						args.IsList(1)? &args.GetList(1) : NULL,
						false, args.IsSet(AScript_Symbol(stable)));
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#rank(directive?):[stable] {block?}
AScript_DeclareMethod(Iterator, rank)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "directive", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, rank)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->GetIterator()->Clone();
	Value value = pIteratorSrc->Eval(env, sig, args);
	Iterator::Delete(pIteratorSrc);
	if (sig.IsSignalled() || value.IsInvalid()) return Value::Null;
	ASSUME(env, value.IsList());
	Object_List *pObj = value.GetListObj()->SortRank(sig, args.GetValue(0), NULL,
							true, args.IsSet(AScript_Symbol(stable)));
	if (sig.IsSignalled()) return Value::Null;
	Iterator *pIterator = new Object_List::IteratorEach(pObj);
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#join(sep?:string)
AScript_DeclareMethod(Iterator, join)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "sep", VTYPE_String, OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, join)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	const char *sep = args.IsString(0)? args.GetString(0) : "";
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	String rtn;
	Value value;
	if (pIterator->Next(env, sig, value)) {
		rtn += value.ToString(sig, false);
		if (sig.IsSignalled()) {
			Iterator::Delete(pIterator);
			return Value::Null;
		}
		while (pIterator->Next(env, sig, value)) {
			rtn += sep;
			rtn += value.ToString(sig, false);
			if (sig.IsSignalled()) {
				Iterator::Delete(pIterator);
				return Value::Null;
			}
		}
	}
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	return Value(env, rtn.c_str());
}

// iterator#joinb()
AScript_DeclareMethod(Iterator, joinb)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Iterator, joinb)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	const char *sep = args.IsString(0)? args.GetString(0) : "";
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Binary buff;
	Value value;
	while (pIterator->Next(env, sig, value)) {
		if (!value.IsBinary()) {
			sig.SetError(ERR_ValueError, "invalid value type");
			Iterator::Delete(pIterator);
			return Value::Null;
		}
		buff += value.GetBinary();
	}
	Iterator::Delete(pIterator);
	if (sig.IsSignalled()) return Value::Null;
	Value result;
	result.InitAsBinary(env, buff);
	return result;
}

// iterator#format(format:string) {block?}
AScript_DeclareMethod(Iterator, format)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, format)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator =
		new Iterator_Format(pSelf->GetIterator()->Clone(), args.GetString(0));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#pack(format:string) {block?}
AScript_DeclareMethod(Iterator, pack)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_String);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, pack)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator =
		new Iterator_Pack(pSelf->GetIterator()->Clone(), args.GetString(0));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#each() {block?}
AScript_DeclareMethod(Iterator, each)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, each)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#offset(n:number) {block?}
AScript_DeclareMethod(Iterator, offset)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, offset)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	for (int cnt = static_cast<int>(args.GetNumber(0)); cnt > 0; cnt--) {
		Value value;
		pIterator->Next(env, sig, value);
	}
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#align(n:number, value?) {block?}
AScript_DeclareMethod(Iterator, align)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, align)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = new Iterator_Align(pSelf->GetIterator()->Clone(),
			static_cast<int>(args.GetNumber(0)), args.GetValue(1));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#skip(n:number) {block?}
AScript_DeclareMethod(Iterator, skip)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, skip)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator =
		new Iterator_Skip(pSelf->GetIterator()->Clone(), static_cast<int>(args.GetNumber(0)));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#skipnil() {block?}
AScript_DeclareMethod(Iterator, skipnil)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, skipnil)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = new Iterator_SkipInvalid(pSelf->GetIterator()->Clone());
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#nilto(replace) {block?}
AScript_DeclareMethod(Iterator, nilto)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "replace", VTYPE_Any);
}

AScript_ImplementMethod(Iterator, nilto)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = new Iterator_ReplaceInvalid(pSelf->GetIterator()->Clone(),
															args.GetValue(0));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#replace(value, replace) {block?}
AScript_DeclareMethod(Iterator, replace)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Any);
	DeclareArg(env, "replace", VTYPE_Any);
}

AScript_ImplementMethod(Iterator, replace)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = new Iterator_Replace(pSelf->GetIterator()->Clone(),
									args.GetValue(0), args.GetValue(1));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#head(n:number):map {block?}
AScript_DeclareMethod(Iterator, head)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, head)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = new Iterator_Head(pSelf->GetIterator()->Clone(),
									static_cast<int>(args.GetNumber(0)));
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#tail(n:number) {block?}
AScript_DeclareMethod(Iterator, tail)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, tail)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIteratorSrc = pSelf->GetIterator()->Clone();
	Value value = pIteratorSrc->Eval(env, sig, args);
	Iterator::Delete(pIteratorSrc);
	if (sig.IsSignalled() || value.IsInvalid()) return Value::Null;
	ASSUME(env, value.IsList());
	Object_List *pObj = dynamic_cast<Object_List *>(value.GetListObj()->Clone());
	int cnt = args.GetInt(0);
	int cntMax = static_cast<int>(pObj->GetList().size());
	size_t offset = (cntMax > cnt)? cntMax - cnt : cntMax;
	Iterator *pIterator = new Object_List::IteratorEach(pObj, offset);
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator#reverse() {block?}
AScript_DeclareMethod(Iterator, reverse)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, reverse)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value value = pIterator->Eval(env, sig, args);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled() || value.IsInvalid()) return Value::Null;
	ASSUME(env, value.IsList());
	Object_List *pObj = dynamic_cast<Object_List *>(value.GetListObj()->Clone());
	return ReturnIterator(env, sig, args,
							new Object_List::IteratorReverse(pObj));
}

// iterator#round(n?:number) {block?}
AScript_DeclareMethod(Iterator, round)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Iterator, round)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	int cnt = args.IsNumber(0)? args.GetInt(0) : -1;
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value value = pIterator->Eval(env, sig, args);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled() || value.IsInvalid()) return Value::Null;
	ASSUME(env, value.IsList());
	Object_List *pObj = dynamic_cast<Object_List *>(value.GetListObj()->Clone());
	return ReturnIterator(env, sig, args,
							new Object_List::IteratorRound(pObj, cnt));
}

// iterator#pingpong(n?:number):[sticky,sticky_l,sticky_r] {block?}
AScript_DeclareMethod(Iterator, pingpong)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(sticky));
	DeclareAttr(AScript_Symbol(sticky_l));
	DeclareAttr(AScript_Symbol(sticky_r));
}

AScript_ImplementMethod(Iterator, pingpong)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	int cnt = args.IsNumber(0)? args.GetInt(0) : -1;
	bool stickyFlagL = args.IsSet(AScript_Symbol(sticky)) ||
						args.IsSet(AScript_Symbol(sticky_l));
	bool stickyFlagR = args.IsSet(AScript_Symbol(sticky)) ||
						args.IsSet(AScript_Symbol(sticky_r));
	Iterator *pIterator = pSelf->GetIterator()->Clone();
	Value value = pIterator->Eval(env, sig, args);
	Iterator::Delete(pIterator);
	if (sig.IsSignalled() || value.IsInvalid()) return Value::Null;
	ASSUME(env, value.IsList());
	Object_List *pObj = dynamic_cast<Object_List *>(value.GetListObj()->Clone());
	return ReturnIterator(env, sig, args,
			new Object_List::IteratorPingpong(pObj, cnt, stickyFlagL, stickyFlagR));
}

// iterator#fold(n:number):[iteritem] {block?}
AScript_DeclareMethod(Iterator, fold)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(iteritem));
}

AScript_ImplementMethod(Iterator, fold)
{
	Object_Iterator *pSelf = Object_Iterator::GetSelfObj(args);
	int cnt = static_cast<int>(args.GetNumber(0));
	bool listItemFlag = !args.IsSet(AScript_Symbol(iteritem));
	Iterator *pIterator = new Iterator_Fold(pSelf->GetIterator()->Clone(), cnt, listItemFlag);
	return ReturnIterator(env, sig, args, pIterator);
}

// assignment
Class_Iterator::Class_Iterator(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Iterator, isinfinite);
	AScript_AssignMethod(Iterator, next);
	AScript_AssignMethod(Iterator, print);
	AScript_AssignMethod(Iterator, println);
	AScript_AssignMethod(Iterator, printf);
	AScript_AssignMethod(Iterator, delay);
	// common operations with list
	AScript_AssignMethod(Iterator, len);
	AScript_AssignMethod(Iterator, min);
	AScript_AssignMethod(Iterator, max);
	AScript_AssignMethod(Iterator, sum);
	AScript_AssignMethod(Iterator, average);
	AScript_AssignMethod(Iterator, variance);
	AScript_AssignMethod(Iterator, stddev);
	AScript_AssignMethod(Iterator, and_);
	AScript_AssignMethod(Iterator, or_);
	AScript_AssignMethod(Iterator, iscontain);
	AScript_AssignMethod(Iterator, filter);
	AScript_AssignMethod(Iterator, while_);
	AScript_AssignMethod(Iterator, since);
	AScript_AssignMethod(Iterator, map);
	AScript_AssignMethod(Iterator, reduce);
	AScript_AssignMethod(Iterator, count);
	AScript_AssignMethod(Iterator, sort);
	AScript_AssignMethod(Iterator, rank);
	AScript_AssignMethod(Iterator, join);
	AScript_AssignMethod(Iterator, joinb);
	AScript_AssignMethod(Iterator, format);
	AScript_AssignMethod(Iterator, pack);
	AScript_AssignMethod(Iterator, each);
	AScript_AssignMethod(Iterator, offset);
	AScript_AssignMethod(Iterator, align);
	AScript_AssignMethod(Iterator, skip);
	AScript_AssignMethod(Iterator, skipnil);
	AScript_AssignMethod(Iterator, nilto);
	AScript_AssignMethod(Iterator, replace);
	AScript_AssignMethod(Iterator, head);
	AScript_AssignMethod(Iterator, tail);
	AScript_AssignMethod(Iterator, reverse);
	AScript_AssignMethod(Iterator, round);
	AScript_AssignMethod(Iterator, pingpong);
	AScript_AssignMethod(Iterator, fold);
}

bool Class_Iterator::CastFrom(Environment &env, Signal sig, Value &value)
{
	Iterator *pIterator = value.CreateIterator(sig);
	if (pIterator != NULL) {
		value = Value(env, pIterator);
		return true;
	}
	return false;
}

Object *Class_Iterator::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	ERROREND(env, "this function must not be called");
	return NULL;
}

}
