/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *     "Apache Jetspeed" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache" or
 *    "Apache Jetspeed", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.jetspeed.portal.controllers;

//ECS stuff
import org.apache.ecs.html.*;
import org.apache.ecs.*;

//turbine RunData
import org.apache.turbine.util.RunData;

//jetspeed stuff
import org.apache.jetspeed.portal.*;

//standard Java stuff
import java.util.Vector;
import java.util.StringTokenizer;
import java.util.Enumeration;


/**
Layouts the portlets in a grid and apply the following constraints
<ul>
<li>all cells within the same column have the same width</li>
<li>all cells within the same row have the same minimum height</li>
</ul>

<p>This controller expects the following parameters in its configuration
file :

<ul>
<li><b>columns<b> optional, number of columns of the grid. If not specified,
determined by the layout information in each portlet</li>
<li><b>columnWidths</b> optional, the size of the columns, separated by a colon</li>
<li><b>rows<b> optional, number of rows of the grid. If not specified,
determined by the layout information in each portlet</li>
<li><b>rowHeights</b> optional, the minimum size of the rows, separated by a colon</li>
</ul>

</p>
<p>The controller expects each portlet to have a row number and a column number
layout information. If this information is not found, put the Portlet in the
first cell of the table</p>

@author <a href="mailto:raphael@apache.org">Raphaël Luta</a>
@version $Id: GridPortletController.java,v 1.14 2003/03/04 00:05:03 sgala Exp $
*/
public class GridPortletController extends AbstractPortletController {

    private int columns = 0;
    private int rows = 0;
    private Vector rowHeights = null;
    private Vector colWidths = null;
    
    /**
    */
    public GridPortletController() {
        rowHeights = new Vector();
        colWidths = new Vector();
    }

    /**
    */
    private void calculateControllerLayout( Portlet portlet ) {

        if ( portlet instanceof PortletSet ) {

            Enumeration more = ((PortletSet)portlet).getPortlets();

            while ( more.hasMoreElements() ) {
                calculateControllerLayout( (Portlet) more.nextElement() );
            }

            return;
        }                

           
        PortletConfig portletConf = portlet.getPortletConfig();
        Integer colObj = portletConf.getConstraints().getColumn();
        Integer rowObj = portletConf.getConstraints().getRow();

        int col = (colObj!=null)?colObj.intValue():0;
        int row = (rowObj!=null)?rowObj.intValue():0;
        
        if ( col + 1 > this.getColumn() ) {
            this.setColumn( col + 1 );
        }
        
        if ( row + 1 > this.getRow() ) {
            this.setRow( row + 1 );
        }

        
    }
    
    /**
    Get the content for this PortletController
    */
    public ConcreteElement getContent( RunData rundata ) {

        ElementContainer base = new ElementContainer();

        try 
        {
        PortletSet portlets = getPortlets();
        PortletConfig pc = portlets.getPortletConfig();

        // first get the number of columns and rows to display
        Enumeration en = portlets.getPortlets();
      
        //see if any or the Portlets you want to add have a larger column or
        //row number than that defined in PSML
        while ( en.hasMoreElements() ) {

            Portlet portlet = (Portlet)en.nextElement();

            calculateControllerLayout( portlet );
            
        }

        setWidth( pc.getLayout( "width", getWidth() ) );

        int rows = getRow();
        int cols = getColumn();

        if (0 == rows || 0 == cols)
            return base; // empty container

        Table t = new Table()
                       .setWidth( this.getWidth() )
                       .setCellPadding( this.getPadding() )
                       .setAlign("center");

        base.addElement( t );

        ElementContainer[][] elements = new ElementContainer[rows][cols];

        for( int i = 0; i < rows; i++ )  {
            for ( int j = 0 ; j < cols; j++ ) {
                elements[i][j]=new ElementContainer();
            }
        }

        // populate the elements array
        en = portlets.getPortlets();
        while (en.hasMoreElements() ) {

            Portlet p = (Portlet)en.nextElement();
            PortletConfig pConf = p.getPortletConfig();

            Integer colObj = pConf.getConstraints().getColumn();
            Integer rowObj = pConf.getConstraints().getRow();
            int colnum = (colObj!=null)?colObj.intValue():0;
            int rownum = (rowObj!=null)?rowObj.intValue():0;

            elements[rownum % rows][colnum % cols]
                .addElement( p.getContent( rundata ) );

        }

        // build the table

        for (int i = 0; i < rows; ++i) {

            TR row = new TR();
            TD td = null;

            for(int j=0; j < cols ; ++j) {
                row.addElement( td= new TD().setVAlign("top")
                                       .addElement( elements[i][j] ) );
                if (getRowHeight(i)!=null) td.setHeight(getRowHeight(i));
                if (getColumnWidth(j)!=null) td.setWidth(getColumnWidth(j));
            }

            t.addElement(row);
        }

        }
        catch (Exception e)
        {
            org.apache.turbine.util.Log.error(e);
        }
        
        return base;

    }

    /**
    */
    public void init() {
        super.init();
        PortletControllerConfig conf = getConfig();
        
        if (conf!=null) {
            setColumn(Integer.parseInt(conf.getInitParameter("column","0")));
            setRow(Integer.parseInt(conf.getInitParameter("row","0")));
            setColumnsWidth(parseList(conf.getInitParameter("columnWidths")));
            setRowsHeight(parseList(conf.getInitParameter("rowHeights")));
        }
            
    }
    
    /**
    Set the number of columns used in this controller
    */
    public void setColumn(int col) {
        this.columns=col;
    }
    
    /**
    Get the number of columns used in this controller
    */
    public int getColumn() {
        return this.columns;
    }
    
    /**
    Set the number of rows used by this controller
    */
    public void setRow(int row) {
        this.rows=row;
    }
    
    /**
    Get the number of rows used by this controll
    */
    public int getRow() {
        return this.rows;
    }
    
    /**
    */
    public void setColumnsWidth(Vector widths) {
        this.colWidths = widths;
    }
    
    /**
    */
    public Enumeration getColumnsWidth() {
        return colWidths.elements();
    }
    
    /**
    */
    public String getColumnWidth(int pos) {
        if (pos < colWidths.size()) return (String)colWidths.elementAt(pos);
        return null;
    }
    
    /**
    */
    public void setRowsHeight(Vector heights) {
        this.rowHeights = heights;
    }
    
    /**
    */
    public Enumeration getRowsHeight() {
        return rowHeights.elements();
    }
    
    /**
    */
    public String getRowHeight(int pos) {
        if (pos < rowHeights.size()) return (String)rowHeights.elementAt(pos);
        return null;
    }
    
    /**
    */
    private Vector parseList(String list) {
        Vector v = new Vector();
        if (list!=null) {
            StringTokenizer st = new StringTokenizer(list,",");
            while (st.hasMoreTokens())
                v.addElement(st.nextToken());
        }

        return v;
    }

}

