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

namespace AScript {

// Implementation of Object_Dict
bool Object_Dict::IsDict() const { return true; }

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

Value Object_Dict::GetByIndex(Signal sig, const Value &valueIdx) const
{
	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::SetByIndex(Signal sig, const Value &valueIdx, const Value &value)
{
	if (!ValueDict::IsValidKey(valueIdx)) {
		sig.SetError(ERR_KeyError, _T("invalid value type for key"));
		return;
	}
	_valDict[valueIdx] = value;
}

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

const Value *Object_Dict::Find(Signal sig, const Value &valueIdx) const
{
	if (!ValueDict::IsValidKey(valueIdx)) {
		sig.SetError(ERR_KeyError, _T("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, _T("dictionary doesn't have a key '%s'"),
										valueIdx.ToString(sig).c_str());
}

// Constructor: Dict(elem[]?) {block?}
Object_Dict::Constructor::Constructor(Environment &env, const TCHAR *name) :
							ConstructorBase(name, env.GetClass_Dict())
{
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

Value Object_Dict::Constructor::DoEval(Environment &env, Signal sig, Context &context) const
{
	Object_Dict *pObj = new Object_Dict(env.GetClass_Dict());
	Value result(pObj, VTYPE_Dict);
	ValueDict &valDict = pObj->GetDict();
	if (context.GetValue(0).IsList()) {
		if (!valDict.Store(sig, context.GetList(0))) return Value::Null;
	}
	if (context.IsBlockSpecified()) {
		Environment envLister(&env, ENVTYPE_Lister);
		Value valueList = context.GetBlock()->GetExprList().ExecForList(envLister, sig, false);
		if (sig.IsSignalled() || !valueList.IsList()) return Value::Null;
		if (!valDict.Store(sig, valueList.GetList())) return Value::Null;
	}
	return result;
}

// Dict#count()
AScript_DeclareMethod(Dict, count)
{
}

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

// Dict#get(key, default?):map:[raise]
AScript_DeclareMethod(Dict, get)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("key"), VTYPE_AnyType);
	DeclareArg(env, _T("default"), VTYPE_AnyType, false, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(raise));
}

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

// Dict#has_key(key):map
AScript_DeclareMethod(Dict, has_key)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("key"), VTYPE_AnyType);
}

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

// Dict#keys()
AScript_DeclareMethod(Dict, keys)
{
}

AScript_ImplementMethod(Dict, keys)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(context);
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueDict, iter, pSelf->GetDict()) {
		valList.push_back(iter->first);
	}
	return result;
}

// Dict#pairs()
AScript_DeclareMethod(Dict, pairs)
{
}

AScript_ImplementMethod(Dict, pairs)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(context);
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueDict, iter, pSelf->GetDict()) {
		Value resultElem;
		ValueList &valListElem = resultElem.InitAsList(env);
		valListElem.push_back(iter->first);
		valListElem.push_back(iter->second);
		valList.push_back(resultElem);
	}
	return result;
}

// Dict#clear!()
AScript_DeclareMethod(Dict, clear_X)
{
}

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

// Dict#add!(elem[])
AScript_DeclareMethod(Dict, add_X)
{
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
}

AScript_ImplementMethod(Dict, add_X)
{
	Object_Dict *pSelf = Object_Dict::GetSelfObj(context);
	if (!pSelf->GetDict().Store(sig, context.GetList(0))) return Value::Null;
	return Value::Null;
}

// Dict#erase!(key)
AScript_DeclareMethod(Dict, erase_X)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("key"), VTYPE_AnyType);
}

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

// Assignment
Class_Dict::Class_Dict(Class *pClassSuper, const Symbol *pSymbol) :
												Class(pClassSuper, pSymbol)
{
	AScript_AssignMethod(Dict, count);
	AScript_AssignMethod(Dict, get);
	AScript_AssignMethod(Dict, has_key);
	AScript_AssignMethod(Dict, keys);
	AScript_AssignMethod(Dict, pairs);
	AScript_AssignMethodEx(Dict, clear_X, _T("clear!"));
	AScript_AssignMethodEx(Dict, add_X, _T("add!"));
	AScript_AssignMethodEx(Dict, erase_X, _T("erase!"));
}

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

}
