#include <mof/widget/flow_layout.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::flow_layout::impl
{
	
	xalign xalign_;
	yalign yalign_;
	size_t width_ , height_;// preferredSize
	std::vector<WidgetView::ptr> views_;
	size_t size_;

	impl(xalign xa, yalign ya, size_t w, size_t h)
	: 
		size_(0), width_(w) , height_(h), xalign_(xa), yalign_(ya)
	{}
	

	~impl()
	{
	}
};
//}}}
//{{{ flow_layout
mof::widget::flow_layout::flow_layout
(
	xalign xa, yalign ya, size_t w, size_t h
)
: pimpl_(new impl(xa, ya, w, h))
{
}
//}}}
//{{{ ~flow_layout
mof::widget::flow_layout::~flow_layout( )
{
}
//}}}
//{{{ add
void mof::widget::flow_layout::add(WidgetView::ptr pView)
{
	mof::Vector2D size = pView->getPreferredSize();
	pimpl_->views_.push_back(pView);
	pimpl_->size_++;
}
//}}}
//{{{ getLength
size_t mof::widget::flow_layout::getLength() const
{
	return pimpl_->size_;
}
//}}}
//{{{ getPreferredSize
mof::Vector2D mof::widget::flow_layout::getPreferredSize() const
{
	return mof::Vector2D
		(
			static_cast<float>(pimpl_->width_),
			static_cast<float>(pimpl_->height_)
		);
}
//}}}
//{{{ connect
void mof::widget::flow_layout::connect(WidgetView* pParentView) 
{
	
	size_t sum_of_width = 0;
	std::vector<float> x_positions; 
	x_positions.reserve(pimpl_->views_.size());
	foreach (WidgetView::ptr pView, pimpl_->views_) {
		mof::Vector2D v = pView->getPreferredSize();
		x_positions.push_back(sum_of_width);
		sum_of_width += v.x;
	}

	size_t i = 0;
	float px = pParentView->getPositionStream().value().x + pParentView->getPreferredSize().x;
	foreach (WidgetView::ptr pView, pimpl_->views_) {
		// 位置の決定
		mof::Vector2D pos(x_positions[i++], 0.0);
		if (pimpl_->xalign_ == XALIGN_CENTER) {
			pos.x += (static_cast<float>(pimpl_->width_) - sum_of_width) / 2;
		}
		else if (pimpl_->xalign_ == XALIGN_RIGHT) {
			pos.x += pimpl_->width_ - sum_of_width;
		}
		if (pimpl_->yalign_ == YALIGN_CENTER) {
			pos.y += (static_cast<float>(pimpl_->height_) - pView->getPreferredSize().y) / 2;
		}
		else if (pimpl_->yalign_ == YALIGN_BOTTOM) {
			pos.y += pimpl_->height_ - pView->getPreferredSize().y;
		}

		pView->getPositionStream() << pos << 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::flow_layout::getAdjacencyAsUp(int index) const
{
	return index;
}
//}}}
//{{{ getAdjacencyAsDown
int mof::widget::flow_layout::getAdjacencyAsDown(int index) const
{
	return index;
}
//}}}
//{{{ getAdjacencyAsLeft
int mof::widget::flow_layout::getAdjacencyAsLeft(int index) const
{
	return mof::rotation_mod(index-1 , pimpl_->size_);
}
//}}}
//{{{ getAdjacencyAsRight
int mof::widget::flow_layout::getAdjacencyAsRight(int index) const
{
	return mof::rotation_mod(index+1 , pimpl_->size_);
}
//}}}
