#pragma once
#include <mof/widget/Menu.hpp>
#include <mof/widget/GridLayout.hpp>
#include <mof/widget/Container.hpp>
#include <memory>
#include <algorithm>

using mof::widget::Container;
using mof::widget::WidgetView;
using mof::widget::GridLayout;
using mof::widget::MenuItem;

namespace mof
{
namespace widget
{
//{{{ Impl
	struct Menu::Impl
	{
	    std::vector< boost::function0<void> > actions;
        std::shared_ptr<Container> pView; 
	    int currentIndex;
	
	    Impl()
	    : pView() , currentIndex(0)
	    {}
	};
//}}}
//{{{ constructor
	Menu::Menu
	(
	    const MenuItem& front ,
	    const MenuItem& back
	)
	: m_pImpl(new Impl)
	{
	    int length = &back - &front + 1;
	    std::vector< std::shared_ptr<mof::widget::WidgetView> > views;
	    for(int i = 0 ; i < length ; i++)
	    {
	        views.push_back( std::shared_ptr<WidgetView>( (&front + i)->view( ) ) );
			m_pImpl->actions.push_back((&front + i)->action);
	    }
	
	    m_pImpl->pView = std::make_shared<Container>
	    (
	        views.front() , views.back() , 
            makeFactoryMethod<GridLayout>(GridLayout::VERTICAL , 0)
        );
	
	    m_pImpl->pView->getAt(m_pImpl->currentIndex)->focus();
	}
//}}}
//{{{ constructor
	Menu::Menu
	(
	    const MenuItem& front ,
	    const MenuItem& back ,
        const FactoryMethod<Layout>& layout
	)
	: m_pImpl(new Impl)
	{
	    int length = &back - &front + 1;
	    std::vector< std::shared_ptr<mof::widget::WidgetView> > views;
	    for(int i = 0 ; i < length ; i++)
	    {
	        views.push_back( std::shared_ptr<WidgetView>( (&front + i)->view( ) ) );
			m_pImpl->actions.push_back((&front + i)->action);
	    }
	
	    m_pImpl->pView = std::make_shared<Container>
	    (
	        views.front() , views.back() , layout
        );
	
	    m_pImpl->pView->getAt(m_pImpl->currentIndex)->focus();// 初期カーソル位置
	}
//}}}
//{{{ destructor
	Menu::~Menu()
	{
	}
//}}}
//{{{ show
	mof::FrameNumber Menu::show(bool imidiately)
	{
	    return m_pImpl->pView->show(imidiately);
	}
//}}}
//{{{ hide
	mof::FrameNumber Menu::hide(bool imidiately)
	{
	    return m_pImpl->pView->hide(imidiately);
	}
//}}}
//{{{ getView
	const std::shared_ptr<WidgetView> Menu::getView( ) const
	{
	    return m_pImpl->pView;
	}
//}}}
//{{{ getView
	std::shared_ptr<WidgetView> Menu::getView( ) 
	{
	    return m_pImpl->pView;
	}
//}}}
//{{{ update
	void Menu::update()
	{
	    m_pImpl->pView->update();
	}
//}}}
//{{{ up
	FrameNumber Menu::up()
	{
	    int index = m_pImpl->pView->getAdjacencyAsUp(m_pImpl->currentIndex);
	    if(index == m_pImpl->currentIndex)return 0;
	    FrameNumber period_blured = m_pImpl->pView->getAt(m_pImpl->currentIndex)->blur();
	    m_pImpl->currentIndex = index;
	    FrameNumber period_focused = m_pImpl->pView->getAt(m_pImpl->currentIndex)->focus();
		return max(period_blured, period_focused);
	}
//}}}
//{{{ down
	FrameNumber Menu::down()
	{
	    int index = m_pImpl->pView->getAdjacencyAsDown(m_pImpl->currentIndex);
	    if(index == m_pImpl->currentIndex)return 0;
		mof::Vector2D position = m_pImpl->pView->getAt(m_pImpl->currentIndex)->getPositionStream().value();
		mof::Vector2D size = m_pImpl->pView->getAt(m_pImpl->currentIndex)->getSizeStream().value();
		DEBUG_PRINT("down:" << position << ":" << size);

	    FrameNumber period_blured = m_pImpl->pView->getAt(m_pImpl->currentIndex)->blur();
	    m_pImpl->currentIndex = index;
	    FrameNumber period_focused = m_pImpl->pView->getAt(m_pImpl->currentIndex)->focus();
		return max(period_blured, period_focused);
	}
//}}}
//{{{ left 
	FrameNumber Menu::left()
	{
	    int index = m_pImpl->pView->getAdjacencyAsLeft(m_pImpl->currentIndex);
	    if(index == m_pImpl->currentIndex)return 0;
	    FrameNumber period_blured = m_pImpl->pView->getAt(m_pImpl->currentIndex)->blur();
	    m_pImpl->currentIndex = index;
	    FrameNumber period_focused = m_pImpl->pView->getAt(m_pImpl->currentIndex)->focus();
		return max(period_blured, period_focused);
	}
//}}}
//{{{ right 
	FrameNumber Menu::right()
	{
	    int index = m_pImpl->pView->getAdjacencyAsRight(m_pImpl->currentIndex);
	    if(index == m_pImpl->currentIndex)return 0;
	    FrameNumber period_blured = m_pImpl->pView->getAt(m_pImpl->currentIndex)->blur();
	    m_pImpl->currentIndex = index;
	    FrameNumber period_focused = m_pImpl->pView->getAt(m_pImpl->currentIndex)->focus();
		return max(period_blured, period_focused);
	}
//}}}
//{{{ performAction
	FrameNumber Menu::performAction() const
	{
	    m_pImpl->actions[m_pImpl->currentIndex]();
	    return m_pImpl->pView->getAt(m_pImpl->currentIndex)->click();
	}
//}}}
//{{{ getSelectedIndex
	int Menu::getSelectedIndex() const
	{
	    return m_pImpl->currentIndex;
	}
//}}}
//{{{ getSelectedView
	std::shared_ptr<WidgetView> Menu::getSelectedView() const
	{
	    return m_pImpl->pView->getAt(m_pImpl->currentIndex);
	}
//}}}
}
}
