#include "mof/widget/TextCompiler.hpp"
#include "mof/widget/WidgetView.hpp"
#include <algorithm>
#include <boost/regex.hpp>
#include <memory>
#include "mof/ConsoleIO.hpp"
#include "mof/Sprite.hpp"
#include "mof/Font.hpp"
#include "mof/Board.hpp"
#include "mof/widget/GridLayout.hpp"
#include "mof/widget/flow_layout.hpp"
#include "mof/widget/Container.hpp"
#include "mof/streams.hpp"
#include "mof/widget/mofml_parser.hpp"

namespace {
//{{{ PageBehaviorSettings
	struct PageBehaviorSettings
	{
		mof::Manipulator<mof::Vector2D>::Handler position;
		mof::Manipulator<mof::Color4f>::Handler color;
		mof::FrameNumber periodOfPosition;
		mof::FrameNumber periodOfColor;

		std::shared_ptr<PageBehaviorSettings> clone()
		{
			std::shared_ptr<PageBehaviorSettings> p = std::make_shared<PageBehaviorSettings>();
			p->periodOfPosition = periodOfPosition;
			p->periodOfColor = periodOfColor;
			p->position = position;
			p->color = color;
			return p;
		}
	};
//}}}
    class SpriteWidgetViewAdapter : public mof::widget::WidgetView
    {
        std::unique_ptr<mof::Sprite> pSprite_;
		mof::Vector2D preferredSize_;
		std::shared_ptr<const PageBehaviorSettings> openSettings_;
		std::shared_ptr<const PageBehaviorSettings> closeSettings_;
		mof::ReferenceWrapper<mof::Vector2D> positionWrapper_;
		mof::ReferenceWrapper<mof::Color4f> colorWrapper_;
    public:
//{{{ constructor
        SpriteWidgetViewAdapter
		(
			std::unique_ptr<mof::Sprite>&& pSprite,
			std::shared_ptr<const PageBehaviorSettings> openSettings,
			std::shared_ptr<const PageBehaviorSettings> closeSettings
		)
        : pSprite_(std::move(pSprite)), openSettings_(openSettings), closeSettings_(closeSettings)
        {
			pSprite_->setVisible(true);
            pSprite_->getPositionStream() << m_positionStream << positionWrapper_.makeRef(mof::Vector2D(0, 0));
            pSprite_->getColorStream() << m_colorStream << colorWrapper_.makeRef(mof::Color4f(0, 1, 1, 1));
			preferredSize_ = pSprite_->getSizeStream().value();
			m_sizeStream << preferredSize_;
        }
//}}}
//{{{ destructor
        virtual ~SpriteWidgetViewAdapter(){}
//}}}
//{{{ show
        virtual mof::FrameNumber show(bool immidiately)
		{
			/*if (immidiately) {
				positionWrapper_.replace(openSettings_->periodOfPosition, openSettings_->position);
				colorWrapper_.replace(openSettings_->periodOfColor, openSettings_->color);
				return 0;
			}*/
			positionWrapper_.replace(0, getPositionStream(), openSettings_->position);
			colorWrapper_.replace(0, getColorStream(), openSettings_->color);
			return max(openSettings_->periodOfPosition, openSettings_->periodOfColor);
		}
//}}}
//{{{ hide
        virtual mof::FrameNumber hide(bool immidiately)
		{
			/*if (immidiately) {
				positionWrapper_.replace(closeSettings_->periodOfPosition, closeSettings_->position);
				colorWrapper_.replace(closeSettings_->periodOfColor, closeSettings_->color);
				return 0;
			}*/
			positionWrapper_.replace(0, getPositionStream(), closeSettings_->position);
			colorWrapper_.replace(0, getColorStream(), closeSettings_->color);
			return max(closeSettings_->periodOfPosition, closeSettings_->periodOfColor);
		}
//}}}
//{{{ focus
        virtual mof::FrameNumber focus(bool immidiately){ return show(immidiately); }
//}}}
//{{{ blur
        virtual mof::FrameNumber blur(bool immidiately){ return hide(immidiately); }
//}}}
//{{{ click
        virtual mof::FrameNumber click(bool){return 0;}
//}}}
//{{{ getPreferredSize
		virtual mof::Vector2D getPreferredSize() const { return preferredSize_; }
//}}} 
//{{{ update
        void update()
        {
            m_positionStream.update();
            m_sizeStream.update();
            m_colorStream.update();
            pSprite_->getPositionStream().update();
            pSprite_->getSizeStream().update();
            pSprite_->getColorStream().update();
            pSprite_->getRectangleCoordinatesStream().update();
        }
//}}}
//{{{ draw
        void draw() const { pSprite_->draw(); }
//}}} 
//{{{ setVisible
        void setVisible(bool visible) { pSprite_->setVisible(visible); }
//}}} 
    };


}

namespace mof
{
namespace widget
{
//{{{ Impl
    struct TextCompiler::Impl
    {
        mof::Font font_;
		std::shared_ptr<PageBehaviorSettings> openSettings_;
		std::shared_ptr<PageBehaviorSettings> closeSettings_;
		boost::function<mof::Texture::ptr (const mof::tstring&)> texture_factory_;
		mof::widget::mofml_parser parser;
//{{{ constructor
	    Impl(const mof::Font& font)
        : font_(font)
        {
			openSettings_ = std::make_shared<PageBehaviorSettings>();
			closeSettings_ = std::make_shared<PageBehaviorSettings>();

			openSettings_->position = closeSettings_->position = mof::makeConstantHandler(mof::Vector2D(0, 0));
			openSettings_->color = mof::makeConstantHandler(mof::Color4f(1, 1, 1, 1));
			closeSettings_->color = mof::makeConstantHandler(mof::Color4f(0, 1, 1, 1));
			openSettings_->periodOfPosition = openSettings_->periodOfColor = 0;
			closeSettings_->periodOfPosition = closeSettings_->periodOfColor = 0;
        }
//}}}
//{{{ make_line_widget_
		mof::widget::WidgetView::ptr make_line_widget_(const std::vector<mof::widget::WidgetView::ptr>& widgets_on_a_line) 
		{
			if (widgets_on_a_line.size() == 1) {
				return widgets_on_a_line.front();
			}
			else  {
				WidgetView::ptr p = std::make_shared<Container>
               		(
                  	 	widgets_on_a_line.front(), widgets_on_a_line.back() ,
                   	 	mof::makeFactoryMethod<GridLayout>(GridLayout::HORIZONTAL , 0 )
               		);
				return p;
			}
		}
//}}}
//{{{ make_layout_widget_
		mof::widget::WidgetView::ptr make_layout_widget_
		(
			const std::vector<mof::widget::WidgetView::ptr>& widgets_in_layout, 
			const mof::widget::mofml_parser::layout_end_node& layout_node
		) 
		{	
			using namespace mof::widget;

			flow_layout::xalign xa = layout_node.xalign;
			flow_layout::yalign ya = layout_node.yalign;
			size_t width = layout_node.width;
			size_t height = layout_node.height;
			WidgetView::ptr p = std::make_shared<Container>
           		(
               	 	widgets_in_layout.front(), widgets_in_layout.back() ,
               	 	mof::makeFactoryMethod<flow_layout>(xa, ya, width, height)
           		);
			return p;
		}
//}}}
//{{{ recursive_for_widget
		void recursive_for_widget
		(
	    	const mof::tstring& text,
    		std::vector<mof::widget::WidgetView::ptr>& result,
   			const mof::Font& font
		)
		{
			using namespace std;
			using namespace mof::widget;

			// もう片方にも反映すること

			parser.set_text(text);
			mof::font_context context;
			std::vector<std::vector<mof::widget::WidgetView::ptr>> layout_widgets(1);

			while (parser.has_next_node()) {
				mofml_parser::node node = parser.next_node();
				// ノードの内容を解釈してコンテキストに変換
				if (node.which() == mofml_parser::TEXT) {
					auto& text_node = boost::get<mofml_parser::text_node>(node);
					context.font_color = text_node.font_color;
					mof::tstring font_name = text_node.font_name == ""? font.name() : text_node.font_name;
					size_t font_size = text_node.font_size == 0? font.size() : text_node.font_size;
					context.edge = text_node.font_edge;
					std::unique_ptr<mof::Sprite> pSprite
						(
							mof::Sprite::createTextSprite(mof::Font(font_name.c_str(), font_size, context), text_node.text)
						);
					auto p = std::make_shared<SpriteWidgetViewAdapter>(std::move(pSprite), openSettings_, closeSettings_);
					layout_widgets.back().push_back(p);// 新しいノードを積む
				}
				else if (node.which() == mofml_parser::IMAGE) {
					auto& image_node = boost::get<mofml_parser::image_node>(node);
					std::unique_ptr<mof::Sprite> pSprite
						(
						 	new Sprite
							(
								Rectangle<int>(0, 0, image_node.width, image_node.height),		
								texture_factory_(image_node.src),
								image_node.t_rect
							)
						);
					auto p = std::make_shared<SpriteWidgetViewAdapter>(std::move(pSprite), openSettings_, closeSettings_);
					layout_widgets.back().push_back(p);// 新しいノードを積む
				}
				else if (node.which() == mofml_parser::NEW_LINE || node.which() == mofml_parser::END) {
					if (!layout_widgets[0].empty()) {
						// 1行分のウィジェットをまとめて、横方向に配置
						result.push_back(make_line_widget_(layout_widgets[0]));
						layout_widgets[0].clear();
					}
				}
				else if (node.which() == mofml_parser::LAYOUT_START) {
					layout_widgets.push_back(std::vector<mof::widget::WidgetView::ptr>());
				}
				else if (node.which() == mofml_parser::LAYOUT_END) {
					auto& layout_node = boost::get<mofml_parser::layout_end_node>(node);
					auto& widgets = layout_widgets.back();
					if (!widgets.empty()) (++layout_widgets.rbegin())->push_back(make_layout_widget_(widgets, layout_node));
					layout_widgets.pop_back();
				}

			}

		}
//}}}	
//{{{ recursive_for_board
		void recursive_for_board
		(
	    	const mof::tstring& text,
    		std::vector<mof::Board::ptr>& result,
   			const mof::Font& font
		)
		{
			using namespace std;
			using namespace mof::widget;
			
			// TODO 今は単純なテキストのみサポート

			parser.set_text(text);
			mof::font_context context;

			// もう片方にも反映すること

			while (parser.has_next_node()) {
				mofml_parser::node node = parser.next_node();
				// ノードの内容を解釈してコンテキストに変換
				if (node.which() == mofml_parser::TEXT) {
					auto& text_node = boost::get<mofml_parser::text_node>(node);
					context.font_color = text_node.font_color;
					mof::tstring font_name = text_node.font_name == ""? font.name() : text_node.font_name;
					size_t font_size = text_node.font_size == 0? font.size() : text_node.font_size;
					context.edge = text_node.font_edge;

					auto p = 
						std::shared_ptr<Board>
						(
							mof::Board::create_text_board
							(
								mof::Font(font_name.c_str(), font_size, context),
								text_node.text 
							).release()
						);
					result.push_back(p);// 新しいノードを積む
					return; // 即リターン
				}
				else if (node.which() == mofml_parser::IMAGE) {
					// 無視
				}
				else if (node.which() == mofml_parser::NEW_LINE || node.which() == mofml_parser::END) {
					// 無視
				}
				else if (node.which() == mofml_parser::LAYOUT_START) {
					// 無視
				}
				else if (node.which() == mofml_parser::LAYOUT_END) {
					// 無視
				}

			}

		}
//}}}	
    };
//}}}
//{{{ constructor
    TextCompiler::TextCompiler(const mof::Font& font)
    : pImpl_(new Impl(font))
    { 
		pImpl_->texture_factory_ = [](const mof::tstring& path){ return std::make_shared<mof::Texture>(path); };
    }
//}}}
//{{{ constructor
    TextCompiler::TextCompiler(const mof::Font& font, const boost::function<mof::Texture::ptr (const mof::tstring&)>& factory)
    : pImpl_(new Impl(font))
    { 
		pImpl_->texture_factory_ = factory;
    }
//}}}
//{{{ destructor
    TextCompiler::~TextCompiler()
    { 
    }
//}}}
//{{{ set_texture_factory
	void TextCompiler::set_texture_factory
	(
		const boost::function<mof::Texture::ptr (const mof::tstring&)>& factory
	)
	{
		pImpl_->texture_factory_ = factory;
	}
//}}}
//{{{ compile_to_widget
    std::auto_ptr<mof::widget::WidgetView> TextCompiler::compile_to_widget(const mof::tstring& text)
    {
        using namespace mof::widget;
        using namespace boost;
        using mof::Sprite;
        using mof::widget::GridLayout;
	
	    std::vector<WidgetView::ptr> result;
	    pImpl_->recursive_for_widget(text, result, pImpl_->font_);
        
        return std::auto_ptr<WidgetView>
            (
                new Container
                (
                   result.front(), result.back() ,
                   mof::makeFactoryMethod<GridLayout>(GridLayout::VERTICAL , 0 )
                ) 
            );
    }
//}}}
//{{{ compile_to_board
    std::shared_ptr<mof::Board> TextCompiler::compile_to_board(const mof::tstring& text)
    {
        using namespace mof::widget;
        using namespace boost;
	
	    std::vector<mof::Board::ptr> result;
	    pImpl_->recursive_for_board(text, result, pImpl_->font_);
        
		// 今はresultのサイズは1

        return result.front();
    }
//}}}
//{{{ setBehaviorOnPosition
	void TextCompiler::setBehaviorOnPosition
	(
		BehaviorTarget target,
		mof::Manipulator<mof::Vector2D>::Handler position,
		FrameNumber period
	)
	{
		switch (target) {
			case TextCompiler::PAGE_OPEN : 
			{
				std::shared_ptr<PageBehaviorSettings> p = pImpl_->openSettings_;	
				p->position = position;
				p->periodOfPosition = period;
				pImpl_->openSettings_ = p;
				break;
			}
			case TextCompiler::PAGE_CLOSE : 
			{
				std::shared_ptr<PageBehaviorSettings> p = pImpl_->closeSettings_;	
				p->position = position;
				p->periodOfPosition = period;
				pImpl_->closeSettings_ = p;
				break;
			}

		}
	}
//}}}	
//{{{ setBehaviorOnColor
	void TextCompiler::setBehaviorOnColor
	(
		BehaviorTarget target,
		mof::Manipulator<mof::Color4f>::Handler color,
		FrameNumber period
	)
	{
		switch (target) {
			case TextCompiler::PAGE_OPEN : 
			{
				std::shared_ptr<PageBehaviorSettings> p = pImpl_->openSettings_;	
				p->color = color;
				p->periodOfColor = period;
				pImpl_->openSettings_ = p;
				break;
			}
			case TextCompiler::PAGE_CLOSE : 
			{
				std::shared_ptr<PageBehaviorSettings> p = pImpl_->closeSettings_;	
				p->color = color;
				p->periodOfColor = period;
				pImpl_->closeSettings_ = p;
				break;
			}

		}

	}
///}}}
}// namespace widget
}// namespace mof

