//
// Object_Codec
//

#include "ascript/Object_Codec.h"
#include "ascript/Object_Binary.h"
#include "ascript/Object_String.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Object_Codec
//-----------------------------------------------------------------------------
Object_Codec::~Object_Codec()
{
	ReleaseCodec();
}

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

bool Object_Codec::InstallCodec(Signal sig, const char *encoding, bool processEOLFlag)
{
	if (encoding == NULL) encoding = "us-ascii";
	CodecFactory *pCodecFactory = CodecFactory::Lookup(encoding);
	if (pCodecFactory == NULL) {
		sig.SetError(ERR_CodecError, "unsupported encoding name %s", encoding);
		return false;
	}
	ReleaseCodec();
	_encoding = encoding;
	_pEncoder = pCodecFactory->CreateEncoder(processEOLFlag);
	_pDecoder = pCodecFactory->CreateDecoder(processEOLFlag);
	return true;
}

void Object_Codec::ReleaseCodec()
{
	_encoding.clear();
	delete _pEncoder;
	delete _pDecoder;
	_pEncoder = NULL;
	_pDecoder = NULL;
}

String Object_Codec::ToString(Signal sig, bool exprFlag)
{
	String str;
	str += "<codec:";
	str += _encoding;
	str += ">";
	return str;
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Codec
//-----------------------------------------------------------------------------
// codec#encode(string:string):map
AScript_DeclareMethod(Codec, encode)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "string", VTYPE_String);
	SetHelp("");
}

AScript_ImplementMethod(Codec, encode)
{
	Object_Codec *pSelf = Object_Codec::GetSelfObj(args);
	Binary dst;
	if (!pSelf->GetEncoder()->Encode(sig, dst, args.GetStringObj(0)->GetString())) {
		return Value::Null;
	}
	Value result;
	result.InitAsBinary(env, dst);
	return result;
}

// codec#decode(buff:binary):map
AScript_DeclareMethod(Codec, decode)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "buff", VTYPE_Binary);
	SetHelp("");
}

AScript_ImplementMethod(Codec, decode)
{
	Object_Codec *pSelf = Object_Codec::GetSelfObj(args);
	String dst;
	if (!pSelf->GetDecoder()->Decode(sig, dst, args.GetBinary(0))) {
		return Value::Null;
	}
	return Value(env, dst.c_str());
}

// assignment
Class_Codec::Class_Codec(Environment *pEnvOuter) : Class(pEnvOuter)
{
	AScript_AssignMethod(Codec, encode);
	AScript_AssignMethod(Codec, decode);
}

bool Class_Codec::CastFrom(Environment &env, Signal sig, Value &value)
{
	if (value.IsString()) {
		Object_Codec *pObj = new Object_Codec(env);
		if (!pObj->InstallCodec(sig, value.GetString(), false)) {
			return false;
		}
		value = Value(pObj);
		return true;
	}
	return false;
}

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

}
