/*******************************************************************************
 * 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.rd;

import java.io.File;
import java.io.IOException;

import org.eclipse.birt.core.archive.FileArchiveReader;
import org.eclipse.birt.core.archive.FileArchiveWriter;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.data.engine.api.APITestCase;
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.BaseQueryDefinition;
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.core.DataException;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;

import testutil.ConfigText;

/**
 * This is a simple test case for report document. It mainly tests that these
 * unused bound columns will be saved into report document as well.
 */
public class ReportDocumentTest2 extends APITestCase
{
	private FileArchiveWriter archiveWriter;
	private FileArchiveReader archiveReader;
	
	private String[] rowExprName;
	private String[] totalExprName;
	
	private String queryResultID;
	
	private DataEngine myGenDataEngine;
	private DataEngine myPreDataEngine;
	
	/*
	 * @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" ) );
	}
	
	/**
	 * @throws Exception
	 */
	public void testBasic( ) throws Exception
	{
		String fileName = getOutputFolder( ) + "testData";
		DataEngineContext deContext1 = newContext( DataEngineContext.MODE_GENERATION,
				fileName );
		myGenDataEngine = DataEngine.newDataEngine( deContext1 );
		
		myGenDataEngine.defineDataSource( this.dataSource );
		myGenDataEngine.defineDataSet( this.dataSet );
		
		this.genBasic( );
		this.closeArchiveWriter( );
		
		DataEngineContext deContext2 = newContext( DataEngineContext.MODE_PRESENTATION,
				fileName );
		myPreDataEngine = DataEngine.newDataEngine( deContext2 );
		
		this.preBasic( );
		this.closeArchiveReader( );
	}
	
	
	/**
	 * Inspite of without accessing all resultset iterator. All resultset
	 * iterator should be kept in document.
	 * @throws Exception 
	 */
	public void testHasGapRowInResultSet( ) throws Exception
	{
		String fileName = getOutputFolder( ) + "testData";
		DataEngineContext deContext1 = newContext( DataEngineContext.MODE_GENERATION,
				fileName );
		myGenDataEngine = DataEngine.newDataEngine( deContext1 );
		
		myGenDataEngine.defineDataSource( this.dataSource );
		myGenDataEngine.defineDataSet( this.dataSet );
		
		genHasGapRs();
		this.closeArchiveWriter( );
		
		DataEngineContext deContext2 = newContext( DataEngineContext.MODE_PRESENTATION,
				fileName );
		myPreDataEngine = DataEngine.newDataEngine( deContext2 );

		this.preBasic( );
	}
	
	private void genHasGapRs( ) throws BirtException
	{
		Context context = Context.enter( );		
		Scriptable scope = context.initStandardObjects( );		
		Context.exit( );
		
		//------------generation----------------
		QueryDefinition qd = newReportQuery( );
		
		// prepare
		IBaseExpression[] rowBeArray = getRowExpr( );
		IBaseExpression[] totalBeArray = getAggrExpr( );
		prepareExprNameAndQuery( rowBeArray, totalBeArray, qd );
		
		// generation
		IQueryResults qr = myGenDataEngine.prepare( qd ).execute( scope );
		
		// important step
		queryResultID = qr.getID( );
		
		IResultIterator ri = qr.getResultIterator( );		

		//only access 0 and 3 resultIterator
		ri.next( );
		ri.moveTo( 3 );

		ri.close( );
		qr.close( );
		myGenDataEngine.shutdown( );
	}
	
	/**
	 * Call skipToEnd() when using detail. Expected: all resultset should be
	 * saved in report document.
	 * 
	 * @throws Exception
	 */
	public void testSkipToEndWhenUsingDetail( ) throws Exception
	{
		String fileName = getOutputFolder( ) + "testData";
		DataEngineContext deContext1 = newContext( DataEngineContext.MODE_GENERATION,
				fileName );
		myGenDataEngine = DataEngine.newDataEngine( deContext1 );
		
		myGenDataEngine.defineDataSource( this.dataSource );
		myGenDataEngine.defineDataSet( this.dataSet );
		
		genSkipToEnd( true );
		this.closeArchiveWriter( );
		
		DataEngineContext deContext2 = newContext( DataEngineContext.MODE_PRESENTATION,
				fileName );
		myPreDataEngine = DataEngine.newDataEngine( deContext2 );

		this.preBasic( );
	}
	
	private void genSkipToEnd( boolean useDetails ) throws BirtException
	{
		Context context = Context.enter( );		
		Scriptable scope = context.initStandardObjects( );		
		Context.exit( );
		
		//------------generation----------------
		QueryDefinition qd = newReportQuery( );
		qd.setUsesDetails( useDetails );
		GroupDefinition gd = new GroupDefinition( );
		String columnBindingNameGroup = "COUNTRY";
		IBaseExpression columnBindingExprGroup = new ScriptExpression( "dataSetRow.COUNTRY" );
		gd.setKeyColumn( "COUNTRY" );
		qd.addResultSetExpression( columnBindingNameGroup,
				columnBindingExprGroup );
		qd.addGroup( gd );
		
		// prepare
		IBaseExpression[] rowBeArray = getRowExpr( );
		IBaseExpression[] totalBeArray = getAggrExpr( );
		prepareExprNameAndQuery( rowBeArray, totalBeArray, qd );
		
		// generation
		IQueryResults qr = myGenDataEngine.prepare( qd ).execute( scope );
		
		// important step
		queryResultID = qr.getID( );
		
		IResultIterator ri = qr.getResultIterator( );		

		//only access 0 and 3 resultIterator
		ri.next( );
		ri.moveTo( 4 );
		ri.skipToEnd( 1 );

		ri.close( );
		qr.close( );
		myGenDataEngine.shutdown( );
	}
	
	
	/**
	 * @throws Exception
	 */
	private void genBasic( ) throws Exception
	{		
		Context context = Context.enter( );		
		Scriptable scope = context.initStandardObjects( );		
		Context.exit( );
		
		//------------generation----------------
		QueryDefinition qd = newReportQuery( );
		
		// prepare
		IBaseExpression[] rowBeArray = getRowExpr( );
		IBaseExpression[] totalBeArray = getAggrExpr( );
		prepareExprNameAndQuery( rowBeArray, totalBeArray, qd );
		
		// generation
		IQueryResults qr = myGenDataEngine.prepare( qd ).execute( scope );
		
		// important step
		queryResultID = qr.getID( );
		
		IResultIterator ri = qr.getResultIterator( );		
		while ( ri.next( ) )
		{
			ri.getValue( this.rowExprName[0] );
			ri.getValue( this.totalExprName[0] );
		}
		
		ri.close( );
		qr.close( );
		myGenDataEngine.shutdown( );
	}
	
	/**
	 * @throws Exception
	 */
	private void preBasic( ) throws Exception
	{
		IQueryResults qr = myPreDataEngine.getQueryResults( queryResultID );
		assert ( qr.getResultMetaData( ) != null );
		
		IResultIterator ri = qr.getResultIterator( );
		assert ( ri.getResultMetaData( ) != null );
		
		while ( ri.next( ) )
		{
			String str = "";
			for ( int i = 0; i < rowExprName.length; i++ )
			{
				Object ob2 = ri.getValue( this.rowExprName[i] );
				if ( i != 0 )
					str += " ";
				str += ob2.toString( );
			}
			
			if ( totalExprName != null )
			{
				for ( int i = 0; i < totalExprName.length; i++ )
				{
					Object ob2 = ri.getValue( this.totalExprName[i] );
					str += " " + ob2.toString( );
				}
			}
			testPrintln( str );
		}
		
		ri.close( );
		myPreDataEngine.shutdown( );
		
		this.checkOutputFile( );
	}
	
	/**
	 * @return row expression array
	 */
	private IBaseExpression[] getRowExpr( )
	{
		// row test
		int num = 4;
		IBaseExpression[] rowBeArray = new IBaseExpression[num];
		rowBeArray[0] = new ScriptExpression( "dataSetRow.COUNTRY" );
		rowBeArray[1] = new ScriptExpression( "dataSetRow.CITY" );
		rowBeArray[2] = new ScriptExpression( "dataSetRow.SALE_DATE" );
		rowBeArray[3] = new ScriptExpression( "dataSetRow.AMOUNT" );
		
		this.rowExprName = new String[rowBeArray.length];
		this.rowExprName[0] = "COUNTRY_1";
		this.rowExprName[1] = "CITY_1";
		this.rowExprName[2] = "SALE_NAME_1";
		this.rowExprName[3] = "AMOUNT_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( dataSetRow.AMOUNT )" );
		
		totalExprName = new String[totalBeArray.length];
		this.totalExprName[0] = "TOTAL_COUNT_1";
		this.totalExprName[1] = "TOTAL_AMOUNT_1";
		
		return totalBeArray;
	}
	
	/**
	 * @return folder for report document
	 */
	private String getOutputFolder() 
	{
		return new File(new File(System.getProperty("java.io.tmpdir")),
				"output").getAbsolutePath();
	}
	
	/**
	 * Add expression on the row of group
	 * @param rowBeArray
	 * @param totalBeArray
	 * @param qd
	 */
	private void prepareExprNameAndQuery( IBaseExpression[] rowBeArray,
			IBaseExpression[] totalBeArray, BaseQueryDefinition qd )
	{
		int num = rowBeArray.length;
		
		for ( int i = 0; i < num; i++ )
			qd.addResultSetExpression( this.rowExprName[i], rowBeArray[i] );

		if ( totalBeArray != null )
		{
			int num2 = totalBeArray.length;
			for ( int i = 0; i < num2; i++ )
				qd.addResultSetExpression( this.totalExprName[i], totalBeArray[i] );
		}
	}
	
	/**
	 * @param type
	 * @param fileName
	 * @return
	 * @throws BirtException
	 */
	private DataEngineContext newContext( int type, String fileName ) throws BirtException
	{
		
		switch ( type )
		{
			case DataEngineContext.MODE_GENERATION :
			{
				try
				{
					archiveWriter = new FileArchiveWriter(fileName);
					archiveWriter.initialize();
				}
				catch (IOException e) 
				{
					throw new IllegalArgumentException( e.getMessage() );
				}
				return DataEngineContext.newInstance( DataEngineContext.MODE_GENERATION,
						null,
						null,
						archiveWriter );
			}
			case DataEngineContext.MODE_PRESENTATION :
			{
				try 
				{
					archiveReader = new FileArchiveReader(fileName);
					archiveReader.open();
				} 
				catch (IOException e) 
				{
					throw new IllegalArgumentException( e.getMessage() );
				}
				return DataEngineContext.newInstance( DataEngineContext.MODE_PRESENTATION,
						null,
						archiveReader,
						null );
			}
			default :
				throw new IllegalArgumentException( "" + type );
		}
	}
	
	/**
	 * @throws DataException
	 */
	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 );
			}
	}
	
}
