//
// C++ Implementation: StackableContainer
//
// Description: 
//
//
// Author: Erik Hjortsberg <erik.hjortsberg@gmail.com>, (C) 2007
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.//
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "StackableContainer.h"

using namespace CEGUI;

namespace EmberOgre {

namespace Gui {

StackableContainer::StackableContainer(CEGUI::Window* window)
: mWindow(window), mInnerContainerWindow(0), mPadding(0), mFlowDirection(Vertical)
{
	if (mWindow) {
		mChildAddedConnection = mWindow->subscribeEvent(CEGUI::Window::EventChildAdded, CEGUI::Event::Subscriber(&StackableContainer::window_ChildAdded, this));
	} 
}


StackableContainer::~StackableContainer()
{
	cleanup();
	if (mWindowDestructionConnection.isValid()) {	
		mWindowDestructionConnection->disconnect();
		mWindowDestructionConnection = CEGUI::Event::Connection();
	}
}

void StackableContainer::cleanup()
{
	for (ConnectorStore::iterator I = mChildConnections.begin(); I != mChildConnections.end(); ++I) {
		I->second->disconnect();
	}
	mChildConnections.clear();
	if (mChildRemovedConnection.isValid()) {	
		mChildRemovedConnection->disconnect();
		mChildRemovedConnection = CEGUI::Event::Connection();
	}
	if (mChildAddedConnection.isValid()) {	
		mChildAddedConnection->disconnect();
		mChildAddedConnection = CEGUI::Event::Connection();
	}
}

CEGUI::Window* StackableContainer::getWindow()
{
	return mWindow;
}

void StackableContainer::setPadding(int padding)
{
	mPadding = padding;
}

int StackableContainer::getPadding() const
{
	return mPadding;
}


void StackableContainer::repositionWindows()
{
	if (mInnerContainerWindow) {
		float accumulatedWidth(0);
		float accumulatedHeight(0);
		float maxHeight(0);
		float maxWidth(0);
		
		///iterate over all child window and rearrange them
		size_t childCount = mInnerContainerWindow->getChildCount();
		for(size_t i = 0; i < childCount; ++i) {
			CEGUI::Window* childWindow = mInnerContainerWindow->getChildAtIdx(i);
			///only use those windows that are visible
			if (childWindow->isVisible()) {
				float absHeight = childWindow->getHeight().asAbsolute(1);
				float absWidth = childWindow->getWidth().asAbsolute(1);
				if (mFlowDirection == Horizontal) {
					maxHeight = std::max<float>(maxHeight, absHeight);
					childWindow->setPosition(UVector2(UDim(0, accumulatedWidth), UDim(0,0)));
					accumulatedWidth += absWidth + mPadding;
				} else {
					maxWidth= std::max<float>(maxWidth, absWidth);
					childWindow->setPosition(UVector2(UDim(0, 0), UDim(0,accumulatedHeight)));
					accumulatedHeight += absHeight + mPadding;
				}
			}
		}
		if (mFlowDirection == Horizontal) {
			accumulatedWidth -= mPadding;
			if (mInnerContainerWindow->getWidth().asRelative(0) != 1) {
				mInnerContainerWindow->setWidth(UDim(0, accumulatedWidth));
			}
		} else {
			accumulatedHeight -= mPadding;
			if (mInnerContainerWindow->getHeight().asRelative(0) != 1) {
				mInnerContainerWindow->setHeight(UDim(0,accumulatedHeight));
			}
		}
		if (mInnerContainerWindow->getParent()) {
			mInnerContainerWindow->getParent()->performChildWindowLayout();
		}
	}
}

float StackableContainer::getAbsoluteHeight()
{
	return mWindow->getSize().d_y.asAbsolute(0);
}

float StackableContainer::getAbsoluteWidth()
{
	return mWindow->getSize().d_x.asAbsolute(0);
}

void StackableContainer::setFlowDirection(StackableContainer::FlowDirection flowDirection)
{
	mFlowDirection = flowDirection;
}

StackableContainer::FlowDirection StackableContainer::getFlowDirection() const
{
	return mFlowDirection;
}

void StackableContainer::setInnerContainerWindow(CEGUI::Window* window)
{
	mInnerContainerWindow = window;
	mChildRemovedConnection = mInnerContainerWindow->subscribeEvent(CEGUI::Window::EventChildRemoved, CEGUI::Event::Subscriber(&StackableContainer::window_ChildRemoved, this));
	mWindowDestructionConnection = mInnerContainerWindow->subscribeEvent(CEGUI::Window::EventDestructionStarted, CEGUI::Event::Subscriber(&StackableContainer::window_DestructionStarted, this));
	
	size_t childCount = mInnerContainerWindow->getChildCount();
 	for(size_t i = 0; i < childCount; ++i) {
		CEGUI::Window* childWindow = mInnerContainerWindow->getChildAtIdx(i);
		mChildConnections.insert(ConnectorStore::value_type(childWindow, childWindow->subscribeEvent(CEGUI::Window::EventSized, CEGUI::Event::Subscriber(&StackableContainer::childwindow_Sized, this))));
	}
}


bool StackableContainer::window_ChildAdded(const CEGUI::EventArgs& e)
{
	const WindowEventArgs& windowEventArg = static_cast<const WindowEventArgs&>(e);
	if (!mInnerContainerWindow) {
		setInnerContainerWindow(windowEventArg.window->getParent());
	} else {
		///if we've called setInnerContainerWindow we would already have connected the new child window
		mChildConnections.insert(ConnectorStore::value_type(windowEventArg.window, windowEventArg.window->subscribeEvent(CEGUI::Window::EventSized, CEGUI::Event::Subscriber(&StackableContainer::childwindow_Sized, this))));
	}
	repositionWindows();
	return true;
}

bool StackableContainer::window_ChildRemoved(const CEGUI::EventArgs& e)
{
	const WindowEventArgs& windowEventArg = static_cast<const WindowEventArgs&>(e);
	ConnectorStore::iterator I = mChildConnections.find(windowEventArg.window);
	if (I != mChildConnections.end()) {
		I->second->disconnect();
		mChildConnections.erase(I);
	}
	
	repositionWindows();
	return true;
}

bool StackableContainer::childwindow_Sized(const CEGUI::EventArgs& e)
{
	repositionWindows();
	return true;
}

bool StackableContainer::window_DestructionStarted(const CEGUI::EventArgs& e)
{
	cleanup();
	return true;
}

void StackableContainer::disconnect()
{
	cleanup();
}


}

}
