#pragma once
#include <mof/widgets.hpp>
#include <mof/utilities.hpp>
#include <mof/EventScheduler.hpp>
#include <mof/streams.hpp>
#include <boost/bind.hpp>
#include <memory>
#include <algorithm>

using namespace mof::widget;
using std::shared_ptr;
using std::make_shared;
using mof::lastOf;
using std::vector;

namespace
{
//{{{ FrameBehaviorSettings
	struct FrameBehaviorSettings
	{
		mof::Manipulator<mof::Vector2D>::Handler position;
		mof::Manipulator<mof::Vector2D>::Handler size;
		mof::Manipulator<mof::Color4f>::Handler color;
		//mof::Manipulator<bool>::Handler visible;
		mof::FrameNumber periodOfPosition;
		mof::FrameNumber periodOfSize;
		mof::FrameNumber periodOfColor;

		std::shared_ptr<FrameBehaviorSettings> clone()
		{
			std::shared_ptr<FrameBehaviorSettings> p = std::make_shared<FrameBehaviorSettings>();
			p->periodOfPosition = periodOfPosition;
			p->periodOfSize = periodOfSize;
			p->periodOfColor = periodOfColor;
			p->position = position;
			p->size = size;
			p->color = color;
			//p->visible = visible;
			return p;
		}
	};
}
//}}}
namespace mof
{
namespace widget
{
//{{{ Impl
	struct Frame::Impl
	{
        //shared_ptr<WidgetView> pCaptionView; 
        shared_ptr<WidgetView> pContentBackgroundView; 
        shared_ptr<WidgetView> pTitleBackgroundView; 
        shared_ptr<WidgetView> pContainer; 
		FrameBehaviorSettings openSettings_;		
		FrameBehaviorSettings closeSettings_;		
		mof::ReferenceWrapper<mof::Vector2D> position_wrapper_;
		mof::ReferenceWrapper<mof::Vector2D> size_wrapper_;
		mof::ReferenceWrapper<mof::Color4f> color_wrapper_;
		mof::EventScheduler scheduler_;
		

	    Impl()
	    : 
            pContentBackgroundView( ) ,
            pContainer( )
	    {
			openSettings_.periodOfPosition = 0;	
			openSettings_.periodOfSize = 0;	
			openSettings_.periodOfColor = 0;	
			openSettings_.position = mof::makeConstantHandler(mof::Vector2D(0, 0));
			openSettings_.size = mof::makeConstantHandler(mof::Vector2D(0, 0));
			openSettings_.color = mof::makeConstantHandler(mof::Color4f(1, 1, 1, 1));
			
			closeSettings_.periodOfPosition = 0;	
			closeSettings_.periodOfSize = 0;	
			closeSettings_.periodOfColor = 0;	
			closeSettings_.position = mof::makeConstantHandler(mof::Vector2D(0, 0));
			closeSettings_.size = mof::makeConstantHandler(mof::Vector2D(0, 0));
			closeSettings_.color = mof::makeConstantHandler(mof::Color4f(1, 1, 1, 1));

		}
	};
//}}}
//{{{ constructor
	Frame::Frame
	(
        const mof::tstring& caption,
        shared_ptr<WidgetView> pContentView,
        const FactoryMethod<WidgetView>& contentBackground, 
        const FactoryMethod<WidgetView>& titleBackground,
		const FactoryMethod<AbstractTextCompiler>& text_compiler_factory
	)
	: m_pImpl(new Impl)
	{
		
		auto text_compiler = std::shared_ptr<AbstractTextCompiler>(text_compiler_factory());
		text_compiler->setBehaviorOnColor
		(
			AbstractTextCompiler::PAGE_CLOSE, 
			mof::makeConstantHandler<mof::Color4f>(mof::Color4f(1, 1, 1, 1)),
			0
		);
        m_pImpl->pContentBackgroundView = shared_ptr<WidgetView>( contentBackground( ) );
        m_pImpl->pTitleBackgroundView = shared_ptr<WidgetView>( titleBackground( ) );

        std::shared_ptr<WidgetView> tmp; 
        if (caption != "") {
        	shared_ptr<WidgetView> pCaptionView = text_compiler->compile(caption);
            shared_ptr<WidgetView> children1[] = {
                m_pImpl->pTitleBackgroundView ,
                make_shared<ClippingView>(pCaptionView),// キャプションをクリッピング
            };
            m_pImpl->pContainer = make_shared<Container>( 
                children1[0] ,
                lastOf( children1 ) ,
                makeFactoryMethod<OverlapLayout>( pCaptionView->getSizeStream().value() )
            );
			
			shared_ptr<WidgetView> children2[] = {
                m_pImpl->pContainer ,
                make_shared<ClippingView>(pContentView),// コンテンツをクリッピング
            };
            tmp = make_shared<Container>( 
                children2[0],
                lastOf(children2),
                makeFactoryMethod<GridLayout>(GridLayout::VERTICAL, 0, 10)
            );

        }
		else {
            tmp = m_pImpl->pContainer = pContentView;
		}

        {
            shared_ptr<WidgetView> children[] = {
                m_pImpl->pContentBackgroundView ,
                tmp ,
            };
            m_pImpl->pContainer = make_shared<Container>( 
                children[0] ,
                lastOf( children ) ,
                makeFactoryMethod<OverlapLayout>( tmp->getPreferredSize() )
            );
			m_pImpl->pContainer->setVisible(false);
        }
		m_pImpl->pContainer->getPositionStream() << m_pImpl->position_wrapper_.makeRef(mof::Vector2D(0, 0));
		m_pImpl->pContainer->getSizeStream() << m_pImpl->size_wrapper_.makeRef(mof::Vector2D(0, 0));
		m_pImpl->pContainer->getColorStream() << m_pImpl->color_wrapper_.makeRef(mof::Color4f(0, 1, 1, 1));
	}
//}}}
//{{{ destructor
	Frame::~Frame()
	{
	}
//}}}
//{{{ show
	mof::FrameNumber Frame::show(bool imidiately)
	{
		m_pImpl->scheduler_.clear();// hideのイベントが残っているなら消す
		m_pImpl->pContainer->setVisible(true);
		m_pImpl->position_wrapper_.replace(0, m_pImpl->pContainer->getPositionStream(), m_pImpl->openSettings_.position);
		m_pImpl->size_wrapper_.replace(0, m_pImpl->pContainer->getSizeStream(), m_pImpl->openSettings_.size);
		m_pImpl->color_wrapper_.replace(0, m_pImpl->pContainer->getColorStream(), m_pImpl->openSettings_.color);
		FrameNumber period = m_pImpl->pContainer->show(imidiately);
		period = max(period, m_pImpl->openSettings_.periodOfPosition);
		period = max(period, m_pImpl->openSettings_.periodOfSize);
		period = max(period, m_pImpl->openSettings_.periodOfColor);
		return period;
	}
//}}}
//{{{ hide
	mof::FrameNumber Frame::hide(bool imidiately)
	{
		m_pImpl->position_wrapper_.replace(0, m_pImpl->pContainer->getPositionStream(), m_pImpl->closeSettings_.position);
		m_pImpl->size_wrapper_.replace(0, m_pImpl->pContainer->getSizeStream(), m_pImpl->closeSettings_.size);
		m_pImpl->color_wrapper_.replace(0, m_pImpl->pContainer->getColorStream(), m_pImpl->closeSettings_.color);
		m_pImpl->pContainer->hide(imidiately);
		FrameNumber period = m_pImpl->closeSettings_.periodOfPosition;
		period = max(period, m_pImpl->closeSettings_.periodOfSize);
		period = max(period, m_pImpl->closeSettings_.periodOfColor);
		m_pImpl->scheduler_.addEvent(period, boost::bind(&WidgetView::setVisible, m_pImpl->pContainer.get(), false));
		return period;
	}
//}}}
//{{{ getView
	const std::shared_ptr<WidgetView> Frame::getView( ) const
	{
        return m_pImpl->pContainer;
	}
//}}}
//{{{ getView
	std::shared_ptr<WidgetView> Frame::getView( ) 
	{
        return m_pImpl->pContainer;
	}
//}}}
//{{{ update
	void Frame::update()
	{
		m_pImpl->scheduler_.update();
	    m_pImpl->pContainer->update();
	}
//}}}
//{{{ setCaption
	void Frame::setCaption(const tstring& title)
	{
	}
//}}}
//{{{ setBehaviorOnPosition
	void Frame::setBehaviorOnPosition(BehaviorTarget target, mof::Manipulator<mof::Vector2D>::Handler position, mof::FrameNumber period)
	{
		switch (target) {
			case Frame::FRAME_OPEN :
				m_pImpl->openSettings_.position = position;
				m_pImpl->openSettings_.periodOfPosition = period;
				break;
			case Frame::FRAME_CLOSE :
				m_pImpl->closeSettings_.position = position;
				m_pImpl->closeSettings_.periodOfPosition = period;
				break;
		}
	}
//}}}
//{{{ setBehaviorOnSize
	void Frame::setBehaviorOnSize(BehaviorTarget target, mof::Manipulator<mof::Vector2D>::Handler size, mof::FrameNumber period)
	{
		switch (target) {
			case Frame::FRAME_OPEN :
				m_pImpl->openSettings_.size = size;
				m_pImpl->openSettings_.periodOfSize = period;
				break;
			case Frame::FRAME_CLOSE :
				m_pImpl->closeSettings_.size = size;
				m_pImpl->closeSettings_.periodOfSize = period;
				break;

		}
	}
//}}}
//{{{ setBehaviorOnColor
	void Frame::setBehaviorOnColor(BehaviorTarget target, mof::Manipulator<mof::Color4f>::Handler color, mof::FrameNumber period)
	{
		switch (target) {
			case Frame::FRAME_OPEN :
				m_pImpl->openSettings_.color = color;
				m_pImpl->openSettings_.periodOfColor = period;
				break;
			case Frame::FRAME_CLOSE :
				m_pImpl->closeSettings_.color = color;
				m_pImpl->closeSettings_.periodOfColor = period;
				break;
		}
	}
//}}}
}
}
