//-----------------------------------------------------------------------------
// AScript yaml module
//-----------------------------------------------------------------------------
#include <ascript.h>
#include <yaml.h>

AScript_BeginModule(yaml)

typedef std::map<String, Value> AnchorMap;

//-----------------------------------------------------------------------------
// utilities
//-----------------------------------------------------------------------------
bool WriteStream(Environment &env, Signal sig, Stream &stream, const Value &value);
const char *GetErrorText(yaml_error_type_e error);

Value ExecParser(Environment &env, Signal sig,
						yaml_parser_t &parser, AnchorMap &anchorMap);
Value ExecEmitter(Environment &env, Signal sig,
						yaml_emitter_t &emitter, yaml_encoding_t encoding);

//-----------------------------------------------------------------------------
// Handler
//-----------------------------------------------------------------------------
class Handler {
private:
	Signal _sig;
	Stream &_stream;
public:
	inline Handler(Signal sig, Stream &stream) : _sig(sig), _stream(stream) {}
	int DoWrite(unsigned char *buffer, unsigned int size);
	static int WriteHandler(void *ext, unsigned char *buffer, unsigned int size);
};

int Handler::DoWrite(unsigned char *buffer, unsigned int size)
{
	_stream.Write(_sig, buffer, size);
	return _sig.IsSignalled()? 0 : 1;
}

int Handler::WriteHandler(void *ext, unsigned char *buffer, unsigned int size)
{
	return reinterpret_cast<Handler *>(ext)->DoWrite(buffer, size);
}

int WriteStringHandler(void *ext, unsigned char *buffer, unsigned int size)
{
	String &str = *reinterpret_cast<String *>(ext);
	for (char *p = reinterpret_cast<char *>(buffer); size > 0; size--, p++) {
		str += *p;
	}
	return 1;
}

//-----------------------------------------------------------------------------
// Stocker
//-----------------------------------------------------------------------------
class Stocker {
public:
	virtual void Stock(const Value &value) = 0;
	virtual Value GetValue() = 0;
};

typedef std::vector<Stocker *> StockerList;

class Stocker_Mapping : public Stocker {
private:
	Object_Dict *_pObjDict;
	bool _keyFlag;
	Value _valueKey;
public:
	inline Stocker_Mapping(Environment &env) :
					_pObjDict(new Object_Dict(env)), _keyFlag(true) {}
	virtual void Stock(const Value &value);
	virtual Value GetValue();
};

void Stocker_Mapping::Stock(const Value &value)
{
	if (_keyFlag) {
		_valueKey = value;
	} else {
		_pObjDict->GetDict()[_valueKey] = value;
	}
	_keyFlag = !_keyFlag;
}

Value Stocker_Mapping::GetValue()
{
	return Value(_pObjDict);
}

class Stocker_Sequence : public Stocker {
private:
	Object_List *_pObjList;
public:
	inline Stocker_Sequence(Environment &env) :
					_pObjList(new Object_List(env)) {}
	virtual void Stock(const Value &value);
	virtual Value GetValue();
	inline Object_List *GetListObj() { return _pObjList; }
};

void Stocker_Sequence::Stock(const Value &value)
{
	_pObjList->GetList().push_back(value);
}

Value Stocker_Sequence::GetValue()
{
	return Value(_pObjList);
}

//-----------------------------------------------------------------------------
// Iterator_FromStream
//-----------------------------------------------------------------------------
class Iterator_FromStream : public Iterator {
private:
	Signal _sig;
	Stream *_pStream;
	yaml_parser_t _parser;
	AnchorMap _anchorMap;
	bool _doneFlag;
public:
	Iterator_FromStream(Signal sig, Stream *pStream);
	virtual ~Iterator_FromStream();
	virtual bool DoNext(Environment &env, Signal sig, Value &value);
	virtual String ToString(Signal sig) const;
	int DoRead(unsigned char *buffer, unsigned int size, unsigned int *length);
	static int ReadHandler(void *ext, unsigned char *buffer,
								unsigned int size, unsigned int *length);
};

Iterator_FromStream::Iterator_FromStream(Signal sig, Stream *pStream) :
			Iterator(false), _sig(sig), _pStream(pStream), _doneFlag(false)
{
	::yaml_parser_initialize(&_parser);
	::yaml_parser_set_input(&_parser, ReadHandler, this);
}

Iterator_FromStream::~Iterator_FromStream()
{
	::yaml_parser_delete(&_parser);
}

bool Iterator_FromStream::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_doneFlag) return false;
	value = ExecParser(env, sig, _parser, _anchorMap);
	_doneFlag = value.IsInvalid();
	return !_doneFlag;
}

String Iterator_FromStream::ToString(Signal sig) const
{
	return String("<iterator:yaml.fromstream>");
}

int Iterator_FromStream::DoRead(unsigned char *buffer, unsigned int size, unsigned int *length)
{
	*length = static_cast<unsigned int>(_pStream->Read(_sig, buffer, size));
	return _sig.IsSignalled()? 0 : 1;
}

int Iterator_FromStream::ReadHandler(void *ext,
		unsigned char *buffer, unsigned int size, unsigned int *length)
{
	return reinterpret_cast<Iterator_FromStream *>(ext)->DoRead(buffer, size, length);
}

//-----------------------------------------------------------------------------
// Iterator_FromString
//-----------------------------------------------------------------------------
class Iterator_FromString : public Iterator {
private:
	yaml_parser_t _parser;
	AnchorMap _anchorMap;
	bool _doneFlag;
public:
	Iterator_FromString(const char *str);
	virtual ~Iterator_FromString();
	virtual bool DoNext(Environment &env, Signal sig, Value &value);
	virtual String ToString(Signal sig) const;
	int DoRead(unsigned char *buffer, unsigned int size, unsigned int *length);
	static int ReadHandler(void *ext, unsigned char *buffer,
								unsigned int size, unsigned int *length);
};

Iterator_FromString::Iterator_FromString(const char *str) :
									Iterator(false), _doneFlag(false)
{
	::yaml_parser_initialize(&_parser);
	::yaml_parser_set_input_string(&_parser,
			reinterpret_cast<const unsigned char *>(str), ::strlen(str));
}

Iterator_FromString::~Iterator_FromString()
{
	::yaml_parser_delete(&_parser);
}

bool Iterator_FromString::DoNext(Environment &env, Signal sig, Value &value)
{
	if (_doneFlag) return false;
	value = ExecParser(env, sig, _parser, _anchorMap);
	_doneFlag = value.IsInvalid();
	return !_doneFlag;
}

String Iterator_FromString::ToString(Signal sig) const
{
	return String("<iterator:yaml.fromstring>");
}

//-----------------------------------------------------------------------------
// Parser body
//-----------------------------------------------------------------------------
Value ExecParser(Environment &env, Signal sig,
							yaml_parser_t &parser, AnchorMap &anchorMap)
{
	Value result;
	StockerList stockerList;
	yaml_event_t event;
	for (bool contFlag = true; contFlag; ) {
		if (!::yaml_parser_parse(&parser, &event)) {
			sig.SetError(ERR_FormatError, "%s", GetErrorText(parser.error));
			break;
		}
		if (event.type == YAML_STREAM_START_EVENT) {
			//::printf("STREAM_START\n");
			yaml_encoding_t encoding = event.data.stream_start.encoding;
			
		} else if (event.type == YAML_DOCUMENT_START_EVENT) {
			yaml_version_directive_t *version_directive =
							event.data.document_start.version_directive;
			yaml_tag_directive_t *tag_start =
							event.data.document_start.tag_directives.start;
			yaml_tag_directive_t *tag_end =
							event.data.document_start.tag_directives.end;
			int implicit = event.data.document_start.implicit;
			//::printf("DOCUMENT_START %p %p %p\n",
			//					version_directive, tag_start, tag_end);
			//tag_start->handle;
			//tag_start->prefix;
			//tag_end->handle;
			//tag_end->prefix;
		} else if (event.type == YAML_SCALAR_EVENT ||
					event.type == YAML_SEQUENCE_START_EVENT ||
					event.type == YAML_MAPPING_START_EVENT) {
			yaml_char_t *anchor = NULL;
			yaml_char_t *tag = NULL;
			Value value;
			Stocker *pStockerTop =
						stockerList.empty()? NULL : stockerList.back();
			if (event.type == YAML_SCALAR_EVENT) {
				anchor = event.data.scalar.anchor;
				tag = event.data.scalar.tag;
				yaml_char_t *valueRaw = event.data.scalar.value;
				size_t length = event.data.scalar.length;
				int plain_implicit = event.data.scalar.plain_implicit;
				int quoted_implicit = event.data.scalar.quoted_implicit;
				yaml_scalar_style_t style = event.data.scalar.style;
				String valueStr(reinterpret_cast<char *>(valueRaw), length);
				//::printf("SCALAR %s %s %s\n", anchor, tag, valueStr.c_str());
				value = Value(env, valueStr.c_str());
			} else if (event.type == YAML_SEQUENCE_START_EVENT) {
				anchor = event.data.sequence_start.anchor;
				tag = event.data.sequence_start.tag;
				int implicit = event.data.sequence_start.implicit;
				yaml_sequence_style_t style = event.data.sequence_start.style;
				//::printf("SEQUENCE_START %s %s\n", anchor, tag);
				Stocker *pStocker = new Stocker_Sequence(env);
				stockerList.push_back(pStocker);
				value = pStocker->GetValue();
			} else { // event.type == YAML_MAPPING_START_EVENT
				anchor = event.data.mapping_start.anchor;
				tag = event.data.mapping_start.tag;
				int implicit = event.data.mapping_start.implicit;
				yaml_mapping_style_t style = event.data.mapping_start.style;
				//::printf("MAPPING_START %s %s\n", anchor, tag);
				Stocker *pStocker = new Stocker_Mapping(env);
				stockerList.push_back(pStocker);
				value = pStocker->GetValue();
			}
			if (pStockerTop == NULL) {
				result = value;
			} else {
				pStockerTop->Stock(value);
			}
			if (anchor != NULL) {
				anchorMap[reinterpret_cast<char *>(anchor)] = value;
			}
		} else if (event.type == YAML_ALIAS_EVENT) {
			yaml_char_t *anchor = event.data.alias.anchor;
			//::printf("ALIAS\ %s\n", anchor);
			AnchorMap::iterator iter =
							anchorMap.find(reinterpret_cast<char *>(anchor));
			if (iter == anchorMap.end()) {
				sig.SetError(ERR_FormatError, "undefined anchor %s", anchor);
				contFlag = false;
			} else if (stockerList.empty()) {
				// something's wrong
			} else {
				stockerList.back()->Stock(iter->second);
			}
		} else if (event.type == YAML_STREAM_END_EVENT) {
			//::printf("STREAM_END\n");
			contFlag = false;
		} else if (event.type == YAML_DOCUMENT_END_EVENT) {
			int implicit = event.data.document_end.implicit;
			//::printf("DOCUMENT_END\n");
			contFlag = false;
		} else if (event.type == YAML_SEQUENCE_END_EVENT) {
			//::printf("SEQUENCE_END\n");
			stockerList.pop_back();
		} else if (event.type == YAML_MAPPING_END_EVENT) {
			//::printf("MAPPING_END\n");
			stockerList.pop_back();
		} else {
			sig.SetError(ERR_FormatError, "unknown event %d\n", event.type);
			contFlag = false;
		}
		::yaml_event_delete(&event);
	}
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

//-----------------------------------------------------------------------------
// Emitter body
//-----------------------------------------------------------------------------
bool ExecEmitterSub(Environment &env, Signal sig,
								yaml_emitter_t &emitter, const Value &value);

bool ExecEmitter(Environment &env, Signal sig, yaml_emitter_t &emitter,
								yaml_encoding_t encoding, const Value &value)
{
	do {
		yaml_event_t event;
		::yaml_stream_start_event_initialize(&event, encoding);
		if (!::yaml_emitter_emit(&emitter, &event)) {
			sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
			return false;
		}
	} while (0);
	do {
		yaml_version_directive_t *version_directive = NULL;
		yaml_tag_directive_t *tag_start = NULL;
		yaml_tag_directive_t *tag_end = NULL;
		int implicit = 1;
		yaml_event_t event;
		::yaml_document_start_event_initialize(&event,
								version_directive, tag_start, tag_end, implicit);
		if (!::yaml_emitter_emit(&emitter, &event)) {
			sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
			return false;
		}
	} while (0);
	if (!ExecEmitterSub(env, sig, emitter, value)) return false;
	do {
		int implicit = 1;
		yaml_event_t event;
		::yaml_document_end_event_initialize(&event, implicit);
		if (!::yaml_emitter_emit(&emitter, &event)) {
			sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
			return false;
		}
	} while (0);
	do {
		yaml_event_t event;
		::yaml_stream_end_event_initialize(&event);
		if (!::yaml_emitter_emit(&emitter, &event)) {
			sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
			return false;
		}
	} while (0);
	return true;
}

bool ExecEmitterSub(Environment &env, Signal sig,
								yaml_emitter_t &emitter, const Value &value)
{
	if (value.IsList()) {
		do {
			yaml_char_t *anchor = NULL;
			yaml_char_t *tag = NULL;
			int implicit = 0;
			yaml_sequence_style_t style = YAML_ANY_SEQUENCE_STYLE;
			yaml_event_t event;
			::yaml_sequence_start_event_initialize(&event, anchor, tag, implicit, style);
			if (!::yaml_emitter_emit(&emitter, &event)) {
				sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
				return false;
			}
		} while (0);
		foreach_const (ValueList, pValue, value.GetList()) {
			if (!ExecEmitterSub(env, sig, emitter, *pValue)) return false;
		}
		do {
			yaml_event_t event;
			::yaml_sequence_end_event_initialize(&event);
			if (!::yaml_emitter_emit(&emitter, &event)) {
				sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
				return false;
			}
		} while (0);
	} else if (value.IsDict()) {
		do {
			yaml_char_t *anchor = NULL;
			yaml_char_t *tag = NULL;
			int implicit = 0;
			yaml_mapping_style_t style = YAML_ANY_MAPPING_STYLE;
			yaml_event_t event;
			::yaml_mapping_start_event_initialize(&event, anchor, tag, implicit, style);
			if (!::yaml_emitter_emit(&emitter, &event)) {
				return false;
			}
		} while (0);
		foreach_const (ValueDict, iter, value.GetDict()) {
			if (!ExecEmitterSub(env, sig, emitter, iter->first)) return false;
			if (!ExecEmitterSub(env, sig, emitter, iter->second)) return false;
		}
		do {
			yaml_event_t event;
			::yaml_mapping_end_event_initialize(&event);
			if (!::yaml_emitter_emit(&emitter, &event)) {
				sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
				return false;
			}
		} while (0);
	} else {
		String str = value.ToString(sig, false);
		if (sig.IsSignalled()) return false;
		yaml_char_t *anchor = NULL;
		yaml_char_t *tag = NULL;
		yaml_char_t *valueRaw =
			reinterpret_cast<unsigned char *>(const_cast<char *>(str.c_str()));
		size_t length = str.size();
		int plain_implicit = 1;
		int quoted_implicit = 1;
		yaml_scalar_style_t style = YAML_ANY_SCALAR_STYLE;
		yaml_event_t event;
		::yaml_scalar_event_initialize(&event,
			anchor, tag, valueRaw, static_cast<int>(length), plain_implicit, quoted_implicit, style);
		if (!::yaml_emitter_emit(&emitter, &event)) {
			sig.SetError(ERR_FormatError, "%s", GetErrorText(emitter.error));
			return false;
		}
	}
	return true;
}

//-----------------------------------------------------------------------------
// AScript module functions: yaml
//-----------------------------------------------------------------------------
// yaml.read(stream:stream)
AScript_DeclareFunction(read)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream);
}

AScript_ImplementFunction(read)
{
	Stream &stream = args.GetStream(0);
	Iterator_FromStream *pIterator =
				new Iterator_FromStream(sig, Stream::Reference(&stream));
	if (args.IsRsltMulti()) {
		return ReturnIterator(env, sig, args, pIterator);
	}
	Value value;
	pIterator->Next(env, sig, value);
	Iterator::Delete(pIterator);
	return value;
}

// yaml.write(stream:stream, obj)
AScript_DeclareFunction(write)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream);
	DeclareArg(env, "obj", VTYPE_Any);
}

AScript_ImplementFunction(write)
{
	if (!WriteStream(env, sig, args.GetStream(0), args.GetValue(1))) {
		return Value::Null;
	}
	return args.GetSelf();
}

// yaml.parse(str:string)
AScript_DeclareFunction(parse)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementFunction(parse)
{
	Iterator_FromString *pIterator = new Iterator_FromString(args.GetString(0));
	if (args.IsRsltMulti()) {
		return ReturnIterator(env, sig, args, pIterator);
	}
	Value value;
	pIterator->Next(env, sig, value);
	Iterator::Delete(pIterator);
	return value;
}

// yaml.compose(obj)
AScript_DeclareFunction(compose)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "obj", VTYPE_Any);
}

AScript_ImplementFunction(compose)
{
	Object_Stream *pSelf = Object_Stream::GetSelfObj(args);
	yaml_emitter_t emitter;
	::yaml_emitter_initialize(&emitter);
	String str;
	::yaml_emitter_set_output(&emitter, WriteStringHandler, &str);
	ExecEmitter(env, sig, emitter, YAML_UTF8_ENCODING, args.GetValue(0));
	::yaml_emitter_delete(&emitter);
	if (sig.IsSignalled()) return Value::Null;
	return Value(env, str.c_str());
}

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

AScript_ImplementMethod(Stream, yamlread)
{
	Stream &stream = Object_Stream::GetSelfObj(args)->GetStream();
	Iterator_FromStream *pIterator =
				new Iterator_FromStream(sig, Stream::Reference(&stream));
	if (args.IsRsltMulti()) {
		return ReturnIterator(env, sig, args, pIterator);
	}
	Value value;
	pIterator->Next(env, sig, value);
	Iterator::Delete(pIterator);
	return value;
}

// stream#yamlwrite(obj):reduce
AScript_DeclareMethod(Stream, yamlwrite)
{
	SetMode(RSLTMODE_Reduce, FLAG_None);
	DeclareArg(env, "obj", VTYPE_Any);
}

AScript_ImplementMethod(Stream, yamlwrite)
{
	Object_Stream *pSelf = Object_Stream::GetSelfObj(args);
	if (!WriteStream(env, sig, pSelf->GetStream(), args.GetValue(0))) {
		return Value::Null;
	}
	return args.GetSelf();
}

// Module entry
AScript_ModuleEntry()
{
	// function assignment
	AScript_AssignFunction(read);
	AScript_AssignFunction(write);
	AScript_AssignFunction(parse);
	AScript_AssignFunction(compose);
	// method assignment to stream type
	AScript_AssignMethodTo(VTYPE_Stream, Stream, yamlread);
	AScript_AssignMethodTo(VTYPE_Stream, Stream, yamlwrite);
}

AScript_ModuleTerminate()
{
}

//-----------------------------------------------------------------------------
// utilities
//-----------------------------------------------------------------------------
bool WriteStream(Environment &env, Signal sig, Stream &stream, const Value &value)
{
	Handler handler(sig, stream);
	yaml_emitter_t emitter;
	::yaml_emitter_initialize(&emitter);
	::yaml_emitter_set_output(&emitter, Handler::WriteHandler, &handler);
	ExecEmitter(env, sig, emitter, YAML_UTF8_ENCODING, value);
	::yaml_emitter_delete(&emitter);
	return !sig.IsSignalled();
}

const char *GetErrorText(yaml_error_type_e error)
{
	static const struct {
		yaml_error_type_e error;
		const char *str;
	} tbl[] = {
		{ YAML_MEMORY_ERROR,	"Cannot allocate or reallocate a block of memory."	},
		{ YAML_READER_ERROR,	"Cannot read or decode the input stream."			},
		{ YAML_SCANNER_ERROR,	"Cannot scan the input stream."						},
		{ YAML_PARSER_ERROR,	"Cannot parse the input stream."					},
		{ YAML_COMPOSER_ERROR,	"Cannot compose a YAML document."					},
		{ YAML_WRITER_ERROR,	"Cannot write to the output stream."				},
		{ YAML_EMITTER_ERROR,	"Cannot emit a YAML stream."						},
	};
	for (int i = 0; i < NUMBEROF(tbl); i++) {
		if (tbl[i].error == error) return tbl[i].str;
	}
	return "unknown error";
}

AScript_EndModule(yaml, yaml)

AScript_RegisterModule(yaml)
