/* ====================================================================
 * 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.portlets.browser;

import org.apache.turbine.util.Log;
import org.apache.jetspeed.portal.portlets.browser.BrowserIterator;

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.sql.Types;

/**
 * A class for iterating over the window. The window constitutes the selection
 * of rows being displayed to the user from the List storing all the ResultSet.
 *
 * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
 * @version $Id: DatabaseBrowserIterator.java,v 1.7 2002/08/05 22:22:04 taylor Exp $
 *
*/
public class DatabaseBrowserIterator implements BrowserIterator
{

    private static final String VELOCITY_NULL_ENTRY = "-";

    int top = 0;
    int index = 0;

    int bottom = -1;
    int windowSize = -1;
    int rsListSize = -1;

    boolean ascendingOrder = true;

    String sortColumnName = null;

    ArrayList rsList;
    ArrayList rsTitleList;
    ArrayList rsTypeList;

    /**
     * Constructor for the database browser iterator
     *
     * @param result The List containg all the rows from the resultSet.
     * @param columnTitles The List containg all the columnLabels from a resultSet.
     * @param pageSize The number of rows to be displayed in a window configured by the user.
     */
    public DatabaseBrowserIterator(List result, List columnTitles,
                                   List columnTypes, int pageSize)
    {
      this.rsList = (ArrayList)result;
      this.rsTitleList= (ArrayList)columnTitles;
      this.rsTypeList = (ArrayList)columnTypes;
      this.windowSize = pageSize;
      this.rsListSize = result.size();
      setBottom();
    }

    /**
     * This method returns the result set.
     *
     */
    public List getResultSet()
    {
        return rsList;
    }

    /**
     * This method returns the number of rows in the result set.
     *
     */
    public int getResultSetSize()
    {
        return rsListSize;
    }

    /**
     * This method returns the List containg the column labels of the result set.
     *
     */
    public List getResultSetTitleList()
    {
        return rsTitleList;
    }
    /**
     * This method returns the List containg the column type names of the result set.
     *
     *@see java.sql.Types
     */
    public List getResultSetTypesList()
    {
        return rsTypeList;
    }

    /**
     * This method returns the index of the row to which the cursor is pointing at.
     *
     */
    public int getTop()
    {
        return top;
    }

    /**
     * This method points the cursor to the index provided.
     *
     * @param start Index to which cursor should point to
     */
    public void setTop(int start)
    {
        top = start;
        index = top;
        setBottom();
    }

    /**
     * This method returns the last index of the row in the window displayed.
     *
     */
    public int getBottom()
    {
        return bottom;
    }

    /**
     * This method returns the window size.
     *
     */
    public int getWindowSize()
    {
        return windowSize;
    }

    /**
     * This method sets the bottom based on which index the cursor points to and
     * the size of the result set.
     *
     */
    private void setBottom()
    {
        bottom = top + windowSize;
        if( bottom > rsListSize )
        {
            bottom = rsListSize;
        }
    }

    /**
     * Returns true if the iteration has more elements
     */
    public boolean hasNext()
    {
        if(index <= rsListSize && index < bottom)
        {
            return true;
        }
        return false;
    }

    /**
     * Returns the next element in the iteration
     */
    public Object next()
    {
        index = index + 1;
        return rsList.get(index - 1);
    }

    /**
     * Logs as info - since remove operation is not supported
     * by this Iterator.
     */
    public void remove()
    {
        Log.info("The remove operation is not supported.");
    }

    /**
     * This method sorts the result set according to the value of the column as
     * specified by the parameter column name.
     * Changes the order of the result set vector.
     *
     *@param String sortColumnName
     */
    public void sort(String columnName)
    {
        //System.out.println("current columnName="+columnName);
        //System.out.println("old columnName="+sortColumnName);
        if( columnName != null )
        {
            if( sortColumnName != null && sortColumnName.equals(columnName) )
            {
                ascendingOrder = !ascendingOrder;
            }
            else
            {
                ascendingOrder= true;
                sortColumnName = columnName;
            }
            Collections.sort( rsList, this);
        }
    }

    /*
     * Compares its two arguments for order.
     *
     */
    public int compare(Object obj1, Object obj2)
    {
        int idx = rsTitleList.indexOf(sortColumnName);
        int order = 0;

        if( idx != -1 )
        {
            Object col1 = ((ArrayList)obj1).get(idx);
            Object col2 = ((ArrayList)obj2).get(idx);

            if( (col1).equals(VELOCITY_NULL_ENTRY) )
            {
                if( (col2).equals(VELOCITY_NULL_ENTRY))
                {
                    order = 0;
                }
                else
                {
                    order = -1;
                }
            }
            else if( (col2).equals(VELOCITY_NULL_ENTRY) )
            {
                order = 1;
            }
            else
            {
                int type = Integer.parseInt((String)rsTypeList.get(idx));
                switch (type)
                {

                case Types.NUMERIC:
                    order = ( ((java.math.BigDecimal)col1).compareTo((java.math.BigDecimal)col2) );
                    break;

                case Types.DECIMAL:
                    order = ( ((java.math.BigDecimal)col1).compareTo((java.math.BigDecimal)col2) );
                    break;

                case Types.TINYINT:
                    order = ( ((Byte)col1).compareTo((Byte)col2) );
                    break;

                case Types.SMALLINT:
                    order = ( ((Short)col1).compareTo((Short)col2) );
                    break;

                case Types.INTEGER:
                    order = ( ((Integer)col1).compareTo((Integer)col2) );
                    break;

                case Types.BIGINT:
                    order = ( ((Long)col1).compareTo((Long)col2) );
                    break;

                case Types.REAL:
                    order = ( ((Float)col1).compareTo((Float)col2) );
                    break;

                case Types.FLOAT:
                    order = ( ((Double)col1).compareTo((Double)col2) );
                    break;

                case Types.DOUBLE:
                    order = ( ((Double)col1).compareTo((Double)col2) );
                    break;

                case Types.DATE:
                    order = ( ((java.sql.Date)col1).compareTo((java.sql.Date)col2) );
                    break;

                case Types.TIME:
                    order = ( ((java.sql.Time)col1).compareTo((java.sql.Time)col2) );
                    break;

                case Types.TIMESTAMP:
                    order = ( ((java.sql.Timestamp)col1).compareTo((java.sql.Timestamp)col2) );
                    break;

                case Types.CHAR:
                    order = ( ((String)col1).compareTo((String)col2) );
                    break;

                case Types.VARCHAR:
                    order = ( ((String)col1).compareTo((String)col2) );
                    break;

                case Types.LONGVARCHAR:
                    order = ( ((String)col1).compareTo((String)col2) );
                    break;

                default:
                    Log.info("DatabaseBrowserIterator.compare DataType mapping not found"+
                             " in DatabaseBrowserIterator. "+
                             "Hence cannot sort based on provided column.");
                    break;
                }
            }
        }
        //System.out.println("index of type= "+idx +", order= "+order+", ascending= "+ascendingOrder);
        if(!ascendingOrder)
        {
            order = 0-order;
        }
        return order;
    }


}
