/*******************************************************************************
 * Copyright (c) 2004, 2005 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.tests.data.engine.acceptance;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.eclipse.birt.core.data.DataType;
import org.eclipse.birt.data.engine.api.querydefn.BaseTransform;
import org.eclipse.birt.data.engine.api.querydefn.GroupDefinition;
import org.eclipse.birt.data.engine.api.querydefn.InputParameterBinding;
import org.eclipse.birt.data.engine.api.querydefn.OdaDataSetDesign;
import org.eclipse.birt.data.engine.api.querydefn.ParameterDefinition;
import org.eclipse.birt.data.engine.api.querydefn.QueryDefinition;
import org.eclipse.birt.data.engine.api.querydefn.ScriptExpression;
import org.eclipse.birt.data.engine.api.querydefn.SortDefinition;
import org.eclipse.birt.data.engine.core.DataException;
// import org.eclipse.birt.data.engine.api.APITestCase;
import testutil.APITestCase;
import org.eclipse.birt.data.engine.api.IBaseDataSetDesign;
import org.eclipse.birt.data.engine.api.IFilterDefinition;
import org.eclipse.birt.data.engine.api.IPreparedQuery;
import org.eclipse.birt.data.engine.api.IQueryResults;
import org.eclipse.birt.data.engine.api.IResultIterator;
import org.eclipse.birt.data.engine.api.ISortDefinition;

import org.eclipse.birt.data.engine.api.IInputParameterBinding;

import testutil.ConfigText;

/**
 * Input parameter binding test. There are two sources of parameter binding.
 * parameter binding added on data set parameter binding added on query
 * definition The latter has higher prioiry than the former.
 */
public class InputParameterTest extends APITestCase
{

	private static final String TEST_TABLE_NAME = ConfigText
			.getString( "Api.TestData.TableName" );

	/*
	 * @see org.eclipse.birt.data.engine.api.APITestCase#getDataSourceInfo()
	 */
	protected DataSourceInfo getDataSourceInfo( )
	{
		return new DataSourceInfo( TEST_TABLE_NAME, ConfigText
				.getString( "Api.TestData.TableSQL" ), ConfigText
				.getString( "Api.TestData.TestDataFileName" ) );
	}

	/*
	 * @see testutil.BaseTestCase#getTestTableName()
	 */
	protected String getTestTableName( )
	{
		return TEST_TABLE_NAME;
	}

	/**
	 * Test one input parameter, larger than in sql statement
	 * 
	 * @throws Exception
	 */
	public void test1( ) throws Exception
	{
		IBaseDataSetDesign baseDataset = newDataSet( "newDataSet",
				"select * from " + InputParameterTest.TEST_TABLE_NAME
						+ " where AMOUNT > ?" );

		Collection inputParamDefns = new ArrayList( );
		ParameterDefinition inputParamDefn = new ParameterDefinition( "param1",
				DataType.INTEGER_TYPE );
		inputParamDefn.setInputMode( true );
		inputParamDefn.setPosition( 1 );
		inputParamDefn.setDefaultInputValue( "0" );
		inputParamDefns.add( inputParamDefn );

		Collection inputParamBindings = new ArrayList( );
		InputParameterBinding paramBinding = new InputParameterBinding( 1,
				new ScriptExpression( "100" ) );
		inputParamBindings.add( paramBinding );

		runQuery( baseDataset, inputParamDefns, inputParamBindings );
	}

	/**
	 * Test one input parameter, equal in sql statement
	 * 
	 * @throws Exception
	 */

	public void test3( ) throws Exception
	{
		IBaseDataSetDesign baseDataset = newDataSet( "newDataSet",
				"select * from " + InputParameterTest.TEST_TABLE_NAME
						+ " where CITY= ? " );

		ParameterDefinition inputParamDefn = new ParameterDefinition( "param1",
				DataType.STRING_TYPE, true, false );
		inputParamDefn.setPosition( 1 );
		inputParamDefn.setDefaultInputValue( "0" );
		Collection inputParamDefns = new ArrayList( );
		inputParamDefns.add( inputParamDefn );

		Collection inputParamBindings = new ArrayList( );
		InputParameterBinding paramBinding = new InputParameterBinding( 1,
				new ScriptExpression( "'Shanghai'" ) );
		inputParamBindings.add( paramBinding );
		runQuery( baseDataset, inputParamDefns, inputParamBindings );

	}

	/**
	 * Test one input parameter, input "NULL" in sql statement
	 * 
	 * @throws Exception
	 */
	public void test4( ) throws Exception
	{
		IBaseDataSetDesign baseDataset = newDataSet( "newDataSet",
				"select * from " + InputParameterTest.TEST_TABLE_NAME
						+ " where NULL_COLUMN = ?" );

		Collection inputParamDefns = new ArrayList( );
		ParameterDefinition inputParamDefn = new ParameterDefinition( "param1",
				DataType.ANY_TYPE );
		inputParamDefn.setInputMode( true );
		inputParamDefn.setPosition( 1 );
		inputParamDefn.setDefaultInputValue( "1" );
		inputParamDefns.add( inputParamDefn );

		Collection inputParamBindingsOfDS = new ArrayList( );
		InputParameterBinding paramBinding = new InputParameterBinding( 1,
				new ScriptExpression( "null" ) );
		inputParamBindingsOfDS.add( paramBinding );

		try
		{
			runQuery( baseDataset, inputParamDefns, inputParamBindingsOfDS,
					null );
			fail( "Should not arrive here" );
		}
		catch ( DataException e )
		{
			// expect a DataException
		}
	}

	/**
	 * Simple calling method than below one.
	 * 
	 * @param dataSet
	 * @param inputParamDefns
	 * @param inputParamBindingsOfQuery
	 * @throws Exception
	 */
	private void runQuery( IBaseDataSetDesign dataSet,
			Collection inputParamDefns, Collection inputParamBindingsOfQuery )
			throws Exception
	{
		runQuery( dataSet, inputParamDefns, null, inputParamBindingsOfQuery );
	}

	/**
	 * A Standard ReportQueryDefn, first add parameter to data set
	 * 
	 * @param dataSet
	 * @param inputParamDefns
	 * @param inputParamBindingsOfDS
	 *            added to data set
	 * @param inputParamBindingsOfQuery
	 *            added to query defintion
	 * @param fileName
	 * @throws Exception
	 */
	private void runQuery( IBaseDataSetDesign dataSet,
			Collection inputParamDefns, Collection inputParamBindingsOfDS,
			Collection inputParamBindingsOfQuery ) throws Exception
	{
		if ( inputParamDefns != null )
		{
			Iterator iterator = inputParamDefns.iterator( );
			while ( iterator.hasNext( ) )
			{
				ParameterDefinition paramDefn = (ParameterDefinition) iterator
						.next( );
				if ( paramDefn.isInputMode( ) )
					( (OdaDataSetDesign) dataSet ).addParameter( paramDefn );
			}
		}

		if ( inputParamBindingsOfDS != null )
		{
			Iterator iteratorOfDS = inputParamBindingsOfDS.iterator( );
			while ( iteratorOfDS.hasNext( ) )
			{
				IInputParameterBinding paramBinds = (IInputParameterBinding) iteratorOfDS
						.next( );
				( (OdaDataSetDesign) dataSet )
						.addInputParamBinding( paramBinds );
			}
		}

		createAndRunQuery( dataSet, inputParamBindingsOfQuery );
		checkOutputFile( );
	}

	/**
	 * create query definition and run it
	 */
	private void createAndRunQuery( IBaseDataSetDesign dataSet,
			Collection inputParamBindings ) throws Exception
	{
		GroupDefinition[] groupDefn = new GroupDefinition[]{
				new GroupDefinition( "group1" ), new GroupDefinition( "group2" )};
		groupDefn[0].setKeyExpression( "row.COUNTRY" );
		groupDefn[1].setKeyExpression( "row.CITY" );

		SortDefinition[] sortDefn = new SortDefinition[]{new SortDefinition( )};
		sortDefn[0].setColumn( "SALE_DATE" );
		sortDefn[0].setSortDirection( ISortDefinition.SORT_DESC );

		ScriptExpression[] expressions = new ScriptExpression[]{
				new ScriptExpression( "row.COUNTRY", 0 ),
				new ScriptExpression( "row.CITY", 0 ),
				new ScriptExpression( "row.SALE_DATE", 0 ),
				new ScriptExpression( "row.AMOUNT", 0 )};

		QueryDefinition queryDefn = createQueryDefn( dataSet, expressions,
				groupDefn, sortDefn, null, inputParamBindings );
		IPreparedQuery preparedQuery = dataEngine.prepare( queryDefn );
		IQueryResults queryResults = preparedQuery.execute( null );
		IResultIterator it = queryResults.getResultIterator( );
		outputQueryResult( it, new String[]{"COL_COUNTRY", "COL_CITY",
				"COL_SALE_DATE", "COL_AMOUNT"} );

	}

	/**
	 * create query definition
	 * 
	 * @param dataSet
	 * @param expressions
	 * @param groupDefn
	 * @param sortDefn
	 * @param filters
	 * @param inputParamBindings
	 * @return
	 */
	private QueryDefinition createQueryDefn( IBaseDataSetDesign dataSet,
			ScriptExpression[] expressions, GroupDefinition[] groupDefn,
			SortDefinition[] sortDefn, IFilterDefinition[] filters ,
			Collection inputParamBindings)
	{
		// define a query design

		QueryDefinition queryDefn = newReportQuery( dataSet );

		if ( groupDefn != null )
			for ( int i = 0; i < groupDefn.length; i++ )
				queryDefn.addGroup( groupDefn[i] );
		if ( sortDefn != null )
			for ( int i = 0; i < sortDefn.length; i++ )
				queryDefn.addSort( sortDefn[i] );
		if ( expressions != null )
			for ( int i = 0; i < expressions.length; i++ )
				queryDefn.addResultSetExpression( expressions[i].toString( ), expressions[i] );

		if ( filters != null )
			for ( int i = 0; i < filters.length; i++ )
				queryDefn.addFilter( filters[i] );
		
		if ( inputParamBindings != null )
		{
			Iterator iterator = inputParamBindings.iterator( );
			while ( iterator.hasNext( ) )
			{
				InputParameterBinding inputParamBinding = (InputParameterBinding) iterator.next( );
				queryDefn.addInputParamBinding( inputParamBinding );
			}
		}
		
		return queryDefn;
	}
}