/*******************************************************************************
 * 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.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.APITestCase;
import testutil.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.IResultMetaData;
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.BaseTransform;
import org.eclipse.birt.data.engine.api.querydefn.ComputedColumn;
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.ScriptableObject;

import testutil.ConfigText;

/**
 * Test the feature of report document. A life cycle of report document can be
 * divided into two parts, one is generation and the other is presentation. Once
 * a report can be generated once, it can be presented many times. In generation
 * time, query results ID and expression id needs to be stored. These ID are
 * automatically generated by DtE implemenation. In presentation time, query
 * results ID is first used to retrieve the query result, and then expression ID
 * can be used to retrieve the detailed data.
 */
public class ReportDoc_Gen_PreModeTest 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[] ccName;
	private String[] ccExpr;
	
	// 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" ) );
	}

	/**
	 * Check that in output column page of edit data set dialog, 
	 * Computed column(Such as integer_type, string_type,DateTime_type,Float_type)
	 *   has been added to data set
	 */
	public void setUp( ) throws Exception
	{
		super.setUp( );

		ccName = new String[]{
				"test", "testInteger","testDateTime","testFloat"
		};
		ccExpr = new String[]{
				"row.COUNTRY", "row.AMOUNT*100","row.SALE_DATE","row.AMOUNT-13.95"
		};
						
		for ( int i = 0; i < 4; i++ )
		{
			ComputedColumn computedColumn = null;
			switch ( i )
			{
				case 0 :
					computedColumn = new ComputedColumn( ccName[i],
							ccExpr[i],
							DataType.STRING_TYPE );
					break;
				case 1:
					computedColumn = new ComputedColumn( ccName[i],
							ccExpr[i],
							DataType.INTEGER_TYPE );
					break;
				case 2:
					computedColumn = new ComputedColumn( ccName[i],
							ccExpr[i],
							DataType.DATE_TYPE);
					break;
				case 3:
					computedColumn = new ComputedColumn( ccName[i],
							ccExpr[i],
							DataType.DOUBLE_TYPE);
					break;
				
			}
			( (BaseDataSetDesign) this.dataSet ).addComputedColumn( computedColumn );
		}
		
		DataEngineContext deContext = newContext( DataEngineContext.MODE_GENERATION );
		myDataEngine = DataEngine.newDataEngine( deContext );

		myDataEngine.defineDataSource( this.dataSource );
		myDataEngine.defineDataSet( this.dataSet );
		
		expectedValue = new ArrayList( );
	

		Context context = Context.enter( );
		
		scope = context.initStandardObjects( );
		
		subScope = context.initStandardObjects( );
		subScope.setPrototype( scope );
		
		subOfSubScope = context.initStandardObjects( );
		subOfSubScope.setPrototype( subScope );
		
		Context.exit( );
	}

	/**
	 * Basic report document test without sub query, but it has aggregation.
	 * 
	 * @throws BirtException
	 */
	public void testBasic( ) throws BirtException
	{
		this.genBasic( );
		this.closeArchiveWriter( );		
		this.preBasic( );
		this.closeArchiveReader( );
		
	}

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

		// prepare
		IBaseExpression[] rowBeArray = getRowExpr( );
		prepareExprNameAndQuery( rowBeArray, qd );
		
		// generation
		IQueryResults qr = myDataEngine.prepare( qd ).execute( scope );
		
		
		IResultMetaData md=qr.getResultMetaData();
		for(int i=1;i<11;i++)
		 {System.out.println(md.getColumnName(i));}
		
			
		
		// important step
		saveForPresentation( qr, rowBeArray);
		
		
		IResultIterator ri = qr.getResultIterator( );		
		while ( ri.next( ) )
		{
			for ( int i = 0; i < rowBeArray.length; i++ )
				expectedValue.add( ri.getValue( rowBeArray[i].toString( ) ) );
		
		}

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

	/**
	 * @throws BirtException
	 */
	private void preBasic( ) throws BirtException
	{
		DataEngineContext deContext = newContext( DataEngineContext.MODE_PRESENTATION );
		myDataEngine = DataEngine.newDataEngine( deContext );
		IQueryResults qr = myDataEngine.getQueryResults( queryResultID );
		assert ( qr.getResultMetaData( ) != null );
		
		IResultIterator ri = qr.getResultIterator( );
		assert ( ri.getResultMetaData( ) != null );
		
		checkResult( 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("d:/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("d:/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 = 8;
		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" );
		for ( int i = 0; i < ccName.length; i++ )
		{
			rowBeArray[i+4] = new ScriptExpression( "row." + this.ccName[i] );
		}
		final int expectedLen = 15;
		String metaData = "";
		metaData += formatStr( "COUNTRY", expectedLen );
		metaData += formatStr( "CITY", expectedLen );
		metaData += formatStr( "SALE_DATE", expectedLen );
		metaData += formatStr( "SALE_DATE", expectedLen );
		for ( int i = 0; i < ccName.length; i++ )
		{  
			metaData += formatStr( ccName[i], expectedLen );}
		this.testPrintln(metaData);

		return rowBeArray;
	}
	

	/**
	 * @param rowBeArray
	 * @param totalBeArray
	 * @param qd
	 */
	private void prepareExprNameAndQuery( IBaseExpression[] rowBeArray,
			 BaseQueryDefinition qd )
	{
		int num = rowBeArray.length;
		
		rowExprName = new String[num];
		
		for ( int i = 0; i < num; i++ )
			qd.addResultSetExpression( rowBeArray[i].toString( ), rowBeArray[i]);
	}
	
	/**
	 * @param rowBeArray
	 * @param totalBeArray
	 */
	private void saveForPresentation( IQueryResults queryResuls,
			IBaseExpression[] rowBeArray )
	{
		queryResultID = queryResuls.getID( );
		
		int num = rowBeArray.length;
		rowExprName = new String[num];
	
		for ( int i = 0; i < num; i++ )
			rowExprName[i] = rowBeArray[i].toString( );		
	}

	/**
	 * Only check the result of the expectedValue of the result set
	 * 
	 * @param it
	 * @param ri
	 * @throws DataException
	 * @throws BirtException
	 */
	private void checkResult( IResultIterator ri )
			throws BirtException
	{
      
		Iterator it = this.expectedValue.iterator( );
	
	
		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( );
			}

		
			System.out.println( "row result set: " + str );
		}
	}

	/**
	 * New an expression for retrieving expression value
	 * 
	 * @param exprID
	 * @return expression instance
	 */
	private static IBaseExpression newExpression( final String exprID )
	{
		return new IBaseExpression( ) {
			/*
			 * @see org.eclipse.birt.data.engine.api.IBaseExpression#getID()
			 */
			public String getID( ){ return exprID; }
			/*
			 * @see org.eclipse.birt.data.engine.api.IBaseExpression#setID(java.lang.String)
			 */
			public void setID( String exprID ){ }
			/*
			 * @see org.eclipse.birt.data.engine.api.IBaseExpression#getDataType()
			 */
			public int getDataType( ){ return 0; }
			/*
			 * @see org.eclipse.birt.data.engine.api.IBaseExpression#getHandle()
			 */
			public Object getHandle( ){	return null; }
			/*
			 * @see org.eclipse.birt.data.engine.api.IBaseExpression#setHandle(java.lang.Object)
			 */
			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 static String formatStr( String inputStr, int length )
	{
		if ( inputStr == null )
			return null;

		int inputLen = inputStr.length( );
		if ( inputLen >= length )
			return inputStr;

		int appendLen = length - inputLen;
		char[] appendChar = new char[appendLen];
		for ( int i = 0; i < appendLen; i++ )
		{
			appendChar[i] = ' ';
		}
		String result = inputStr + new String( appendChar );

		return result;
	}
	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 );
			}
	}
	
}
