#include <mof/widget/mofml_parser.hpp>
#include <mof/utilities.hpp>
#include <mof/ConsoleIO.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include <iostream>
#include <vector>
#include <sstream>
#include <expat.h>
#include <expatJP.h>

namespace
{
//{{{ user_data_t
	struct user_data_t
	{
		std::vector<mof::widget::mofml_parser::node> node_list;
		std::vector<mof::widget::mofml_parser::node> backtrack_nodes;
		bool end_parse;
		int layout_depth;
		mof::widget::mofml_parser::text_node last_text_node;///< 最後に設定されたtextノード情報
	};
//}}}
//{{{ element_start
	static void XMLCALL element_start
	(
		void* user_data,
		const XML_Char* name,
		const XML_Char** attrs
	)
	{
		using namespace std;
		using namespace mof::widget;

		user_data_t* data = reinterpret_cast<user_data_t*>(user_data);
		if (data->end_parse) return;
		mofml_parser::node node;
		if (strcmp(name, "col") == 0) {
			// フォントを設定
			for (int i = 0; attrs[i] != NULL; i += 2) {
				if (strcmp(attrs[i], "name") == 0) {
					// font-colorを設定 
					mof::Color4f color;
					if (strcmp(attrs[i + 1], "white") == 0) color = mof::Color4f(1, 1, 1);
					else if (strcmp(attrs[i + 1], "red") == 0) color = mof::Color4f(1, 0, 0);
					else if (strcmp(attrs[i + 1], "green") == 0) color = mof::Color4f(0, 1, 0);
					else if (strcmp(attrs[i + 1], "blue") == 0) color = mof::Color4f(0, 0, 1);
					else if (strcmp(attrs[i + 1], "gray") == 0) color = mof::Color4f(0.6, 0.6, 0.6);
					else if (strcmp(attrs[i + 1], "yellow") == 0) color = mof::Color4f(1, 1, 0);
					data->last_text_node.font_color = color;
					node = data->last_text_node;
				}
				else if (strcmp(attrs[i], "code") == 0) {
					// font-colorを設定 
					data->last_text_node.font_color = mof::Color4f(mof::createColor(attrs[i + 1]));
					node = data->last_text_node;
				}

			}
		}
		else if (strcmp(name, "nl") == 0) {
			// 改行
			if (data->layout_depth != 0) throw std::logic_error("don't use <nl/> tag in layout");
			node = mofml_parser::new_line_node();
		}
		else if (strcmp(name, "img") == 0) {
			mofml_parser::image_node image_node;
			// 画像を設定
			for (int i = 0; attrs[i] != NULL; i += 2) {
				if (strcmp(attrs[i], "src") == 0) {
					// 画像のソースを設定 
					if (strcmp(attrs[i + 1], "") != 0) image_node.src = attrs[i + 1];
				}
				else if (strcmp(attrs[i], "w") == 0) {
					// 画像の幅
					if (strcmp(attrs[i + 1], "") != 0) image_node.width = boost::lexical_cast<float>(attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "h") == 0) {
					// 画像の高さ
					if (strcmp(attrs[i + 1], "") != 0) image_node.height = boost::lexical_cast<float>(attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "tbeginx") == 0) {
					if (strcmp(attrs[i + 1], "") != 0) image_node.t_rect.beginX = boost::lexical_cast<float>(attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "tendx") == 0) {
					if (strcmp(attrs[i + 1], "") != 0) image_node.t_rect.endX = boost::lexical_cast<float>(attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "tbeginy") == 0) {
					if (strcmp(attrs[i + 1], "") != 0) image_node.t_rect.beginY = boost::lexical_cast<float>(attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "tendy") == 0) {
					if (strcmp(attrs[i + 1], "") != 0) image_node.t_rect.endY = boost::lexical_cast<float>(attrs[i + 1]);
				}
			}
			// TODO エラーチェック	
			node = image_node;
		}
		else if (strcmp(name, "layout") == 0) {
			mofml_parser::layout_end_node layout_node;
			// 内部オブジェクトのレイアウト設定
			for (int i = 0; attrs[i] != NULL; i += 2) {
				if (strcmp(attrs[i], "w") == 0) {
					// 画像のソースを設定 
					if (strcmp(attrs[i + 1], "") != 0) layout_node.width = boost::lexical_cast<int>(attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "h") == 0) {
					// 画像の高さ
					if (strcmp(attrs[i + 1], "") != 0) layout_node.height = boost::lexical_cast<int>(attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "xalign") == 0) {
					if (strcmp(attrs[i + 1], "center") == 0) layout_node.xalign = flow_layout::XALIGN_CENTER;
					else if (strcmp(attrs[i + 1], "left") == 0) layout_node.xalign = flow_layout::XALIGN_LEFT;
					else if (strcmp(attrs[i + 1], "right") == 0) layout_node.xalign = flow_layout::XALIGN_RIGHT;
					else throw std::invalid_argument(std::string("unkonwn align:") + attrs[i + 1]);
				}
				else if (strcmp(attrs[i], "yalign") == 0) {
					if (strcmp(attrs[i + 1], "center") == 0) layout_node.yalign = flow_layout::YALIGN_CENTER;
					else if (strcmp(attrs[i + 1], "top") == 0) layout_node.yalign = flow_layout::YALIGN_TOP;
					else if (strcmp(attrs[i + 1], "bottom") == 0) layout_node.yalign = flow_layout::YALIGN_BOTTOM;
					else throw std::invalid_argument(std::string("unkonwn align:") + attrs[i + 1]);
				}

			}
			// TODO エラーチェック	
			node = layout_node;
			data->backtrack_nodes.push_back(node);// 復帰用にlayout_end_nodeを積む
			node = mofml_parser::layout_start_node();
			data->node_list.push_back(node);// layout_start_nodeを積む
			node = data->last_text_node;// textノードを積む
			data->layout_depth++;
		}
		else if (strcmp(name, "end") == 0) {
			node = mofml_parser::end_node();
			data->end_parse = true;
		}
		else if (strcmp(name, "font") == 0) {
			// フォントを設定
			for (int i = 0; attrs[i] != NULL; i += 2) {
				if (strcmp(attrs[i], "name") == 0) {
					data->last_text_node.font_name = attrs[i + 1];
					node = data->last_text_node;
				}
				else if (strcmp(attrs[i], "size") == 0) {
					data->last_text_node.font_size = boost::lexical_cast<size_t>(attrs[i + 1]);
					node = data->last_text_node;
				}

			}
		}


		data->node_list.push_back(node);// <tag/>でもノード化したい場合があるので、ここで一度積む（有効化はまだ）
		data->backtrack_nodes.push_back(node);
	}
//}}}
//{{{ element_end
	void XMLCALL element_end
	(
		void* user_data,
		const XML_Char* name
	)
	{
		using namespace std;
		using namespace mof::widget;
		user_data_t* data = reinterpret_cast<user_data_t*>(user_data);
		if (data->end_parse) return;

		data->backtrack_nodes.pop_back();

		auto& node = data->backtrack_nodes.back();
		if (node.which() == mofml_parser::TEXT) {
			// last_text_nodeを戻す
			data->last_text_node = boost::get<mofml_parser::text_node>(node);
		}
		else if (node.which() == mofml_parser::LAYOUT_END) {
			// layout_end_nodeを積む
			data->node_list.push_back(node);
			data->backtrack_nodes.pop_back();
			data->layout_depth--;
		}

		data->node_list.push_back(data->backtrack_nodes.back());
	}
//}}}
//{{{ value_handler
	void XMLCALL value_handler
	(
		void* user_data,
		const XML_Char* text,
		int len
	)
	{
		using namespace std;
		using namespace mof::widget;
		
		static char converted_text[512];
		user_data_t* data = reinterpret_cast<user_data_t*>(user_data);	
		if (data->end_parse) return;
		int converted_len = UTF8toSJIS(text, len, converted_text, mof::lengthOf(converted_text));
		
		auto& last_node = data->node_list.back();
		if (last_node.which() == mofml_parser::TEXT) {// text node
			boost::get<mofml_parser::text_node>(last_node).text 
				= mof::tstring(converted_text, converted_len);
		}
	}
//}}}
}

namespace mof
{
namespace widget
{
//{{{ impl
	struct mofml_parser::impl
	{
		XML_Parser parser;
		user_data_t user_data;
		size_t index_node;

		impl()
			: index_node(1)
		{
			clear();
		}
		
		void clear()
		{
			user_data.node_list.clear();
			user_data.backtrack_nodes.clear();
			user_data.backtrack_nodes.push_back(mof::widget::mofml_parser::node());//番兵
			user_data.end_parse = false;
			user_data.last_text_node = mof::widget::mofml_parser::text_node();
			user_data.layout_depth = 0;
			index_node = 0;
		}

	};
//}}}
//{{{ constructor
	mofml_parser::mofml_parser()
		: pimpl_(new impl)
	{
		pimpl_->parser = XML_ParserCreate("shift_jis");
		if (NULL == pimpl_->parser) throw std::invalid_argument("faild: XML_ParserCreate");
		XML_SetElementHandler(pimpl_->parser, element_start, element_end);
		XML_SetCharacterDataHandler(pimpl_->parser, value_handler);
		XML_SetUserData(pimpl_->parser, &(pimpl_->user_data));
		XML_SetUnknownEncodingHandler(pimpl_->parser, XML_JapaneseEncodingHandler, NULL);
		
		mof::tstring header("<document>");
		XML_Parse(pimpl_->parser, header.c_str(), header.size(), XML_FALSE);// これいれないと junk after document elementエラーが発生する
	}
//}}}
//{{{ destructo
	mofml_parser::~mofml_parser()
	{
		XML_ParserFree(pimpl_->parser);
	}
//}}}
//{{{ set_text
	void mofml_parser::set_text(const mof::tstring& text)
	{
		std::stringstream ss;
		char* head = "<col name=\"white\">";
		char* tail = "</col><end/>";
		ss << head << text << tail;// デフォルト状態タグで囲む．XMLの正しい形式に合わせる意味もある．
		pimpl_->clear();
		if (XML_Parse(pimpl_->parser, ss.str().c_str(), ss.str().size(), XML_FALSE) == XML_STATUS_ERROR) {
			throw std::logic_error(XML_ErrorString(XML_GetErrorCode(pimpl_->parser)));
		}
	}
//}}}
//{{{ next_node
	mofml_parser::node mofml_parser::next_node()
	{
		std::vector<mof::widget::mofml_parser::node>& node_list = pimpl_->user_data.node_list;
		if (!has_next_node()) throw std::logic_error("no-node");
		return node_list.at(pimpl_->index_node++);
	}
//}}}
//{{{ has_next_node
	bool mofml_parser::has_next_node() const
	{
		using namespace mof::widget;
		std::vector<mof::widget::mofml_parser::node>& node_list = pimpl_->user_data.node_list;
		while (pimpl_->index_node < node_list.size()) {
			// 空文字のtextノードは無視する
			auto& node = pimpl_->user_data.node_list[pimpl_->index_node];
			if (node.which() != mofml_parser::TEXT || boost::get<mofml_parser::text_node>(node).text != "") break;
			pimpl_->index_node++;
		}
		return pimpl_->index_node < node_list.size();
	}
//}}}
}// namespace widget
}// namespace mof
