#include "AScript.h"
#include "Expr.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Value
//-----------------------------------------------------------------------------
const Complex Value::_compZero;
const Value Value::Null;

Value::Value(const Value &value) : _valType(value._valType), _ownFlag(value._ownFlag)
{
	if (value.IsNumber()) {
		_u.num = value._u.num;
		DBG(::_tprintf(_T("%p Value(Value as Number %g)\n"), this, _u.num));
	} else if (value.IsBoolean()) {
		_u.flag = value._u.flag;
		DBG(::_tprintf(_T("%p Value(Value as Boolean %d)\n"), this, _u.flag));
	} else if (value.IsSymbol()) {
		_u.pSymbol = value._u.pSymbol;
		DBG(::_tprintf(_T("%p Value(Value as Symbol %s)\n"), this, _u.pSymbol->GetName()));
	} else if (value.IsComplex()) {
		_u.pComp = DupComplex(*value._u.pComp);
		DBG(::_tprintf(_T("%p Value(Value as Complex (%g,%g))\n"), this, _u.pComp->real(), _u.pComp->imag()));
	} else if (value.IsModule()) {
		_u.pModule = value._u.pModule->IncRef();
		DBG(::_tprintf(_T("%p Value(Value as Module(%p)) ref=%d\n"), this, _u.pModule, _u.pModule->GetRefCnt()));
	} else if (value.IsClass()) {
		_u.pClass = value._u.pClass->IncRef();
		DBG(::_tprintf(_T("%p Value(Value as Class(%p)) ref=%d\n"), this, _u.pClass, _u.pClass->GetRefCnt()));
	} else if (value.IsObject()) {
		_u.pObj = value._u.pObj->IncRef();
		DBG(::_tprintf(_T("%p Value(Value as Object(%p)) ref=%d\n"), this, _u.pObj, _u.pObj->GetRefCnt()));
	} else {
		// nothing to do
	}
}

Value::~Value()
{
	DBG(::_tprintf(_T("%p ~Value()\n"), this));
	FreeResource();
}

Value::Value(Environment &env, const TCHAR *str)
{
	_valType = static_cast<char>(VTYPE_String);
	_ownFlag = true;
	_u.pObj = new Object_String(env.GetClass_String(), str);
}

Value::Value(Environment &env, const TCHAR *str, size_t len)
{
	_valType = static_cast<char>(VTYPE_String);
	_ownFlag = true;
	_u.pObj = new Object_String(env.GetClass_String(), str, len);
}

Value::Value(Object *pObj, ValueType valType, bool ownFlag)
{
	_valType = static_cast<char>(valType);
	_ownFlag = ownFlag;
	_u.pObj = pObj;
}

Complex *Value::DupComplex(const Complex &comp)
{
	return new Complex(comp);
}

void Value::FreeResource()
{
	if (IsComplex()) {
		delete _u.pComp;
		_u.pComp = NULL;
		DBG(::_tprintf(_T("%p  Value::FreeResource() delete pComp\n"), this));
	} else if (IsModule()) {
		if (_ownFlag) Module::Delete(_u.pModule);
		_u.pModule = NULL;
	} else if (IsClass()) {
		if (_ownFlag) Class::Delete(_u.pClass);
		_u.pClass = NULL;
	} else if (IsObject()) {
		if (_ownFlag) Object::Delete(_u.pObj);
		_u.pObj = NULL;
	} else { // Number, Boolean
		// nothing to do
	}
	_valType = static_cast<char>(VTYPE_Invalid);
}

Value &Value::operator=(const Value &value)
{
	FreeResource();
	_valType = value._valType;
	_ownFlag = value._ownFlag;
	if (value.IsNumber()) {
		_u.num = value._u.num;
		DBG(::_tprintf(_T("%p Value::operator=(%p Value as Number)\n"), this, &value));
	} else if (value.IsBoolean()) {
		_u.flag = value._u.flag;
		DBG(::_tprintf(_T("%p Value::operator=(%p Value as Boolean)\n"), this, &value));
	} else if (value.IsSymbol()) {
		_u.pSymbol = value._u.pSymbol;
		DBG(::_tprintf(_T("%p Value::operator=(%p Value as Symbol)\n"), this, &value));
	} else if (value.IsComplex()) {
		_u.pComp = DupComplex(*value._u.pComp);
		DBG(::_tprintf(_T("%p Value::operator=(%p Value as Complex)\n"), this, &value));
	} else if (value.IsModule()) {
		_u.pModule = value._u.pModule->IncRef();
		DBG(::_tprintf(_T("%p Value::operator=(%p Value as Module(%p)) ref=%d\n"),
								this, &value, _u.pModule, _u.pModule->GetRefCnt()));
	} else if (value.IsClass()) {
		_u.pClass = value._u.pClass->IncRef();
		DBG(::_tprintf(_T("%p Value::operator=(%p Value as Class(%p)) ref=%d\n"),
								this, &value, _u.pClass, _u.pClass->GetRefCnt()));
	} else if (value.IsObject()) {
		_u.pObj = value._u.pObj->IncRef();
		DBG(::_tprintf(_T("%p Value::operator=(%p Value as Object(%p)) ref=%d\n"),
								this, &value, _u.pObj, _u.pObj->GetRefCnt()));
	} else {
		_valType = static_cast<char>(VTYPE_Invalid);
	}
	return *this;
}

const TCHAR *Value::GetTypeName() const
{
	return IsObject()? GetObject()->GetClassName() :
							GetValueTypeSymbol(GetType())->GetName();
}

bool Value::IsObject() const
{
	return IsGenericObject() ||
		IsFunction() || IsString() || IsList() || IsDict() ||
		IsFile() || IsFileStat() || IsDateTime() || IsExpr() ||
		IsEnvironment() || IsError();
}

ObjectBase *Value::ExtractObject(Signal sig)
{
	ObjectBase *pObjBase = NULL;
	if (IsModule()) {
		pObjBase = GetModule();
	} else if (IsClass()) {
		pObjBase = GetClass();
	} else if (IsObject()) {
		pObjBase = GetObject();
		if (pObjBase->IsFunction()) {
			const Object_Function *pObjFunc =
								dynamic_cast<const Object_Function *>(pObjBase);
			Class *pClass = pObjFunc->GetFunction()->GetClassToConstruct();
			if (pClass != NULL) {
				InitAsClass(pClass->IncRef());
				pObjBase = pClass;
			}
		}
	} else {
		sig.SetError(ERR_ValueError,
			_T("%s can not be specified as l-value of field"), GetTypeName());
		return NULL;
	}
	return pObjBase;
}

bool Value::IsFlatList() const
{
	return IsList() && GetList().IsFlat();
}

Object_String *Value::GetStringObj()
{
	return dynamic_cast<Object_String *>(_u.pObj);
}

const Object_String *Value::GetStringObj() const
{
	return dynamic_cast<const Object_String *>(_u.pObj);
}

const TCHAR *Value::GetString() const
{
	return dynamic_cast<Object_String *>(_u.pObj)->GetString();
}

const DateTime &Value::GetDateTime() const
{
	return dynamic_cast<Object_DateTime *>(_u.pObj)->GetDateTime();
}

ErrorType Value::GetErrorType() const
{
	return dynamic_cast<Object_Error *>(_u.pObj)->GetErrorType();
}

ValueList &Value::GetList()
{
	return dynamic_cast<Object_List *>(_u.pObj)->GetList();
}

const ValueList &Value::GetList() const
{
	return dynamic_cast<const Object_List *>(_u.pObj)->GetList();
}

ValueDict &Value::GetDict()
{
	return dynamic_cast<Object_Dict *>(_u.pObj)->GetDict();
}

const ValueDict &Value::GetDict() const
{
	return IsDict()?
		dynamic_cast<const Object_Dict *>(_u.pObj)->GetDict() : ValueDict::Null;
}

File &Value::GetFile()
{
	return dynamic_cast<Object_File *>(_u.pObj)->GetFile();
}

const Expr *Value::GetExpr() const
{
	return IsExpr()? dynamic_cast<Object_Expr *>(_u.pObj)->GetExpr() : NULL;
}

Function *Value::GetFunction()
{
	return IsFunction()? dynamic_cast<Object_Function *>(_u.pObj)->GetFunction() : NULL;
}

Expr *Value::CloneExpr() const
{
	return IsExpr()? dynamic_cast<Object_Expr *>(_u.pObj)->GetExpr()->IncRef() :
			IsSymbol()? new Expr_Symbol(_u.pSymbol) : NULL;
}

String Value::ToString(Signal sig, bool exprFlag) const
{
	if (IsNumber()) {
		TCHAR buff[32];
		::_stprintf(buff, GetNumberFormat(), _u.num);
		return String(buff);
	} else if (IsBoolean()) {
		return String(_u.flag?
			AScript_Symbol(true_)->GetName() : AScript_Symbol(false_)->GetName());
	} else if (IsSymbol()) {
		String str;
		if (exprFlag) str += _T('`');
		str += _u.pSymbol->GetName();
		return str;
	} else if (IsComplex()) {
		const Complex &comp = *_u.pComp;
		String str;
		TCHAR buff[32];
		if (comp.real() == 0. && comp.imag() == 0.) {
			::_stprintf(buff, GetNumberFormat(), 0.);
			str += buff;
		} else if (comp.real() == 0.) {
			if (_u.pComp->imag() < 0.) str += _T("-");
			::_stprintf(buff, GetNumberFormat(), ::fabs(_u.pComp->imag()));
			str += buff;
			str += AScript_Symbol(j)->GetName();
		} else if (comp.imag() == 0.) {
			::_stprintf(buff, GetNumberFormat(), _u.pComp->real());
			str += buff;
		} else {
			::_stprintf(buff, GetNumberFormat(), _u.pComp->real());
			str += buff;
			str += (_u.pComp->imag() >= 0.)? _T("+") : _T("-");
			::_stprintf(buff, GetNumberFormat(), ::fabs(_u.pComp->imag()));
			str += buff;
			str += AScript_Symbol(j)->GetName();
		}
		return str;
	} else if (IsModule()) {
		return _u.pModule->ToString(sig, exprFlag);
	} else if (IsClass()) {
		return _u.pClass->ToString(sig, exprFlag);
	} else if (IsObject()) {
		return _u.pObj->ToString(sig, exprFlag);
	}
	return String(exprFlag? _T("nil") : _T(""));
}

Number Value::ToNumber(bool allowPartFlag, bool &successFlag) const
{
	successFlag = true;
	if (IsNumber()) {
		return _u.num;
	} else if (IsBoolean()) {
		return _u.flag? 1. : 0.;
	} else if (IsString()) {
		const TCHAR *str = GetString();
		Number num = 0.;
		TCHAR *p = const_cast<TCHAR *>(str);
		if (str[0] == _T('0') &&
				(str[1] == _T('x') || str[1] == _T('X') || IsOctDigit(str[1]))) {
			num = static_cast<Number>(::_tcstoul(str, &p, 0));
		} else {
			num = ::_tcstod(str, &p);
		}
		successFlag = (p > str && (allowPartFlag || *p == _T('\0')));
		return num;
	} else {
		successFlag = false;
		return 0.;
	}
}

int Value::Compare(const Value &value1, const Value &value2)
{
	int rtn = -1;
	if (value1.GetType() != value2.GetType()) {
		rtn = static_cast<int>(value1.GetType()) -
								static_cast<int>(value2.GetType());
	} else if (value1.IsNumber()) {
		rtn = (value1.GetNumber() == value2.GetNumber())? 0 :
				(value1.GetNumber() < value2.GetNumber())? -1 : +1;
	} else if (value1.IsBoolean()) {
		rtn = static_cast<int>(value1.GetBoolean()) -
								static_cast<int>(value2.GetBoolean());
	} else if (value1.IsSymbol()) {
		rtn = static_cast<int>(value1.GetSymbol()->GetUniqNum()) -
										value2.GetSymbol()->GetUniqNum();
	} else if (value1.IsString()) {
		rtn = ::_tcscmp(value1.GetString(), value2.GetString());
	} else if (value1.IsDateTime()) {
		rtn = DateTime::Compare(value1.GetDateTime(), value2.GetDateTime());
	} else if (value1.IsList()) {
		bool emptyFlag1 = value1.GetList().empty();
		bool emptyFlag2 = value2.GetList().empty();
		if (emptyFlag1 || emptyFlag2) {
			rtn = -(static_cast<int>(emptyFlag1) - static_cast<int>(emptyFlag2));
		} else {
			return Value::Compare(value1.GetList().front(), value2.GetList().front());
		}
	} else if (value1.IsInvalid() && value2.IsInvalid()) {
		rtn = 0;
	}
	return rtn;
}

void Value::Accept(Signal sig, ValueVisitor &visitor) const
{
	if (IsList()) {
		foreach_const (ValueList, pValue, GetList()) {
			pValue->Accept(sig, visitor);
			if (sig.IsSignalled()) break;
		}
	} else {
		visitor.Visit(sig, *this);
	}
}

void Value::InitAsModule(Module *pModule)
{
	FreeResource();
	_valType = static_cast<char>(VTYPE_Module), _u.pModule = pModule;
}

void Value::InitAsClass(Class *pClass)
{
	FreeResource();
	_valType = static_cast<char>(VTYPE_Class), _u.pClass = pClass;
}

void Value::InitAsObject(Object *pObj, ValueType valType, bool ownFlag)
{
	FreeResource();
	_valType = static_cast<char>(valType), _u.pObj = pObj;
	_ownFlag = ownFlag;
}

Object *Value::InitAsObject(Environment &envOuter)
{
	Signal sig;
	Object *pObj = envOuter.GetClass_Object()->CreateDescendant(envOuter, sig, NULL);
	InitAsObject(pObj, VTYPE_Object);
	return pObj;
}

void Value::InitAsFunction(Environment &envOuter, Function *pFunc)
{
	Object_Function *pObj = new Object_Function(envOuter.GetClass_Function(), pFunc);
	InitAsObject(pObj, VTYPE_Function);
}

ValueList &Value::InitAsList(Environment &envOuter)
{
	Object_List *pObj = new Object_List(envOuter.GetClass_List());
	InitAsObject(pObj, VTYPE_List);
	return pObj->GetList();
}

ValueList &Value::InitAsList(Environment &envOuter, size_t n, const Value &value)
{
	Object_List *pObj = (n == 0)?
			new Object_List(envOuter.GetClass_List()) :
			new Object_List(envOuter.GetClass_List(), n, value);
	InitAsObject(pObj, VTYPE_List);
	return pObj->GetList();
}

ValueDict &Value::InitAsDict(Environment &envOuter)
{
	Object_Dict *pObj = new Object_Dict(envOuter.GetClass_Dict());
	InitAsObject(pObj, VTYPE_Dict);
	return pObj->GetDict();
}

Object_File *Value::InitAsFile(Environment &envOuter)
{
	Object_File *pObj = new Object_File(envOuter.GetClass_File());
	InitAsObject(pObj, VTYPE_File);
	return pObj;
}

void Value::InitAsExpr(Environment &envOuter, Expr *pExpr)
{
	Object_Expr *pObj = new Object_Expr(envOuter.GetClass_Expr(), pExpr);
	InitAsObject(pObj, VTYPE_Expr);
}

void Value::InitAsEnvironment(Environment &env)
{
	Object_Environment *pObj =
				new Object_Environment(env.GetClass_Environment(), env);
	InitAsObject(pObj, VTYPE_Environment);
}

//-----------------------------------------------------------------------------
// ValueList
//-----------------------------------------------------------------------------
const ValueList ValueList::Null;

bool ValueList::IsTypeMixed() const
{
	if (empty()) return false;
	ValueType valTypeFirst = front().GetType();
	if (valTypeFirst == VTYPE_Complex) valTypeFirst = VTYPE_Number;
	foreach_const (ValueList, pValue, *this) {
		ValueType valType = pValue->GetType();
		if (valType == VTYPE_Complex) valType = VTYPE_Number;
		if (valTypeFirst != valType) return true;
	}
	return false;
}

bool ValueList::IsFlat() const
{
	foreach_const (ValueList, pValue, *this) {
		if (pValue->IsList()) return false;
	}
	return true;
}

bool ValueList::IsContain(const Value &value) const
{
	foreach_const (ValueList, pValue, *this) {
		if (Value::Compare(*pValue, value) == 0) return true;
	}
	return false;
}

// not tested yet
bool ValueList::CheckMatrix(int *pnRow, int *pnCol) const
{
	if (empty()) return false;
	size_t nRow = size();
	size_t nCol = 1;
	if (front().IsList()) {
		size_t nCol = front().GetList().size();
		foreach_const (ValueList, pValueRow, *this) {
			if (!pValueRow->IsList()) {
				return false;
			} else if (pValueRow->GetList().size() != nCol) {
				return false;
			} else {
				foreach_const (ValueList, pValueCol, pValueRow->GetList()) {
					if (!pValueCol->IsNumberOrComplex()) return false;
				}
			}
		}
	} else {
		foreach_const (ValueList, pValueCol, *this) {
			if (!pValueCol->IsNumberOrComplex()) return false;
		}
	}
	if (pnRow != NULL) *pnRow = static_cast<int>(nRow);
	if (pnCol != NULL) *pnCol = static_cast<int>(nCol);
	return true;
}

bool ValueList::AssumeSameLength(Signal sig,
						const ValueList &valList1, const ValueList &valList2)
{
	if (valList1.size() == valList2.size()) return true;
	sig.SetError(ERR_ValueError, _T("lists are different in length"));
	return false;
}

void ValueList::Print(Signal sig, int indentLevel) const
{
	foreach_const (ValueList, pValue, *this) {
		if (pValue->IsList()) {
			pValue->GetList().Print(sig, indentLevel + 1);
		} else {
			::_tprintf(_T("%*s%s\n"),
					indentLevel * 2, _T(""), pValue->ToString(sig).c_str());
		}
	}
}

//-----------------------------------------------------------------------------
// ValueMap
//-----------------------------------------------------------------------------
const ValueMap ValueMap::Null;

//-----------------------------------------------------------------------------
// ValueDict
//-----------------------------------------------------------------------------
const ValueDict ValueDict::Null;

bool ValueDict::Store(Signal sig, const ValueList &valList)
{
	enum { FIELD_Key, FIELD_Value } field = FIELD_Key;
	Value valueIdx;
	const ValueList *pValList = &valList;
	if (valList.size() == 1 && valList[0].IsList()) {
		pValList = &valList[0].GetList();
	}
	foreach_const (ValueList, pValue, *pValList) {
		if (field == FIELD_Key) {
			Value value;
			valueIdx = *pValue;
			if (valueIdx.IsList()) {
				const ValueList &valListElem = valueIdx.GetList();
				if (valListElem.size() != 2) {
					sig.SetError(ERR_KeyError, _T("invalid key-value format"));
					return false;
				}
				valueIdx = valListElem[0];
				value = valListElem[1];
			}
			if (!ValueDict::IsValidKey(valueIdx)) {
				sig.SetError(ERR_KeyError, _T("invalid value type for key"));
				return false;
			} else if (find(valueIdx) != end()) {
				sig.SetError(ERR_KeyError, _T("duplicated key '%s'"), valueIdx.ToString(sig).c_str());
				return false;
			}
			if (value.IsValid()) {
				insert(ValueDict::value_type(valueIdx, value));
			} else {
				field = FIELD_Value;
			}
		} else { // FIELD_Value
			insert(ValueDict::value_type(valueIdx, *pValue));
			field = FIELD_Key;
		}
	}
	if (field == FIELD_Value) {
		sig.SetError(ERR_KeyError, _T("unmatching key-value pair"));
		return false;
	}
	return true;
}

bool ValueDict::Store(Signal sig, const Value &valueIdx, const Value &value)
{
	if (!ValueDict::IsValidKey(valueIdx)) {
		sig.SetError(ERR_KeyError, _T("invalid value type for key"));
		return false;
	} else if (find(valueIdx) != end()) {
		sig.SetError(ERR_KeyError, _T("duplicated key '%s'"), valueIdx.ToString(sig).c_str());
		return false;
	}
	insert(ValueDict::value_type(valueIdx, value));
	return true;
}

//-----------------------------------------------------------------------------
// ValuePickerList
//-----------------------------------------------------------------------------
bool ValuePickerList::IsValidAll() const
{
	if (empty()) return false;
	foreach_const (ValuePickerList, pPicker, *this) {
		if (!pPicker->IsValid()) return false;
	}
	return true;
}

bool ValuePickerList::IsValidAny() const
{
	if (empty()) return false;
	foreach_const (ValuePickerList, pPicker, *this) {
		if (pPicker->IsValid()) return true;
	}
	return false;
}

void ValuePickerList::Reset()
{
	foreach (ValuePickerList, pPicker, *this) pPicker->Reset();
}

void ValuePickerList::Advance()
{
	foreach (ValuePickerList, pPicker, *this) pPicker->Advance();
}

}
