/*
 * Copyright 2009 SIB Visions GmbH
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 *
 * History
 * 
 * 19.08.2009 - [JR] - creation
 * 02.11.2009 - [JR] - sizeHint, setGeometry: use QVxUtil.sizeHint
 */
package com.sibvisions.rad.ui.qt.ext.layout;

import com.sibvisions.rad.ui.qt.ext.QVxUtil;
import com.sibvisions.util.ArrayUtil;
import com.trolltech.qt.core.QRect;
import com.trolltech.qt.core.QSize;
import com.trolltech.qt.gui.QContentsMargins;
import com.trolltech.qt.gui.QLayout;
import com.trolltech.qt.gui.QLayoutItemInterface;
import com.trolltech.qt.gui.QWidget;
import com.trolltech.qt.gui.QWidgetItem;

/**
 * The QVxBorderLayout is a {@link java.awt.BorderLayout} implementation for QT.
 * 
 * @author René Jahn
 * @see java.awt.BorderLayout
 */
public class QVxBorderLayout extends QLayout
{
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Class members
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	/** the layout position for 'center'. */
	public static final String CENTER = "Center";

	/** the layout position for 'west'. */
	public static final String WEST = "West";
	
	/** the layout position for 'north'. */
	public static final String NORTH = "North";
	
	/** the layout position for 'east'. */
	public static final String EAST = "East";

	/** the layout position for 'south'. */
	public static final String SOUTH = "South";

	
	/** the list of items. */
	private ArrayUtil<ItemWrapper> items = new ArrayUtil<ItemWrapper>(5);

	/** the position of the north widget in the item list. */
	private int iNorth  = -1;

	/** the position of the west widget in the item list. */
	private int iWest   = -1;
	
	/** the position of the center widget in the item list. */
	private int iCenter = -1;
	
	/** the position of the east widget in the item list. */
	private int iEast   = -1;
	
	/** the position of the south widget in the item list. */
	private int iSouth  = -1;
	
	/** The horizontal gap. */ 
	private int hgap = 0;

	/** The vertical gap. */ 
	private int vgap = 0;

	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Initialization
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	/**
	 * Constructs a new BorderLayout.
	 */
	public QVxBorderLayout()
	{
		setContentsMargins(0, 0, 0, 0);
	}
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Abstract methods implementation
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addItem(QLayoutItemInterface pItem)
	{
		add(pItem, CENTER);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int count()
	{
		return items.size();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public QLayoutItemInterface itemAt(int pPosition)
	{
		if (pPosition < items.size())
		{
			return items.get(pPosition).item;
		}
		
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public QSize sizeHint()
	{
		ItemWrapper iwrItem;
		QLayoutItemInterface item;

		QSize size;

		int iWidth = 0;
		int iHeight = 0;

		if (iWest >= 0)
		{
			iwrItem = items.get(iWest);
			item = iwrItem.item;
			
			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				size = QVxUtil.sizeHint(widget);
	
				iWidth += size.width() + hgap;
				iHeight = Math.max(iHeight, size.height());
			}
		}

		if (iEast >= 0)
		{
			iwrItem = items.get(iEast);
			item = iwrItem.item;

			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				size = QVxUtil.sizeHint(widget);
				
				iWidth += size.width() + hgap;
				iHeight = Math.max(iHeight, size.height());
			}
		}
		
		if (iCenter >= 0)
		{
			iwrItem = items.get(iCenter);
			item = iwrItem.item;

			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				size = QVxUtil.sizeHint(widget);
				
				iWidth += size.width();
				iHeight = Math.max(iHeight, size.height());
			}
		}

		if (iNorth >= 0)
		{
			iwrItem = items.get(iNorth);
			item = iwrItem.item;
			
			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				size = QVxUtil.sizeHint(widget);

				iWidth  = Math.max(iWidth, size.width());
				iHeight += size.height() + vgap;
			}
		}

		if (iSouth >= 0)
		{
			iwrItem = items.get(iSouth);
			item = iwrItem.item;
			
			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				size = QVxUtil.sizeHint(widget);

				iWidth  = Math.max(iWidth, size.width());
				iHeight += size.height() + vgap;
			}
		}
		
		QContentsMargins margins = getContentsMargins();
		
		return new QSize(iWidth + margins.left + margins.right, iHeight + margins.top + margins.bottom);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public QLayoutItemInterface takeAt(int pPosition)
	{
		if (pPosition < items.size())
		{
			if (iNorth == pPosition)
			{
				iNorth = -1;
			}
			else if (iSouth == pPosition)
			{
				iSouth = -1;
			}
			else if (iWest == pPosition)
			{
				iWest = -1;
			}
			else if (iEast == pPosition)
			{
				iEast = -1;
			}
			else if (iCenter == pPosition)
			{
				iCenter = -1;
			}
			
			return items.remove(pPosition).item;
		}
		
		return null;
	}
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Overwritten methods
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setGeometry(QRect pGeometry)
	{
		if (items.isEmpty())
		{
			return;
		}
		
		ItemWrapper iwrItem;
		QLayoutItemInterface item;

		QContentsMargins margins = getContentsMargins();

		int iX = pGeometry.x() + margins.left;
		int iY = pGeometry.y() + margins.top;
		int iWidth = pGeometry.width() - margins.left - margins.right;
		int iHeight = pGeometry.height() - margins.top - margins.bottom;
		
		int iTopY = iY;
		int iBottomY = iY + iHeight;
		
		int iLeftX = iX;
		int iRightX = iX + iWidth;

		if (iNorth >= 0)
		{
			iwrItem = items.get(iNorth);
			item = iwrItem.item;
			
			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				QSize size = QVxUtil.sizeHint(widget);
				iTopY += size.height();

				item.widget().setGeometry(new QRect(iX, iY, iWidth, size.height()));
				
				iTopY += vgap;
			}
		}

		if (iSouth >= 0)
		{
			iwrItem = items.get(iSouth);
			item = iwrItem.item;
			
			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				QSize size = QVxUtil.sizeHint(widget);
				iBottomY -= size.height(); 
				
				item.setGeometry(new QRect(iX, iBottomY, iWidth, size.height()));
				
				iBottomY -= vgap;
			}
		}

		if (iWest >= 0)
		{
			iwrItem = items.get(iWest);
			item = iwrItem.item;

			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				QSize size = QVxUtil.sizeHint(widget);
				iLeftX += size.width(); 
				
				item.setGeometry(new QRect(iX, iTopY, iLeftX, iBottomY - iTopY));
				
				iLeftX += hgap;
			}
		}

		if (iEast >= 0)
		{
			iwrItem = items.get(iEast);
			item = iwrItem.item;

			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				QSize size = QVxUtil.sizeHint(widget);
				iRightX -= size.width();
				
				item.setGeometry(new QRect(iRightX, iTopY, size.width(), iBottomY - iTopY));
				
				iRightX -= hgap;
			}
		}
		
		if (iCenter >= 0)
		{
			iwrItem = items.get(iCenter);
			item = iwrItem.item;

			QWidget widget = item.widget(); 
			
			if (widget == null || (widget != null && !widget.isHidden()))
			{
				item.setGeometry(new QRect(iLeftX, iTopY, iRightX - iLeftX, iBottomY - iTopY));
			}
		}
	}
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// User-defined methods
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	/**
	 * Adds a widget to the layout to the specified constraint.
	 * 
	 * @param pWidget the widget to add
	 * @param pConstraints one of the following {@link #NORTH}, {@link #WEST}, {@link #CENTER}, {@link #EAST}, {@link #SOUTH}
	 */
	public void addWidget(QWidget pWidget, String pConstraints)
	{
		addChildWidget(pWidget);
		
		add(new WidgetItem(pWidget), pConstraints);
		
		invalidate();
	}
	
	/**
	 * Adds a layout item with a specified constraint.
	 * 
	 * @param pItem the item
	 * @param pConstraints one of the following {@link #NORTH}, {@link #WEST}, {@link #CENTER}, {@link #EAST}, {@link #SOUTH}
	 */
	private void add(QLayoutItemInterface pItem, String pConstraints)
	{		
		String sConstraints;
		
		if (pConstraints == null)
		{
			sConstraints = CENTER;
		}
		else
		{
			sConstraints = pConstraints;
		}
		
		int iPos;
		int iNewNorth = -1;
		int iNewSouth = -1;
		int iNewWest = -1;
		int iNewEast = -1;
		int iNewCenter = -1;
		
		if (CENTER.equals(sConstraints)) 
		{
			iPos = iCenter >= 0 ? iCenter : items.size();
			iNewCenter = iPos;
		} 
		else if (NORTH.equals(sConstraints)) 
		{
			iPos = iNorth >= 0 ? iNorth : items.size();
			iNewNorth = iPos;
		} 
		else if (SOUTH.equals(sConstraints)) 
		{
			iPos = iSouth >= 0 ? iSouth : items.size();
			iNewSouth = iPos;
		} 
		else if (EAST.equals(sConstraints)) 
		{
			iPos = iEast >= 0 ? iEast : items.size();
			iNewEast = iPos;
		} 
		else if (WEST.equals(sConstraints)) 
		{
			iPos = iWest >= 0 ? iWest : items.size();
			iNewWest = iPos;
		} 
		else 
		{
		    throw new IllegalArgumentException("cannot add to layout: unknown constraint: " + sConstraints);
		}
		
		QLayoutItemInterface item;
		
		QWidget widget = pItem.widget();
		
		if (widget == null)
		{
			item = pItem;
		}
		else
		{
			item = new WidgetItem(widget);
		}

		if (iPos == items.size())
		{
			items.add(new ItemWrapper(item, sConstraints));
		}
		else
		{
			ItemWrapper iwrItem = items.get(iPos);
			
			removeItem(iwrItem.item);
			
			if (iwrItem.item.widget() != null)
			{
				iwrItem.item.widget().close();
			}

			items.add(iPos, new ItemWrapper(item, sConstraints));
		}
		
		//update index
		iNorth  = iNewNorth  >= 0 ? iNewNorth  : iNorth;
		iWest   = iNewWest   >= 0 ? iNewWest   : iWest;
		iCenter = iNewCenter >= 0 ? iNewCenter : iCenter;
		iEast   = iNewEast   >= 0 ? iNewEast   : iEast;
		iSouth  = iNewSouth  >= 0 ? iNewSouth  : iSouth;
	}
	
    /**
     * Gets the horizontal gap.
     * 
     * @return the horizontal gap.
     */
    public int getHorizontalGap() 
    {
        return hgap;
    }
    
    /**
     * Sets the horizontal gap.
     * 
     * @param pGap the horizontal gap.
     */
    public void setHorizontalGap(int pGap) 
    {
        hgap = pGap;
    }
    
    /**
     * Gets the vertical gap.
     * 
     * @return the vertical gap.
     */
    public int getVerticalGap() 
    {
        return vgap;
    }
    
    /**
     * Sets the vertical gap.
     * 
     * @param pGap the vertical gap.
     */
    public void setVerticalGap(int pGap) 
    {
        vgap = pGap;
    }
	
    /**
     * Gets the constraints for the specified widget.
     *
     * @param pWidget the widget to be queried
     * @return  the constraint for the specified component,
     *          or null if component is null or is not present
     *          in this layout
     * @see #addWidget(QWidget, String)
     */
	public String getConstraints(QWidget pWidget)
	{
		for (int i = 0, anz = items.size(); i < anz; i++)
		{
			if (items.get(i).item.widget() == pWidget)
			{
				return items.get(i).constraint;
			}
		}
		
		return null;
	}
	
	//****************************************************************
	// Subclass definition
	//****************************************************************

	/**
	 * The <code>ItemWrapper</code> saves the position of an item in the
	 * layout.
	 * 
	 * @author René Jahn
	 */
	private final class ItemWrapper
	{
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Class members
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		/** the layout item. */
		private QLayoutItemInterface item;
		
		/** the position in the layout. */
		private String constraint;
		
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Initialization
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		/**
		 * Creates a new instance of <code>ItemWrapper</code> for a specific
		 * item at a specific position.
		 * 
		 * @param pItem the layouting item
		 * @param pConstraint the layout position
		 */
		private ItemWrapper(QLayoutItemInterface pItem, String pConstraint)
		{
			item = pItem;
			constraint = pConstraint;
		}
		
	}	// ItemWrapper
	
	/**
	 * A simple widget wrapper with the maximum size of the widget. The <code>QWidgetItem</code>
	 * doesn't handle the maximum size as expected. It uses the sizeHint.
	 * 
	 * @author René Jahn
	 */
	private static final class WidgetItem extends QWidgetItem
	{
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Initialization
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		/**
		 * Creates a new instance of <code>WidgetItem</code> for a specific {@link QWidget}.
		 * 
		 * @param pWidget the widget
		 */
		public WidgetItem(QWidget pWidget)
		{
			super(pWidget);
		}
		
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		// Overwritten methods
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		/**
		 * {@inheritDoc} 
		 */
		@Override
		public QSize maximumSize()
		{
			return widget().maximumSize();
		}
		
	}	// WidgetItem
	
}	// QVxBorderLayout
