/* This file is part of the KDE project
   Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "kexiflowlayout.h"

#include <kdebug.h>

/// Iterator class

class KexiFlowLayoutIterator : public TQGLayoutIterator
{
	public:
		KexiFlowLayoutIterator( TQPtrList<TQLayoutItem> *list )
		  : m_idx(0), m_list( list )
		{}
		uint count() const;
		TQLayoutItem *current();
		TQLayoutItem *next();
		TQLayoutItem *takeCurrent();

	private:
		int m_idx;
		TQPtrList<TQLayoutItem> *m_list;
};

uint
KexiFlowLayoutIterator::count() const
{
	return m_list->count();
}

TQLayoutItem *
KexiFlowLayoutIterator::current()
{
	return (m_idx < (int)count()) ? m_list->at(m_idx) : 0;
}

TQLayoutItem *
KexiFlowLayoutIterator::next()
{
	m_idx++;
	return current();
}

TQLayoutItem *
KexiFlowLayoutIterator::takeCurrent()
{
	return (m_idx < (int)count()) ? m_list->take(m_idx) : 0;
}

//// The layout itself

KexiFlowLayout::KexiFlowLayout(TQWidget *parent, int border, int space, const char *name)
 : TQLayout(parent, border, space, name)
{
	m_orientation =TQt::Horizontal;
	m_justify = false;
	m_cached_width = 0;
}

KexiFlowLayout::KexiFlowLayout(TQLayout* parent, int space, const char *name)
 : TQLayout( parent, space, name )
{
	m_orientation =TQt::Horizontal;
	m_justify = false;
	m_cached_width = 0;
}

KexiFlowLayout::KexiFlowLayout(int space, const char *name)
 : TQLayout(space, name)
 {
	m_orientation =TQt::Horizontal;
	m_justify = false;
	m_cached_width = 0;
 }

KexiFlowLayout::~KexiFlowLayout()
{
	deleteAllItems();
}

void
KexiFlowLayout::addItem(TQLayoutItem *item)
{
	m_list.append(item);
}

void
KexiFlowLayout::addSpacing(int size)
{
	if (m_orientation ==TQt::Horizontal)
		addItem( new TQSpacerItem( size, 0, TQSizePolicy::Fixed, TQSizePolicy::Minimum ) );
	else
		addItem( new TQSpacerItem( 0, size, TQSizePolicy::Minimum, TQSizePolicy::Fixed ) );
}

TQLayoutIterator
KexiFlowLayout::iterator()
{
	return TQLayoutIterator( new KexiFlowLayoutIterator(&m_list) );
}

TQPtrList<TQWidget>*
KexiFlowLayout::widgetList() const
{
	TQPtrList<TQWidget> *list = new TQPtrList<TQWidget>();
	for (TQPtrListIterator<TQLayoutItem> it(m_list); it.current(); ++it) {
		if(it.current()->widget())
			list->append(it.current()->widget());
	}
	return list;
}

void
KexiFlowLayout::invalidate()
{
	TQLayout::invalidate();
	m_cached_sizeHint = TQSize();
	m_cached_minSize = TQSize();
	m_cached_width = 0;
}

bool
KexiFlowLayout::isEmpty()
{
	return m_list.isEmpty();
}

bool
KexiFlowLayout::hasHeightForWidth() const
{
	return (m_orientation ==TQt::Horizontal);
}

int
KexiFlowLayout::heightForWidth(int w) const
{
	if(m_cached_width != w) {
		// workaround to allow this method to stay 'const'
		KexiFlowLayout *mthis = (KexiFlowLayout*)this;
		int h = mthis->simulateLayout( TQRect(0,0,w,0) );
		mthis->m_cached_hfw = h;
		mthis->m_cached_width = w;
		return h;
 	}
	return m_cached_hfw;
}

TQSize
KexiFlowLayout::sizeHint() const
{
	if(m_cached_sizeHint.isEmpty()) {
		KexiFlowLayout *mthis = (KexiFlowLayout*)this;
		TQRect r = TQRect(0, 0, 2000, 2000);
		mthis->simulateLayout(r);
	}
	return m_cached_sizeHint;
}

TQSize
KexiFlowLayout::minimumSize() const
{
//js: do we really need to simulate layout here?
//    I commented this out because it was impossible to stretch layout conveniently.
//    Now, minimum size is computed automatically based on item's minimumSize...
#if 0
	if(m_cached_minSize.isEmpty()) {
		KexiFlowLayout *mthis = (KexiFlowLayout*)this;
		TQRect r = TQRect(0, 0, 2000, 2000);
		mthis->simulateLayout(r);
	}
#endif
	return m_cached_minSize;
}

TQSizePolicy::ExpandData
KexiFlowLayout::expanding() const
{
	if(m_orientation == TQt::Vertical)
		return TQSizePolicy::Vertically;
	else
		return TQSizePolicy::Horizontally;
}

void
KexiFlowLayout::setGeometry(const TQRect &r)
{
	TQLayout::setGeometry(r);
	if(m_orientation ==TQt::Horizontal)
		doHorizontalLayout(r);
	else
		doVerticalLayout(r);
}

int
KexiFlowLayout::simulateLayout(const TQRect &r)
{
	if(m_orientation ==TQt::Horizontal)
		return doHorizontalLayout(r, true);
	else
		return doVerticalLayout(r, true);
}

int
KexiFlowLayout::doHorizontalLayout(const TQRect &r, bool testOnly)
{
	int x = r.x();
	int y = r.y();
	int h = 0; // height of this line
	int availableSpace = r.width() + spacing();
	int expandingWidgets=0; // number of widgets in the line with TQSizePolicy == Expanding
	TQPtrListIterator<TQLayoutItem> it(m_list);
	TQPtrList<TQLayoutItem> currentLine;
	TQLayoutItem *o;
	TQSize minSize, sizeHint(20, 20);
	int minSizeHeight = 0 - spacing();

	while ( (o = it.current()) != 0 ) {
		if(o->isEmpty()) { /// do not consider hidden widgets
			++it;
			continue;
		}

//		kdDebug() << "- doHorizontalLayout(): " << o->widget()->className() << " " << o->widget()->name() << endl;
		TQSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts)
		if ((x + oSizeHint.width()) > r.right() && h > 0) {
			// do the layout of current line
			TQPtrListIterator<TQLayoutItem> it2(currentLine);
			TQLayoutItem *item;
			int wx = r.x();
			int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0;
			while( (item = it2.current()) != 0 ) {
				TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
				TQSize itemMinSize = item->minimumSize(); // a while to get them
				TQSize s;
				if(m_justify) {
					if(expandingWidgets != 0) {
						if(item->expanding() == TQSizePolicy::Horizontally || item->expanding() == TQSizePolicy::BothDirections)
							s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / expandingWidgets
								, r.width()), itemSizeHint.height() );
						else
							s = TQSize( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
					}
					else
						s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count()
							, r.width()), itemSizeHint.height() );
				}
				else
					s = TQSize ( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
				if(!testOnly)
					item->setGeometry( TQRect(TQPoint(wx, y), s) );
				wx = wx + s.width() + spacing();
				minSizeWidth = minSizeWidth + spacing() + itemMinSize.width();
				sizeHintWidth = sizeHintWidth + spacing() +  itemSizeHint.width();
				lineMinHeight = TQMAX( lineMinHeight, itemMinSize.height() );
				++it2;
			}
			sizeHint = sizeHint.expandedTo( TQSize(sizeHintWidth, 0) );
			minSize = minSize.expandedTo( TQSize(minSizeWidth, 0) );
			minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
			// start a new line
			y = y + spacing() + h;
			h = 0;
			x = r.x();
			currentLine.clear();
			expandingWidgets = 0;
			availableSpace = r.width() + spacing();
		}

		x = x + spacing() + oSizeHint.width();
		h = TQMAX( h,  oSizeHint.height() );
		currentLine.append(o);
		if(o->expanding() == TQSizePolicy::Horizontally || o->expanding() == TQSizePolicy::BothDirections)
			++expandingWidgets;
		availableSpace = TQMAX(0, availableSpace - spacing() - oSizeHint.width());
		++it;
	}

	// don't forget to layout the last line
	TQPtrListIterator<TQLayoutItem> it2(currentLine);
	TQLayoutItem *item;
	int wx = r.x();
	int sizeHintWidth = 0 -spacing(), minSizeWidth=0 - spacing(), lineMinHeight=0;
	while( (item = it2.current()) != 0 ) {
		TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
		TQSize itemMinSize = item->minimumSize(); // a while to get them
		TQSize s;
		if(m_justify) {
			if(expandingWidgets != 0) {
				if(item->expanding() == TQSizePolicy::Horizontally || item->expanding() == TQSizePolicy::BothDirections)
					s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / expandingWidgets
						, r.width()), itemSizeHint.height() );
				else
					s = TQSize( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
			}
			else
				s = TQSize( TQMIN(itemSizeHint.width() + availableSpace / (int)currentLine.count()
					, r.width()), itemSizeHint.height() );
		}
		else
			s = TQSize ( TQMIN(itemSizeHint.width(), r.width()), itemSizeHint.height() );
		if(!testOnly)
			item->setGeometry( TQRect(TQPoint(wx, y), s) );
		wx = wx + s.width() + spacing();
		minSizeWidth = minSizeWidth + spacing() + itemMinSize.width();
		sizeHintWidth = sizeHintWidth + spacing() +  itemSizeHint.width();
		lineMinHeight = TQMAX( lineMinHeight, itemMinSize.height() );
		++it2;
	}
	sizeHint = sizeHint.expandedTo( TQSize(sizeHintWidth, y + spacing() + h) );
	minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
	minSize = minSize.expandedTo( TQSize(minSizeWidth, minSizeHeight) );

	// store sizeHint() and minimumSize()
	m_cached_sizeHint = sizeHint + TQSize(2* margin(), 2*margin());
	m_cached_minSize = minSize + TQSize(2* margin() , 2*margin());
	// return our height
	return y + h - r.y();
}

int
KexiFlowLayout::doVerticalLayout(const TQRect &r, bool testOnly)
{
	int x = r.x();
	int y = r.y();
	int w = 0; // width of this line
	int availableSpace = r.height() + spacing();
	int expandingWidgets=0; // number of widgets in the line with TQSizePolicy == Expanding
	TQPtrListIterator<TQLayoutItem> it(m_list);
	TQPtrList<TQLayoutItem> currentLine;
	TQLayoutItem *o;
	TQSize minSize, sizeHint(20, 20);
	int minSizeWidth = 0 - spacing();

	while ( (o = it.current()) != 0 ) {
		if(o->isEmpty()) { /// do not consider hidden widgets
			++it;
			continue;
		}

		TQSize oSizeHint = o->sizeHint(); // we cache these ones because it can take a while to get it (eg for child layouts)
		if (y + oSizeHint.height() > r.bottom() && w > 0) {
			// do the layout of current line
			TQPtrListIterator<TQLayoutItem> it2(currentLine);
			TQLayoutItem *item;
			int wy = r.y();
			int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0;
			while( (item = it2.current()) != 0 ) {
				TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
				TQSize itemMinSize = item->minimumSize(); // a while to get them
				TQSize s;
				if(m_justify) {
					if(expandingWidgets != 0) {
						if(item->expanding() == TQSizePolicy::Vertically || item->expanding() == TQSizePolicy::BothDirections)
							s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / expandingWidgets
								, r.height()) );
						else
							s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) );
					}
					else
						s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count()
							, r.height()) );
				}
				else
					s = TQSize (  itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) );
				if(!testOnly)
					item->setGeometry( TQRect(TQPoint(x, wy), s) );
				wy = wy + s.height() + spacing();
				minSizeHeight = minSizeHeight + spacing() + itemMinSize.height();
				sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height();
				colMinWidth = TQMAX( colMinWidth, itemMinSize.width() );
				++it2;
			}
			sizeHint = sizeHint.expandedTo( TQSize(0, sizeHintHeight) );
			minSize = minSize.expandedTo( TQSize(0, minSizeHeight) );
			minSizeWidth = minSizeWidth + spacing() + colMinWidth;
			// start a new column
			x = x + spacing() + w;
			w = 0;
			y = r.y();
			currentLine.clear();
			expandingWidgets = 0;
			availableSpace = r.height() + spacing();
		}

		y = y + spacing() + oSizeHint.height();
		w = TQMAX( w,  oSizeHint.width() );
		currentLine.append(o);
		if(o->expanding() == TQSizePolicy::Vertically || o->expanding() == TQSizePolicy::BothDirections)
			++expandingWidgets;
		availableSpace = TQMAX(0, availableSpace - spacing() - oSizeHint.height());
		++it;
	}

	// don't forget to layout the last line
	TQPtrListIterator<TQLayoutItem> it2(currentLine);
	TQLayoutItem *item;
	int wy = r.y();
	int sizeHintHeight = 0 - spacing(), minSizeHeight = 0 - spacing(), colMinWidth=0;
	while( (item = it2.current()) != 0 ) {
		TQSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
		TQSize itemMinSize = item->minimumSize(); // a while to get them
		TQSize s;
		if(m_justify) {
			if(expandingWidgets != 0) {
				if(item->expanding() == TQSizePolicy::Vertically || item->expanding() == TQSizePolicy::BothDirections)
					s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / expandingWidgets
						, r.height()) );
				else
					s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) );
			}
			else
				s = TQSize( itemSizeHint.width(), TQMIN(itemSizeHint.height() + availableSpace / (int)currentLine.count()
					, r.height()) );
		}
		else
			s = TQSize (  itemSizeHint.width(), TQMIN(itemSizeHint.height(), r.height()) );
		if(!testOnly)
			item->setGeometry( TQRect(TQPoint(x, wy), s) );
		wy = wy + s.height() + spacing();
		minSizeHeight = minSizeHeight + spacing() + itemMinSize.height();
		sizeHintHeight = sizeHintHeight + spacing() + itemSizeHint.height();
		colMinWidth = TQMAX( colMinWidth, itemMinSize.width() );
		++it2;
	}
	sizeHint = sizeHint.expandedTo( TQSize( x + spacing() + w, sizeHintHeight) );
	minSizeWidth = minSizeWidth + spacing() + colMinWidth;
	minSize = minSize.expandedTo( TQSize(minSizeWidth, minSizeHeight) );

	// store sizeHint() and minimumSize()
	m_cached_sizeHint = sizeHint + TQSize(2* margin(), 2*margin());
	m_cached_minSize = minSize + TQSize(2* margin(), 2*margin());
	// return our width
	return x + w - r.x();
}
