/*  $Id: layout_generic.cpp,v 1.6 2004/02/01 20:53:15 mbn Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 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
**  Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
**
*/

#include "precomp.h"
#include "layout_generic.h"

/////////////////////////////////////////////////////////////////////////////
// CL_Layout_Generic construction:

CL_Layout_Generic::CL_Layout_Generic(CL_LayoutType type) : type(type), grid_columns(-1), margin(0), spacing(4)
{
}

CL_Layout_Generic::~CL_Layout_Generic()
{
	for (std::vector<CL_LayoutItem *>::size_type i = 0; i < elements.size(); i++)
	{
		delete elements[i];
	}
}

/////////////////////////////////////////////////////////////////////////////
// CL_Layout_Generic attributes:

/////////////////////////////////////////////////////////////////////////////
// CL_Layout_Generic operations:

#define cl_max(a,b) ((a > b) ? a : b)

CL_Size CL_Layout_Generic::get_preferred_size() const
{
	CL_Size size(0,0);
	std::vector<CL_LayoutItem *>::size_type i;
	for (i=0; i<elements.size(); i++)
	{
		CL_Size cur = elements[i]->get_preferred_size();

		if (type == layout_vertical)
		{
			size.width = cl_max(size.width, cur.width);
			size.height += cur.height;
			if (i > 0) size.height += spacing;
		}
		else if (type == layout_horizontal)
		{
			size.width += cur.width;
			size.height = cl_max(size.height, cur.height);
			if (i > 0) size.width += spacing;
		}
	}
	size.width += margin*2;
	size.height += margin*2;
	return size;
}

CL_Size CL_Layout_Generic::get_minimum_size() const
{
	CL_Size size(0,0);
	std::vector<CL_LayoutItem *>::size_type i;
	for (i=0; i<elements.size(); i++)
	{
		CL_Size cur = elements[i]->get_minimum_size();

		if (type == layout_vertical)
		{
			size.width = cl_max(size.width, cur.width);
			size.height += cur.height;
			if (i > 0) size.height += spacing;
		}
		else if (type == layout_horizontal)
		{
			size.width += cur.width;
			size.height = cl_max(size.height, cur.height);
			if (i > 0) size.width += spacing;
		}
	}
	size.width += margin*2;
	size.height += margin*2;
	return size;
}

CL_Size CL_Layout_Generic::get_maximum_size() const
{
	CL_Size size(0,0);
	std::vector<CL_LayoutItem *>::size_type i;
	for (i=0; i<elements.size(); i++)
	{
		CL_Size cur = elements[i]->get_maximum_size();

		if (type == layout_vertical)
		{
			size.width = cl_max(size.width, cur.width);
			size.height += cur.height;
			if (i > 0) size.height += spacing;
		}
		else if (type == layout_horizontal)
		{
			size.width += cur.width;
			size.height = cl_max(size.height, cur.height);
			if (i > 0) size.width += spacing;
		}
	}
	size.width += margin*2;
	size.height += margin*2;
	return size;
}

CL_LayoutPolicy CL_Layout_Generic::get_vertical_policy() const
{
	std::vector<CL_LayoutItem *>::size_type i;
	for (i=0; i<elements.size(); i++)
	{
		if (elements[i]->get_vertical_policy() == layout_expanding) return layout_expanding;
	}
	return layout_minimum;
}

CL_LayoutPolicy CL_Layout_Generic::get_horizontal_policy() const
{
	std::vector<CL_LayoutItem *>::size_type i;
	for (i=0; i<elements.size(); i++)
	{
		if (elements[i]->get_horizontal_policy() == layout_expanding) return layout_expanding;
	}
	return layout_minimum;
}

void CL_Layout_Generic::set_position(const CL_Rect &pos)
{
	std::vector<CL_LayoutItem *>::size_type i;

	int minimum_width = 0;
	int minimum_height = 0;
	int num_expanding_x = 0;
	int num_expanding_y = 0;

	for (i=0; i<elements.size(); i++)
	{
		if (elements[i]->get_horizontal_policy() == layout_minimum)
		{
			minimum_width += elements[i]->get_preferred_size().width;
		}
		else
		{
			num_expanding_x++;
		}

		if (elements[i]->get_vertical_policy() == layout_minimum)
		{
			minimum_height += elements[i]->get_preferred_size().height;
		}
		else
		{
			num_expanding_y++;
		}

		if (type == layout_horizontal && i > 0) minimum_width += spacing;
		if (type == layout_vertical && i > 0) minimum_height += spacing;
	}

	int x = pos.left + margin;
	int y = pos.top + margin;
	int expandable_width = pos.get_width() - margin*2 - minimum_width;
	int expandable_height = pos.get_height() - margin*2 - minimum_height;

	// Do vertical and horizontal layout:
	switch (type)
	{
	case layout_vertical:
		for (i=0; i<elements.size(); i++)
		{
			if (elements[i]->get_vertical_policy() == layout_minimum && num_expanding_y > 0)
			{
				int element_height = elements[i]->get_preferred_size().height;
				elements[i]->set_position(
					CL_Rect(
						pos.left+margin,
						y,
						pos.right-margin,
						y+element_height));
				y += element_height;
			}
			else if (num_expanding_y > 0)
			{
				int element_height =
					expandable_height*(i+1)/num_expanding_y - expandable_height*i/num_expanding_y;

				elements[i]->set_position(
					CL_Rect(
						pos.left+margin,
						y,
						pos.right-margin,
						y+element_height));
				y += element_height;
			}
			else
			{
				elements[i]->set_position(
					CL_Rect(
						pos.left+margin,
						pos.top+margin+(pos.get_height()-margin*2)*i/elements.size(),
						pos.right-margin,
						pos.top+margin+(pos.get_height()-margin*2)*(i+1)/elements.size()));
			}

			y += spacing;
		}
		break;

	case layout_horizontal:
		for (i=0; i<elements.size(); i++)
		{
			if (elements[i]->get_horizontal_policy() == layout_minimum && num_expanding_x > 0)
			{
				int element_width = elements[i]->get_preferred_size().width;
				elements[i]->set_position(
					CL_Rect(
						x,
						pos.top+margin,
						x+element_width,
						pos.bottom-margin));
				x += element_width;
			}
			else if (num_expanding_x > 0)
			{
				int element_width =
					expandable_width*(i+1)/num_expanding_x - expandable_width*i/num_expanding_x;

				elements[i]->set_position(
					CL_Rect(
						x,
						pos.top+margin,
						x+element_width,
						pos.bottom-margin));
				x += element_width;
			}
			else
			{
				elements[i]->set_position(
					CL_Rect(
						pos.left+margin+(pos.get_width()-margin*2)*i/elements.size(),
						pos.top+margin,
						pos.left+margin+(pos.get_width()-margin*2)*(i+1)/elements.size(),
						pos.bottom-margin));
			}

			x += spacing;
		}
		break;
	default:
		break;
	}
}

/////////////////////////////////////////////////////////////////////////////
// CL_Layout_Generic implementation:
