/*******************************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/
package org.eclipse.birt.data.engine.impl.document.viewing;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.eclipse.birt.data.engine.api.ISortDefinition;
import org.eclipse.birt.data.engine.core.DataException;
import org.eclipse.birt.data.engine.executor.cache.SortSpec;
import org.eclipse.birt.data.engine.executor.transform.group.GroupBy;
import org.eclipse.birt.data.engine.executor.transform.group.GroupUtil;
import org.eclipse.birt.data.engine.i18n.ResourceConstants;
import org.eclipse.birt.data.engine.impl.ColumnInfo;
import org.eclipse.birt.data.engine.impl.QueryExecutorUtil;
import org.eclipse.birt.data.engine.odi.IQuery;
import org.eclipse.birt.data.engine.odi.IResultClass;
import org.eclipse.birt.data.engine.odi.IQuery.GroupSpec;
import org.mozilla.javascript.Context;

/**
 * Some util methods used in viewing
 */
public class ViewingUtil
{
	/**
	 * @param groupSpecs
	 * @param rsMeta
	 * @return
	 * @throws DataException
	 */
	public static GroupBy[] initGroupSpec( IQuery.GroupSpec[] groupSpecs,
			IResultClass rsMeta ) throws DataException
	{
		GroupBy[] groupDefs;

		if ( groupSpecs != null )
		{
			groupDefs = new GroupBy[groupSpecs.length];
			for ( int i = 0; i < groupSpecs.length; ++i )
			{
				int keyIndex = groupSpecs[i].getKeyIndex( );
				String keyColumn = groupSpecs[i].getKeyColumn( );

				if ( GroupUtil.isRowIdColumn( keyIndex, keyColumn ) )
				{
					groupDefs[i] = GroupBy.newInstanceForRowID( groupSpecs[i] );
					continue;
				}

				// Convert group key name to index for faster future access
				// assume priority of keyColumn is higher than keyIndex
				if ( keyColumn != null )
					keyIndex = rsMeta.getFieldIndex( keyColumn );

				if ( keyIndex < 1 || keyIndex > rsMeta.getFieldCount( ) )
				{
					// Invalid group key name
					throw new DataException( ResourceConstants.INVALID_GROUP_KEY_COLUMN,
							keyColumn );
				}
				groupDefs[i] = GroupBy.newInstance( groupSpecs[i],
						keyIndex,
						null,
						rsMeta.getFieldValueClass( keyIndex ) );
			}
		}
		else
		{
			groupDefs = new GroupBy[0];
		}

		return groupDefs;
	}
	
	/**
	 * @param sorts
	 * @return
	 */
	public static List getSortList( List sorts )
	{
		Context cx = Context.enter( );

		IQuery.SortSpec[] sortSpecs = null;
		if ( sorts != null && sorts.size( ) > 0 )
		{
			sortSpecs = new IQuery.SortSpec[sorts.size( )];
			Iterator it = sorts.iterator( );
			for ( int i = 0; it.hasNext( ); i++ )
			{
				ISortDefinition src = (ISortDefinition) it.next( );
				int sortIndex = -1;
				String sortKey = src.getColumn( );
				if ( sortKey == null || sortKey.length( ) == 0 )
				{
					// Firstly try to treat sort key as a column reference
					// expression
					ColumnInfo columnInfo = QueryExecutorUtil.getColInfoFromJSExpr( cx,
							src.getExpression( ).getText( ) );

					sortIndex = columnInfo.getColumnIndex( );
					sortKey = columnInfo.getColumnName( );
				}
				IQuery.SortSpec dest = new IQuery.SortSpec( sortIndex,
						sortKey,
						src.getSortDirection( ) == ISortDefinition.SORT_ASC );
				sortSpecs[i] = dest;
			}
		}

		Context.exit( );
		if ( sortSpecs == null )
			return null;
		else
			return Arrays.asList( sortSpecs );
	}
	
	/**
	 * @param groupDefs
	 * @param sortDefns
	 * @param rsMeta
	 * @return
	 * @throws DataException
	 */
	public static SortSpec getSortSpec( GroupSpec[] groupDefns,
			IQuery.SortSpec[] sortDefns, IResultClass rsMeta )
			throws DataException
	{
		// Create an array of column indexes. We first sort by Group keys, then
		// sort keys
		int groupCount = 0;
		int sortCount = 0;
		
		if ( groupDefns != null )
		{
			for ( int i = 0; i < groupDefns.length; i++ )
			{
				// When group column is rowid, sort on it should not
				// take effect.
				if ( groupDefns[i].getKeyIndex( ) >= 0 )
				{
					groupCount++;
				}
			}
		}

		if ( sortDefns != null )
		{
			sortCount = sortDefns.length;
		}

		int[] sortKeyIndexes = new int[groupCount + sortCount];
		String[] sortKeyNames = new String[groupCount + sortCount];
		boolean[] sortAscending = new boolean[groupCount + sortCount];

		for ( int i = 0; i < groupCount; i++ )
		{
			int index = groupDefns[i].getKeyIndex( );
			if ( index >= 0 )
			{
				sortKeyIndexes[i] = groupDefns[i].getKeyIndex( );
				sortKeyNames[i] = groupDefns[i].getKeyColumn( );
				sortAscending[i] = groupDefns[i].getSortDirection( ) != IQuery.GroupSpec.SORT_DESC;
			}
		}
		for ( int i = 0; i < sortCount; i++ )
		{
			int keyIndex = sortDefns[i].getIndex( );
			String keyName = sortDefns[i].getField( );

			// If sort key name exist (not null) then depend on key name, else
			// depend on key index
			if ( keyName != null )
				keyIndex = rsMeta.getFieldIndex( keyName );

			if ( keyIndex < 1 || keyIndex > rsMeta.getFieldCount( ) )
				// Invalid sort key name
				throw new DataException( ResourceConstants.INVALID_KEY_COLUMN,
						keyName );
			
			sortKeyIndexes[groupCount + i] = keyIndex;
			sortKeyNames[groupCount + i] = sortDefns[i].getField( );
			sortAscending[groupCount + i] = sortDefns[i].isAscendingOrder( );
		}

		return new SortSpec( sortKeyIndexes, sortKeyNames, sortAscending );
	}

}
