/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          clayoutmanager.cpp  -  description
                             -------------------
    begin                : Sun May 18 2003
    copyright            : (C) 2000-2005 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   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; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <sptk3/CGroup.h>
#include <sptk3/CControl.h>

#include <stdio.h>

#include <sptk3/CScroll.h>
#include <sptk3/CLayoutManager.h>

CLayoutManager::CLayoutManager() { 
	m_backgroundImage = 0L;
	m_layoutSpacing = 3; 
	m_layoutGrowMode = LGM_AUTO_GROW; 
	m_group = 0L;
}

void CLayoutManager::autoLayout(int x,int y,int& w,int& h,bool resizeWidgets) const {
	if (!m_group) return;

	bool completed = false;
	int frame_dw = Fl::box_dw(m_group->box()) + m_layoutSpacing * 2;
	int frame_dh = Fl::box_dh(m_group->box()) + m_layoutSpacing * 2;
	int frame_dx = Fl::box_dx(m_group->box()) + m_layoutSpacing;
	int frame_dy = Fl::box_dy(m_group->box()) + m_layoutSpacing;

	while (!completed) {
		int xx = x + frame_dx;
		int yy = y + frame_dy;
		int ww = w - frame_dw;
		int hh = h - frame_dh;
		int preferred_w, preferred_h;
		Fl_Widget *clientWidget = 0L;
		unsigned cnt = m_group->children();
		bool extended = false;
		CLayoutClient *ca;
		for (unsigned i = 0; i < cnt; i++) {
			ca = 0L;
			Fl_Widget *widget = m_group->child(i);
			try {
				ca = dynamic_cast<CLayoutClient *>(widget);
			}
			catch (...) {
				continue;
			}
			if (!ca || !widget || !widget->visible()) continue;
			if (ca->layoutAlign() == SP_ALIGN_NONE) continue;

			preferred_w = ww;
			preferred_h = hh;
			if (preferred_w < 0) preferred_w = 20;
			if (preferred_h < 0) preferred_h = 20;

			int preferred_x = xx, preferred_y = yy;

			switch (ca->layoutAlign()) {
				case SP_ALIGN_NONE:
					break;

				case SP_ALIGN_RIGHT:
					preferred_w = ca->layoutSize();
					ca->preferredSize(preferred_w,preferred_h);
					preferred_x = xx + ww - preferred_w;
					preferred_y += (hh - preferred_h) / 2;
					break;

				case SP_ALIGN_LEFT:
					preferred_w = ca->layoutSize();
					ca->preferredSize(preferred_w,preferred_h);
					preferred_y += (hh - preferred_h) / 2;
					break;

				case SP_ALIGN_BOTTOM:
					preferred_h = ca->layoutSize();
					ca->preferredSize(preferred_w,preferred_h);
					preferred_y = yy + hh - preferred_h;
					break;

				case SP_ALIGN_TOP:
					preferred_h = ca->layoutSize();
					ca->preferredSize(preferred_w,preferred_h);
					break;

				case SP_ALIGN_CLIENT:
					clientWidget = m_group->child(i);
					break;
			}

         //if (resizeWidgets && ca->layoutAlign() != SP_ALIGN_CLIENT) 
         //   widget->resize(preferred_x,preferred_y,preferred_w,preferred_h);

			switch (ca->layoutAlign()) {
				case SP_ALIGN_NONE:
					break;

				case SP_ALIGN_LEFT:
					xx += preferred_w + m_layoutSpacing;
				case SP_ALIGN_RIGHT:
					ww -= preferred_w + m_layoutSpacing;
					if (preferred_h > hh) {
						if (!(m_layoutGrowMode & LGM_VERTICAL_GROW))
							preferred_h = hh;
						else {
							extended = true;
							if (hh < 0) {
								h += preferred_h;
								hh += preferred_h;
							} else {
								h += preferred_h - hh;
								hh = preferred_h;
							}
						}
					}
					break;

				case SP_ALIGN_TOP:
					yy += preferred_h + m_layoutSpacing;
				case SP_ALIGN_BOTTOM:
					hh -= preferred_h + m_layoutSpacing;
					if (preferred_w > ww) {
						if (!(m_layoutGrowMode & LGM_HORIZONTAL_GROW))
							preferred_w = ww;
						else {
							extended = true;
							if (ww < 0) {
								w += preferred_w;
								ww += preferred_w;
							} else {
								w += preferred_w - ww;
								ww = preferred_w;
							}
						}
					}
					break;

				case SP_ALIGN_CLIENT:
					clientWidget = m_group->child(i);
					break;
			}

			if (resizeWidgets && ca->layoutAlign() != SP_ALIGN_CLIENT) 
				widget->resize(preferred_x,preferred_y,preferred_w,preferred_h);
		}

		if (clientWidget) { // One widget has the client alignment
			CLayoutClient *ca = dynamic_cast<CLayoutClient *>(clientWidget);

			preferred_w = ww; // - m_layoutSpacing;
			preferred_h = hh; // - m_layoutSpacing;
			if (preferred_w < 0) preferred_w = 20;
			if (preferred_h < 0) preferred_h = 20;

			ca->preferredSize(preferred_w,preferred_h);

			if (resizeWidgets && !dynamic_cast<CScroll *>(m_group)) {
				if (preferred_w > ww && !(m_layoutGrowMode & LGM_HORIZONTAL_GROW))
					preferred_w = ww;
				if (preferred_h > hh && !(m_layoutGrowMode & LGM_VERTICAL_GROW))
					preferred_h = hh;
			}

			ww -= preferred_w; // + m_layoutSpacing;
			hh -= preferred_h; // + m_layoutSpacing;

			if (resizeWidgets) 
				clientWidget->resize(xx,yy,preferred_w,preferred_h);
		}

        // Final adjustment
		if (m_layoutGrowMode & LGM_HORIZONTAL_GROW) {
			if (ww < 0) w -= ww;
		} else ww = 0;

		if (m_layoutGrowMode & LGM_VERTICAL_GROW) {
			if (hh < 0) h -= hh;
		} else hh = 0;
		completed = ww >= 0 && hh >= 0 && !extended;
	}
}

void CLayoutManager::paintBackground() {
	if (!m_backgroundImage) return;
	int xx = m_group->x() + Fl::box_dx(m_group->box());
	int yy = m_group->y() + Fl::box_dy(m_group->box());
	int ww = m_group->w() - Fl::box_dw(m_group->box());
	int hh = m_group->h() - Fl::box_dh(m_group->box());
	int dx = m_backgroundImage->w();
	int dy = m_backgroundImage->h();
	fl_push_clip(xx,yy,ww,hh);
	for (int xi = xx; xi < xx + ww; xi += dx) {
		for (int yi = yy; yi < yy + hh; yi += dy) {
			m_backgroundImage->draw(xi,yi,dx,dy,0,0);
		}
	}
	fl_pop_clip();
}
