//-----------------------------------------------------------------------------
// AScript bzip2 module
//-----------------------------------------------------------------------------
#include <ascript.h>
#include "ascript/BZLibHelper.h"

AScript_BeginModule(bzip2)

//-----------------------------------------------------------------------------
// utilities
//-----------------------------------------------------------------------------
Object_Stream *GenerateDecompressor(Environment &env, Signal sig, Stream &stream)
{
	BZLib::Stream_Decompressor *pStream = 
		new BZLib::Stream_Decompressor(sig, Stream::Reference(&stream), InvalidSize);
	if (!pStream->Initialize(sig, 0, 0)) {
		Stream::Delete(pStream);
		return NULL;
	}
	return new Object_Stream(env, pStream);
}

Object_Stream *GenerateCompressor(Environment &env, Signal sig, Stream &stream)
{
	BZLib::Stream_Compressor *pStream =
		new BZLib::Stream_Compressor(sig, Stream::Reference(&stream));
	if (!pStream->Initialize(sig, 9, 0, 0)) {
		Stream::Delete(pStream);
		return NULL;
	}
	return new Object_Stream(env, pStream);
}

//-----------------------------------------------------------------------------
// AScript module functions: bzip2
//-----------------------------------------------------------------------------
// bzip2.open(name:string, mode?:string, encoding:string => "utf-8")
AScript_DeclareFunction(open)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "name", VTYPE_String);
	DeclareArg(env, "mode", VTYPE_String, OCCUR_ZeroOrOnce);
	DeclareArg(env, "encoding", VTYPE_String, OCCUR_Once, false, false,
												new Expr_String("utf-8"));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(open)
{
	unsigned long attr = Stream::ATTR_Readable;
	const char *name = args.GetString(0);
	if (*name == '>') {
		attr = Stream::ATTR_Writable;
		name++;
	}
	if (args.IsString(1)) {
		const char *p = args.GetString(1);
		if (*p == 'r') {
			attr = Stream::ATTR_Readable;
		} else if (*p == 'w') {
			attr = Stream::ATTR_Writable;
		} else {
			sig.SetError(ERR_IOError, "invalid open mode");
			return NULL;
		}
	}
	Stream *pStream = Directory::OpenStream(env, sig, name, attr, args.GetString(2));
	if (sig.IsSignalled()) return Value::Null;
	Object_Stream *pObjStream;
	if (pStream->IsWritable()) {
		pObjStream = GenerateCompressor(env, sig, *pStream);
	} else {
		pObjStream = GenerateDecompressor(env, sig, *pStream);
	}
	if (sig.IsSignalled()) return Value::Null;
	Value result(pObjStream);
	if (args.IsBlockSpecified()) {
		const Function *pFuncBlock =
						args.GetBlockFunc(env, sig, GetSymbolForBlock());
		if (pFuncBlock == NULL) return Value::Null;
		ValueList valListArg(result);
		Args argsSub(valListArg);
		pFuncBlock->Eval(env, sig, argsSub);
		result = Value::Null;	// object is destroyed here
	}
	return result;
}

// bzip2.decomp(stream:stream)
AScript_DeclareFunction(decomp)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream);
}

AScript_ImplementFunction(decomp)
{
	Stream &stream = args.GetStream(0);
	Object_Stream *pObjStream = GenerateDecompressor(env, sig, stream);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObjStream);
}

// bzip2.comp(stream:stream)
AScript_DeclareFunction(comp)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream);
}

AScript_ImplementFunction(comp)
{
	Stream &stream = args.GetStream(0);
	Object_Stream *pObjStream = GenerateCompressor(env, sig, stream);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObjStream);
}

//-----------------------------------------------------------------------------
// AScript interfaces for Object_Stream
//-----------------------------------------------------------------------------
// stream#bzip2decomp()
AScript_DeclareMethod(Stream, bzip2decomp)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Stream, bzip2decomp)
{
	Stream &stream = Object_Stream::GetSelfObj(args)->GetStream();
	Object_Stream *pObjStream = GenerateDecompressor(env, sig, stream);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObjStream);
}

// stream#bzip2comp()
AScript_DeclareMethod(Stream, bzip2comp)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementMethod(Stream, bzip2comp)
{
	Stream &stream = Object_Stream::GetSelfObj(args)->GetStream();
	Object_Stream *pObjStream = GenerateCompressor(env, sig, stream);
	if (sig.IsSignalled()) return Value::Null;
	return Value(pObjStream);
}

// Module entry
AScript_ModuleEntry()
{
	// function assignment
	AScript_AssignFunction(open);
	AScript_AssignFunction(decomp);
	AScript_AssignFunction(comp);
	// method assignment to stream type
	AScript_AssignMethodTo(VTYPE_Stream, Stream, bzip2decomp);
	AScript_AssignMethodTo(VTYPE_Stream, Stream, bzip2comp);
}

AScript_ModuleTerminate()
{
}

AScript_EndModule(bzip2, bzip2)

AScript_RegisterModule(bzip2)
