
package org.eclipse.birt.tests.data.engine.api;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.birt.core.archive.FileArchiveReader;
import org.eclipse.birt.core.archive.FileArchiveWriter;
import org.eclipse.birt.core.data.DataType;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.data.engine.api.DataEngine;
import org.eclipse.birt.data.engine.api.DataEngineContext;
import org.eclipse.birt.data.engine.api.IBaseExpression;
import org.eclipse.birt.data.engine.api.IQueryResults;
import org.eclipse.birt.data.engine.api.IResultIterator;
import org.eclipse.birt.data.engine.api.querydefn.BaseDataSetDesign;
import org.eclipse.birt.data.engine.api.querydefn.BaseQueryDefinition;
import org.eclipse.birt.data.engine.api.querydefn.ComputedColumn;
import org.eclipse.birt.data.engine.api.querydefn.GroupDefinition;
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.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;

import testutil.APITestCase;
import testutil.ConfigText;

public class ReportDoc_GroupTest extends APITestCase
{

	// instance of DataEngine
	private DataEngine myDataEngine;

	private ScriptableObject scope;

	private ScriptableObject subScope;

	private ScriptableObject subOfSubScope;

	// id to store
	private String queryResultID;

	private String[] rowExprName;

	private String[] totalExprName;

	/** computed column name */
	private String[] ccName;

	/** computed column expression */
	private String[] ccExpr;

	// name of sub query
	private String subQueryName = "test";

	// name of sub query of sub query
	private String subOfSubQueryName = "test2";

	// value to compare
	private List expectedValue;
	private FileArchiveWriter archiveWriter;
	private FileArchiveReader archiveReader;

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

	/*
	 * @see junit.framework.TestCase#setUp()
	 */
	public void setUp( ) throws Exception
	{
		super.setUp( );

		DataEngineContext deContext = newContext( DataEngineContext.MODE_GENERATION );
		myDataEngine = DataEngine.newDataEngine( deContext );

		myDataEngine.defineDataSource( this.dataSource );
		myDataEngine.defineDataSet( this.dataSet );

		ccName = new String[]{"cc1", "cc2"};

		ccExpr = new String[]{"10*row.AMOUNT", "row.COUNTRY"};

		ComputedColumn computedColumn = new ComputedColumn( ccName[0],
				ccExpr[0], DataType.INTEGER_TYPE );
		ComputedColumn computedColumn1 = new ComputedColumn( ccName[1],
				ccExpr[1], DataType.STRING_TYPE );

		( (BaseDataSetDesign) this.dataSet ).addComputedColumn( computedColumn );
		( (BaseDataSetDesign) this.dataSet )
				.addComputedColumn( computedColumn1 );

		expectedValue = new ArrayList( );

		Context context = Context.enter( );

		scope = context.initStandardObjects( );

		subScope = context.initStandardObjects( );
		subScope.setPrototype( scope );

		subOfSubScope = context.initStandardObjects( );
		subOfSubScope.setPrototype( subScope );

		Context.exit( );
	}

	/**
	 * report document with group on computed column
	 * 
	 * @throws BirtException
	 */
	public void testGroup( ) throws BirtException
	{
		this.genGroup( );
		this.closeArchiveWriter( );
		this.preGroup( );
		this.closeArchiveReader( );
	}

	/**
	 * @throws BirtException
	 */
	private void genGroup( ) throws BirtException
	{
		QueryDefinition qd = newReportQuery( );

		// prepare
		IBaseExpression[] rowBeArray = getRowExpr( );
		IBaseExpression[] totalBeArray = getAggrExpr( );
		prepareExprNameAndQuery( rowBeArray, totalBeArray, qd );

		GroupDefinition groupDefn = new GroupDefinition( );
		groupDefn.setKeyExpression( "row.cc1" );
		qd.addGroup( groupDefn );

		// generation
		IQueryResults qr = myDataEngine.prepare( qd ).execute( scope );

		// important step
		saveForPresentation( qr, rowBeArray, totalBeArray );

		IResultIterator ri = qr.getResultIterator( );
		while ( ri.next( ) )
		{
			for ( int i = 0; i < rowBeArray.length; i++ )
				expectedValue.add( ri.getValue( rowBeArray[i].toString( ) ) );

			for ( int i = 0; i < totalBeArray.length; i++ )
				expectedValue.add( ri.getValue( totalBeArray[i].toString( ) ) );
		}

		ri.close( );
		qr.close( );
		myDataEngine.shutdown( );
	}

	/**
	 * @throws BirtException
	 */
	private void preGroup( ) throws BirtException
	{
		DataEngineContext deContext = newContext( DataEngineContext.MODE_PRESENTATION );
		myDataEngine = DataEngine.newDataEngine( deContext );
		IQueryResults qr = myDataEngine.getQueryResults( queryResultID );
		IResultIterator ri = qr.getResultIterator( );

		checkResult( expectedValue, ri );

		ri.close( );
		myDataEngine.shutdown( );
	}

	/**
	 * report document with sorted group on date time type column
	 * 
	 * @throws BirtException
	 */
	public void testSortedGroup( ) throws BirtException
	{
		this.genSortedGroup( );

		this.closeArchiveWriter( );
		this.preSortedGroup( );
		this.closeArchiveReader( );
	}

	/**
	 * @throws BirtException
	 */
	private void genSortedGroup( ) throws BirtException
	{
		QueryDefinition qd = newReportQuery( );

		// prepare
		IBaseExpression[] rowBeArray = getRowExpr( );
		IBaseExpression[] totalBeArray = getAggrExpr( );
		prepareExprNameAndQuery( rowBeArray, totalBeArray, qd );

		GroupDefinition groupDefn = new GroupDefinition( );
		groupDefn.setKeyExpression( "row.SALE_DATE" );
		groupDefn.setInterval( GroupDefinition.MONTH_INTERVAL );
		groupDefn.setIntervalRange( 2 );
		SortDefinition sortDefn = new SortDefinition( );
		sortDefn.setExpression( "Total.sum(row.AMOUNT,null,1)" );
		sortDefn.setSortDirection( SortDefinition.SORT_DESC );
		groupDefn.addSort( sortDefn );
		qd.addGroup( groupDefn );

		// generation
		IQueryResults qr = myDataEngine.prepare( qd ).execute( scope );

		// important step
		saveForPresentation( qr, rowBeArray, totalBeArray );

		IResultIterator ri = qr.getResultIterator( );
		while ( ri.next( ) )
		{
			for ( int i = 0; i < rowBeArray.length; i++ )
				expectedValue.add( ri.getValue( rowBeArray[i].toString( ) ) );

			for ( int i = 0; i < totalBeArray.length; i++ )
				expectedValue.add( ri.getValue( totalBeArray[i].toString( ) ) );
		}

		ri.close( );
		qr.close( );
		myDataEngine.shutdown( );
	}

	/**
	 * @throws BirtException
	 */
	private void preSortedGroup( ) throws BirtException
	{
		DataEngineContext deContext = newContext( DataEngineContext.MODE_PRESENTATION );
		myDataEngine = DataEngine.newDataEngine( deContext );
		IQueryResults qr = myDataEngine.getQueryResults( queryResultID );
		IResultIterator ri = qr.getResultIterator( );

		checkResult( expectedValue, ri );

		ri.close( );
		myDataEngine.shutdown( );
	}

	/**
	 * @param type
	 * @return context
	 * @throws BirtException
	 */
	private DataEngineContext newContext( int type ) throws BirtException
	{
		switch ( type )
		{
			case DataEngineContext.MODE_GENERATION :
			{
				archiveWriter = null;
				try
				{
					archiveWriter = new FileArchiveWriter( "./test2" );
					archiveWriter.initialize( );
				}
				catch ( IOException e )
				{
					throw new IllegalArgumentException( e.getMessage( ) );
				}
				return DataEngineContext.newInstance(
						DataEngineContext.MODE_GENERATION, null, null,
						archiveWriter );
			}
			case DataEngineContext.MODE_PRESENTATION :
			{
				archiveReader = null;
				try
				{
					archiveReader = new FileArchiveReader( "./test2" );
					archiveReader.open( );
				}
				catch ( IOException e )
				{
					throw new IllegalArgumentException( e.getMessage( ) );
				}
				return DataEngineContext.newInstance(
						DataEngineContext.MODE_PRESENTATION, null,
						archiveReader, null );
			}
			default :
				throw new IllegalArgumentException( "" + type );
		}
	}

	/**
	 * @return row expression array
	 */
	private IBaseExpression[] getRowExpr( )
	{

		// row test
		int num = 6;
		IBaseExpression[] rowBeArray = new IBaseExpression[num];
		rowBeArray[0] = new ScriptExpression( "row.COUNTRY" );
		rowBeArray[1] = new ScriptExpression( "row.CITY" );
		rowBeArray[2] = new ScriptExpression( "row.SALE_DATE" );
		rowBeArray[3] = new ScriptExpression( "row.AMOUNT" );
		rowBeArray[4] = new ScriptExpression( "row." + ccName[0] );
		rowBeArray[5] = new ScriptExpression( "row." + ccName[1] );

		return rowBeArray;
	}

	/**
	 * @return aggregation expression array
	 */
	private IBaseExpression[] getAggrExpr( )
	{
		int num2 = 2;
		IBaseExpression[] totalBeArray = new IBaseExpression[num2];
		totalBeArray[0] = new ScriptExpression( "Total.Count( )" );
		totalBeArray[1] = new ScriptExpression( "Total.Sum( row.AMOUNT )" );

		return totalBeArray;
	}

	/**
	 * @param rowBeArray
	 * @param totalBeArray
	 * @param qd
	 */
	private void prepareExprNameAndQuery( IBaseExpression[] rowBeArray,
			IBaseExpression[] totalBeArray, BaseQueryDefinition qd )
	{
		int num = rowBeArray.length;
		int num2 = totalBeArray.length;

		rowExprName = new String[num];
		totalExprName = new String[num2];

		for ( int i = 0; i < num; i++ )
			qd
					.addResultSetExpression( rowBeArray[i].toString( ),
							rowBeArray[i] );

		for ( int i = 0; i < num2; i++ )
			qd.addResultSetExpression( totalBeArray[i].toString( ),
					totalBeArray[i] );
	}

	/**
	 * @param rowBeArray
	 * @param totalBeArray
	 */
	private void saveForPresentation( IQueryResults queryResuls,
			IBaseExpression[] rowBeArray, IBaseExpression[] totalBeArray )
	{
		queryResultID = queryResuls.getID( );

		int num = rowBeArray.length;
		int num2 = totalBeArray.length;

		rowExprName = new String[num];
		totalExprName = new String[num2];

		for ( int i = 0; i < num; i++ )
			rowExprName[i] = rowBeArray[i].toString( );

		for ( int i = 0; i < num2; i++ )
			totalExprName[i] = totalBeArray[i].toString( );
	}

	/**
	 * @param it
	 * @param ri
	 * @throws DataException
	 * @throws BirtException
	 */
	private void checkResult( List list, IResultIterator ri )
			throws BirtException
	{
		Iterator it = list.iterator( );

		System.out.print( list );
		while ( ri.next( ) )
		{
			String str = "";
			for ( int i = 0; i < rowExprName.length; i++ )
			{
				Object ob1 = it.next( );
				Object ob2 = ri.getValue( newExpression( rowExprName[i] )
						.toString( ) );
				assertEquals( ob1, ob2 );
				str += " " + ob2.toString( );
			}

			for ( int i = 0; i < totalExprName.length; i++ )
			{
				Object ob1 = it.next( );
				Object ob2 = ri.getValue( newExpression( totalExprName[i] )
						.toString( ) );
				assertEquals( ob1, ob2 );
				str += " " + ob2.toString( );
			}

			System.out.println( str );
		}
	}

	/**
	 * New an expression for retrieving expression value
	 * 
	 * @param exprID
	 * @return expression instance
	 */
	private static IBaseExpression newExpression( final String exprID )
	{
		return new IBaseExpression( ) {

			public String getID( )
			{
				return exprID;
			}

			public void setID( String exprID )
			{
			}

			public int getDataType( )
			{
				return 0;
			}

			public Object getHandle( )
			{
				return null;
			}

			public void setHandle( Object handle )
			{
			}

			public String getGroupName( )
			{
				// TODO Auto-generated method stub
				return null;
			}

			public void setGroupName( String name )
			{
				// TODO Auto-generated method stub

			}

		};
	}

	private void closeArchiveWriter( ) throws DataException
	{
		if ( archiveWriter != null )
			try
			{
				archiveWriter.finish( );
			}
			catch ( IOException e )
			{
				throw new DataException( "error", e );
			}
	}

	/**
	 * @throws DataException
	 */
	private void closeArchiveReader( ) throws DataException
	{
		if ( archiveReader != null )
			try
			{
				archiveReader.close( );
			}
			catch ( IOException e )
			{
				throw new DataException( "error", e );
			}
	}

}
