#include <mof/widget/GridLayout.hpp>
#include <mof/widget/WidgetView.hpp>
#include <mof/widget/VerticalClipping.hpp>
#include <mof/widget/HorizontalClipping.hpp>
#include <mof/streams.hpp>
#include <mof/utilities.hpp>
#include "mof/ConsoleIO.hpp"
#include <algorithm>
#include <vector>
#include <memory>

using std::shared_ptr;
 

//{{{ Impl
struct mof::widget::GridLayout::Impl
{
	
	mof::widget::GridLayout::Direction direction;
	int span , margin;
    int width , height;// preferredSize
    std::vector< shared_ptr<WidgetView> > views;
    std::vector<Vector2D> positions;

	Impl(Direction _direction , int span_ , int margin_ )
	: 
        direction(_direction) , span(span_) , margin(margin_) ,
		width(0) , height(0)
	{}
	

	~Impl()
    {
	}
};
//}}}
//{{{ GridLayout
mof::widget::GridLayout::GridLayout
(
	Direction direction, int span, int margin
)
: m_pImpl( new Impl( direction , span , margin ) )
{
}
//}}}
//{{{ ~GridLayout
mof::widget::GridLayout::~GridLayout( )
{
}
//}}}
//{{{ add
void mof::widget::GridLayout::add(shared_ptr<WidgetView> pView)
{
    mof::Vector2D size = pView->getPreferredSize();
    m_pImpl->views.push_back(pView);
    
    if(m_pImpl->direction == VERTICAL)
    {
        m_pImpl->positions.push_back( mof::Vector2D(0 , m_pImpl->height));
        if( !m_pImpl->views.empty( ) )m_pImpl->height += m_pImpl->margin;
        m_pImpl->width = m_pImpl->width < size.x 
            ? mof::real2int(size.x) 
            : m_pImpl->width;
	    m_pImpl->height += mof::real2int(size.y);
    }
    else if(m_pImpl->direction == HORIZONTAL)
    {
        m_pImpl->positions.push_back( mof::Vector2D(m_pImpl->width , 0));
        if( !m_pImpl->views.empty( ) )m_pImpl->width += m_pImpl->margin;
	    m_pImpl->width += mof::real2int(size.x);
        m_pImpl->height = m_pImpl->height < size.y 
            ? mof::real2int(size.y) 
            : m_pImpl->height;
    }
}
//}}}
//{{{ getLength
size_t mof::widget::GridLayout::getLength() const
{
    return m_pImpl->positions.size();
}
//}}}
//{{{ getPreferredSize
mof::Vector2D mof::widget::GridLayout::getPreferredSize() const
{
    return mof::Vector2D(m_pImpl->width , m_pImpl->height);
}
//}}}
//{{{ connect
void mof::widget::GridLayout::connect(WidgetView* pParentView) 
{
    size_t i = 0;
    if(m_pImpl->direction == VERTICAL)
    {
		float py = pParentView->getPositionStream().value().y + pParentView->getPreferredSize().y;
        foreach(WidgetView::ptr pView, m_pImpl->views) 
        {
            pView->getPositionStream() << m_pImpl->positions[i++] << pParentView->getPositionStream();
			{
				// 幅は親のサイズに合わせる
            	mof::Vector2D filter(1, 0);
				float cy = pView->getPositionStream().value().y + pView->getPreferredSize().y;
				float d = py - cy;// 親と子の終点の差
            	pView->getSizeStream() 
                	<< -pView->getPreferredSize()
                	<< mof::makeFilterHandler(pParentView->getSizeStream().toManipulator(), filter)//TODO この時点のフレームが非ゼロだと問題が起こるかも？
					<< std::make_shared<VerticalClipping>(pParentView->getSizeStream().toManipulator(), pView->getPreferredSize().y, d);
			}
        }
    }
    else
    {
		float px = pParentView->getPositionStream().value().x + pParentView->getPreferredSize().x;
        foreach(WidgetView::ptr pView, m_pImpl->views) 
        {
            pView->getPositionStream() << m_pImpl->positions[i++] << pParentView->getPositionStream();
			{
				// 高さは親のサイズに合わせる
            	mof::Vector2D filter(0, 1);
				float cx = pView->getPositionStream().value().x + pView->getPreferredSize().x;
				float d = px - cx;// 親と子の終点の差
            	pView->getSizeStream() 
                	<< -pView->getPreferredSize()
                	<< mof::makeFilterHandler(pParentView->getSizeStream().toManipulator(), filter)//TODO この時点のフレームが非ゼロだと問題が起こるかも？
					<< std::make_shared<HorizontalClipping>(pParentView->getSizeStream().toManipulator(), pView->getPreferredSize().x, d);
			}

        }
    }
}
//}}}
//{{{ getAdjacencyAsUp
int mof::widget::GridLayout::getAdjacencyAsUp(int index) const
{
    if(m_pImpl->direction == VERTICAL)return mof::rotation_mod(index-1 , m_pImpl->positions.size());
    else return index;
}
//}}}
//{{{ getAdjacencyAsDown
int mof::widget::GridLayout::getAdjacencyAsDown(int index) const
{
    if(m_pImpl->direction == VERTICAL)return mof::rotation_mod(index+1 , m_pImpl->positions.size());
    else return index;
}
//}}}
//{{{ getAdjacencyAsLeft
int mof::widget::GridLayout::getAdjacencyAsLeft(int index) const
{
    if(m_pImpl->direction == HORIZONTAL)return mof::rotation_mod(index-1 , m_pImpl->positions.size());
    else return index;
}
//}}}
//{{{ getAdjacencyAsRight
int mof::widget::GridLayout::getAdjacencyAsRight(int index) const
{
    if(m_pImpl->direction == HORIZONTAL)return mof::rotation_mod(index+1 , m_pImpl->positions.size());
    else return index;
}
//}}}

