//
// Object_Dict
//
#include "ascript/Object_Dict.h"
#include "ascript/Expr.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Dict
//-----------------------------------------------------------------------------
Object *Object_Dict::Clone() const
{
	return new Object_Dict(*this);
}

Value Object_Dict::IndexGet(Signal sig, const Value &valueIdx)
{
	const Value *pValue = Find(sig, valueIdx);
	if (sig.IsSignalled()) {
		return Value::Null;
	} else if (pValue == NULL) {
		SetError_KeyNotFound(sig, valueIdx);
		return Value::Null;
	}
	return *pValue;
}

void Object_Dict::IndexSet(Signal sig, const Value &valueIdx, const Value &value)
{
	if (!ValueDict::IsValidKey(valueIdx)) {
		sig.SetError(ERR_KeyError, "invalid value type for key");
		return;
	}
	_valDict[valueIdx] = value;
}

Iterator *Object_Dict::CreateIterator(Signal sig)
{
	return new IteratorItems(Object_Dict::Reference(this));
}

String Object_Dict::ToString(Signal sig, bool exprFlag)
{
	bool limitLenFlag = true;
	String str;
	str += "%{";
	foreach_const (ValueDict, iter, _valDict) {
		if (iter != _valDict.begin()) str += ", ";
		if (limitLenFlag && str.size() > 4096) {
			str += "...";
			break;
		}
		const Value &value = iter->first;
		if (value.IsString()) {
			str += '"';
			str += value.GetString();
			str += '"';
		} else {
			str += value.ToString(sig, false);
		}
		str += " => ";
		str += iter->second.ToString(sig);
	}
	str += "}";
	return str;
}

const Value *Object_Dict::Find(Signal sig, const Value &valueIdx) const
{
	if (!ValueDict::IsValidKey(valueIdx)) {
		sig.SetError(ERR_KeyError, "invalid value type for key");
		return NULL;
	}
	ValueDict::const_iterator iter = _valDict.find(valueIdx);
	return (iter == _valDict.end())? NULL : &iter->second;
}

void Object_Dict::SetError_KeyNotFound(Signal sig, const Value &valueIdx)
{
	sig.SetError(ERR_KeyError, "dictionary doesn't have a key '%s'",
										valueIdx.ToString(sig).c_str());
}

//-----------------------------------------------------------------------------
// Object_Dict::IteratorKeys
//-----------------------------------------------------------------------------
Object_Dict::IteratorKeys::IteratorKeys(Object_Dict *pObj) :
				Iterator(false), _pObj(pObj), _pCur(pObj->GetDict().begin())
{
}

Object_Dict::IteratorKeys::~IteratorKeys()
{
	Object::Delete(_pObj);
}

bool Object_Dict::IteratorKeys::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_pCur == _pObj->GetDict().end()) return false;
	value = _pCur->first;
	_pCur++;
	return true;
}

String Object_Dict::IteratorKeys::ToString(Signal sig) const
{
	return String("<iterator:dict#keys>");
}

//-----------------------------------------------------------------------------
// Object_Dict::IteratorValues
//-----------------------------------------------------------------------------
Object_Dict::IteratorValues::IteratorValues(Object_Dict *pObj) :
				Iterator(false), _pObj(pObj), _pCur(pObj->GetDict().begin())
{
}

Object_Dict::IteratorValues::~IteratorValues()
{
	Object::Delete(_pObj);
}

bool Object_Dict::IteratorValues::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_pCur == _pObj->GetDict().end()) return false;
	value = _pCur->second;
	_pCur++;
	return true;
}

String Object_Dict::IteratorValues::ToString(Signal sig) const
{
	return String("<iterator:dict#values>");
}

//-----------------------------------------------------------------------------
// Object_Dict::IteratorItems
//-----------------------------------------------------------------------------
Object_Dict::IteratorItems::IteratorItems(Object_Dict *pObj) :
				Iterator(false), _pObj(pObj), _pCur(pObj->GetDict().begin())
{
}

Object_Dict::IteratorItems::~IteratorItems()
{
	Object::Delete(_pObj);
}

bool Object_Dict::IteratorItems::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_pCur == _pObj->GetDict().end()) return false;
	ValueList &valList = value.InitAsList(*_pObj);
	valList.push_back(_pCur->first);
	valList.push_back(_pCur->second);
	_pCur++;
	return true;
}

String Object_Dict::IteratorItems::ToString(Signal sig) const
{
	return String("<iterator:dict#items>");
}

//-----------------------------------------------------------------------------
// Object_Dict::IteratorGet
//-----------------------------------------------------------------------------
Object_Dict::IteratorGet::IteratorGet(Object_Dict *pObj, Iterator *pIteratorKey,
					const Value &valDefault, bool raiseFlag, bool setDefaultFlag) :
	Iterator(pIteratorKey->IsInfinite()), _pObj(pObj), _pIteratorKey(pIteratorKey),
	_valDefault(valDefault), _raiseFlag(raiseFlag), _setDefaultFlag(setDefaultFlag)
{
}

Object_Dict::IteratorGet::~IteratorGet()
{
	Object::Delete(_pObj);
	Iterator::Delete(_pIteratorKey);
}

bool Object_Dict::IteratorGet::DoNext(Environment &env, Signal sig, Value &value)
{
	Value valueIdx;
	if (!_pIteratorKey->Next(env, sig, valueIdx)) return false;
	const Value *pValue = _pObj->Find(sig, valueIdx);
	if (pValue != NULL) {
		value = *pValue;
	} else if (_raiseFlag) {
		Object_Dict::SetError_KeyNotFound(sig, valueIdx);
		return false;
	} else {
		value = _valDefault;
		if (_setDefaultFlag) _pObj->IndexSet(sig, valueIdx, value);
		if (sig.IsSignalled()) return false;
	}
	return true;
}

String Object_Dict::IteratorGet::ToString(Signal sig) const
{
	return String("<iterator:dict#get>");
}

//-----------------------------------------------------------------------------
// AScript Interface
//-----------------------------------------------------------------------------
// dict#len()
AScript_DeclareMethod(Dict, len)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Dict, len)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	return Value(static_cast<Number>(pSelf->GetDict().size()));
}

// dict#get(key, default?:nomap):map:[raise]
AScript_DeclareMethod(Dict, get)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "key", VTYPE_Any);
	DeclareArg(env, "default", VTYPE_Any, OCCUR_ZeroOrOnce, false, true);
	DeclareAttr(AScript_Symbol(raise));
}

AScript_ImplementMethod(Dict, get)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	const Value &valueIdx = args.GetValue(0);
	bool raiseFlag = args.IsSet(AScript_Symbol(raise));
	const Value *pValue = pSelf->Find(sig, valueIdx);
	if (pValue != NULL) {
		return *pValue;
	} else if (raiseFlag) {
		Object_Dict::SetError_KeyNotFound(sig, valueIdx);
		return Value::Null;
	} else {
		const Value &value = args.GetValue(1);
		return value;
	}
}

// dict#gets(key, default?):map:[raise]
AScript_DeclareMethod(Dict, gets)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "key", VTYPE_Any);
	DeclareArg(env, "default", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(raise));
}

AScript_ImplementMethod(Dict, gets)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	const Value &valueIdx = args.GetValue(0);
	bool raiseFlag = args.IsSet(AScript_Symbol(raise));
	const Value *pValue = pSelf->Find(sig, valueIdx);
	if (pValue != NULL) {
		return *pValue;
	} else if (raiseFlag) {
		Object_Dict::SetError_KeyNotFound(sig, valueIdx);
		return Value::Null;
	} else {
		const Value &value = args.GetValue(1);
		return value;
	}
}

// dict#set(key, value:nomap):map:void
AScript_DeclareMethod(Dict, set)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "key", VTYPE_Any);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_Once, false, true);
}

AScript_ImplementMethod(Dict, set)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	const Value &valueIdx = args.GetValue(0);
	pSelf->IndexSet(sig, args.GetValue(0), args.GetValue(1));
	return Value::Null;
}

// dict#sets(key, value):map:void
AScript_DeclareMethod(Dict, sets)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "key", VTYPE_Any);
	DeclareArg(env, "value", VTYPE_Any);
}

AScript_ImplementMethod(Dict, sets)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	const Value &valueIdx = args.GetValue(0);
	pSelf->IndexSet(sig, args.GetValue(0), args.GetValue(1));
	return Value::Null;
}

// dict#store(elems?):reduce {block?}
AScript_DeclareMethod(Dict, store)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "elems", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Dict, store)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	ValueDict &valDict = pSelf->GetDict();
	bool allowDupFlag = true;
	if (args.GetValue(0).IsList()) {
		if (!valDict.Store(sig, args.GetList(0), allowDupFlag)) {
			return Value::Null;
		}
	} else if (args.GetValue(0).IsDict()) {
		if (!valDict.Store(sig, args.GetDict(0), allowDupFlag)) {
			return Value::Null;
		}
	} else if (args.IsValid(0)) {
		sig.SetError(ERR_ValueError, "invalid argument type");
		return Value::Null;
	}
	if (args.IsBlockSpecified()) {
		const Expr_Block *pExprBlock = args.GetBlock(env, sig);
		if (sig.IsSignalled()) return Value::Null;
		Environment envLister(&env, ENVTYPE_Lister);
		Value valueList =
				pExprBlock->GetExprList().ExecForList(envLister, sig, false, false);
		if (sig.IsSignalled() || !valueList.IsList()) return Value::Null;
		if (!valDict.Store(sig, valueList.GetList(), true)) return Value::Null;
	}
	return args.GetSelf();
}

// dict#setdefault(key, default)
AScript_DeclareMethod(Dict, setdefault)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "key", VTYPE_Any);
	DeclareArg(env, "default", VTYPE_Any);
}

AScript_ImplementMethod(Dict, setdefault)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	const Value &valueArg = args.GetValue(0);
	if (valueArg.IsList() || valueArg.IsIterator()) {
		Iterator *pIteratorKey = valueArg.CreateIterator(sig);
		if (sig.IsSignalled()) return Value::Null;
		Iterator *pIterator = new Object_Dict::IteratorGet(
						Object_Dict::Reference(pSelf),
						pIteratorKey, args.GetValue(1), false, true);
		return ReturnIterator(env, sig, args, pIterator);
	} else {
		const Value &valueIdx = valueArg;
		const Value *pValue = pSelf->Find(sig, valueIdx);
		if (pValue != NULL) {
			return *pValue;
		} else {
			const Value &value = args.GetValue(1);
			pSelf->IndexSet(sig, valueIdx, value);
			if (sig.IsSignalled()) return Value::Null;
			return value;
		}
	}
}

// dict#haskey(key):map
AScript_DeclareMethod(Dict, haskey)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "key", VTYPE_Any);
}

AScript_ImplementMethod(Dict, haskey)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	const Value &valueIdx = args.GetValue(0);
	const Value *pValue = pSelf->Find(sig, valueIdx);
	return Value(pValue != NULL);
}

// dict#keys() {block?}
AScript_DeclareMethod(Dict, keys)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Dict, keys)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	Object_Dict *pObj = Object_Dict::Reference(pSelf);
	return ReturnIterator(env, sig, args,
							new Object_Dict::IteratorKeys(pObj));
}

// dict#values() {block?}
AScript_DeclareMethod(Dict, values)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Dict, values)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	Object_Dict *pObj = Object_Dict::Reference(pSelf);
	return ReturnIterator(env, sig, args,
							new Object_Dict::IteratorValues(pObj));
}

// dict#items() {block?}
AScript_DeclareMethod(Dict, items)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementMethod(Dict, items)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	Object_Dict *pObj = Object_Dict::Reference(pSelf);
	return ReturnIterator(env, sig, args,
							new Object_Dict::IteratorItems(pObj));
}

// dict#clear()
AScript_DeclareMethod(Dict, clear)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Dict, clear)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	pSelf->GetDict().clear();
	return Value::Null;
}

// dict#erase(key):map
AScript_DeclareMethod(Dict, erase)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "key", VTYPE_Any);
}

AScript_ImplementMethod(Dict, erase)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(args);
	pSelf->GetDict().erase(args.GetValue(0));
	return Value::Null;
}

// Assignment
Class_Dict::Class_Dict(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Dict, len);
	AScript_AssignMethod(Dict, get);
	AScript_AssignMethod(Dict, gets);
	AScript_AssignMethod(Dict, set);
	AScript_AssignMethod(Dict, sets);
	AScript_AssignMethod(Dict, store);
	AScript_AssignMethod(Dict, setdefault);
	AScript_AssignMethod(Dict, haskey);
	AScript_AssignMethod(Dict, keys);
	AScript_AssignMethod(Dict, values);
	AScript_AssignMethod(Dict, items);
	AScript_AssignMethod(Dict, clear);
	AScript_AssignMethod(Dict, erase);
}

Object *Class_Dict::CreateDescendant(Environment &env, Signal sig, Class *pClass)
{
	return new Object_Dict((pClass == NULL)? this : pClass);
}

}
