
package org.eclipse.birt.test.performance;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;

import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.IReportEngineFactory;
import org.eclipse.birt.report.engine.api.impl.ReportEngineFactory;

/**
 * Scheduler - schedule tests - prepare for testing - execute warmup tests -
 * execute actual tests - calculate avg. execution time
 */
public class Scheduler
{

	private RunThread runThread = null;

	private RenderThread renderThread = null;

	private RunRenderThread runRenderThread = null;

	private EngineConfig config = null;

	private IReportEngineFactory factory = null;

	private IReportEngine engine = null;

	private Semaphore semaphore = null;

	private FileWriter fileWriter = null;

	private PrintWriter printWriter = null;

	private static final int MODE_JAVA = 0;

	private static final int MODE_ECLIPSE = 1;

	private String basePath = ""; //$NON-NLS-1$

	/**
	 * Constructor
	 * 
	 * @param mode -
	 *            execution mode
	 */
	public Scheduler( int mode, String testCase )
	{

		File folderRun = null;
		File folderRender = null;
		File folderRunRender = null;

		// get time stamp for statsFile
		Calendar calendar = Calendar.getInstance( );
		SimpleDateFormat format = new SimpleDateFormat( "yyyyMMdd_hhmmss" ); //$NON-NLS-1$
		String timeStamp = "_" + format.format( calendar.getTime( ) ) + ".log"; //$NON-NLS-1$//$NON-NLS-2$
		String statsFile = ""; //$NON-NLS-1$

		try
		{
			if ( mode == MODE_JAVA )
			{
				// set basePath
				basePath = "./"; //$NON-NLS-1$

				// set output folder
				folderRun = new File( basePath + Constants.RUN_PATH );
				folderRender = new File( basePath + Constants.RENDER_PATH );
				folderRunRender = new File( basePath + Constants.RUNRENDER_PATH );
			}
			if ( mode == MODE_ECLIPSE )
			{
				// set basePath
				basePath = new File( this.getClass( ).getProtectionDomain( )
						.getCodeSource( ).getLocation( ).getPath( ) )
						.getParent( ) + "/"; //$NON-NLS-1$

				folderRun = new File( basePath + Constants.RUN_PATH );
				folderRender = new File( basePath + Constants.RENDER_PATH );
				folderRunRender = new File( basePath + Constants.RUNRENDER_PATH );
			}
			
			// parse config. profile
			Configuration.parseFile( basePath, testCase );

			// init. stats file name
			statsFile = basePath + Constants.STATS
					+ timeStamp.replaceAll( "/", "-" ); //$NON-NLS-1$ //$NON-NLS-2$

			// remove output folders
			removeFolder( folderRun );
			removeFolder( folderRender );
			removeFolder( folderRunRender );

			// create output folders
			createFolder( folderRun );
			createFolder( folderRender );
			createFolder( folderRunRender );

			// create an engine instance
			config = new EngineConfig( );
			Platform.startup( config );
			factory = new ReportEngineFactory( );
			engine = factory.createReportEngine( config );
			engine.changeLogLevel( Level.SEVERE );

			// create a semaphore instance
			semaphore = new Semaphore( 0 );

			// create I/O writers
			fileWriter = new FileWriter( statsFile, false );
			printWriter = new PrintWriter( fileWriter, true );
			printWriter.println( "\n" ); //$NON-NLS-1$
			printWriter.println( "TCount: " + Configuration.tCount ); //$NON-NLS-1$
			printWriter.println( "RCount: " + Configuration.rCount ); //$NON-NLS-1$
			printWriter.println( "Format: " + Configuration.format ); //$NON-NLS-1$
			printWriter.println( "Locale: " + Configuration.locale ); //$NON-NLS-1$
			printWriter.println( "Encoding: " + Configuration.encoding ); //$NON-NLS-1$
			printWriter.println( "\n" ); //$NON-NLS-1$
		}
		catch ( Exception e )
		{
			e.printStackTrace( );
			System.exit( -1 );
		}
	}

	/**
	 * run warmup test
	 */
	public void runWarmUp( )
	{

		exeRun( 1, Configuration.rCount, null );
		exeRender( 1, Configuration.rCount, null, "all" ); //$NON-NLS-1$
		exeRunRender( 1, Configuration.rCount, null );
	}

	/**
	 * run actual test
	 */
	public void runTasks( )
	{

		exeRun( Configuration.tCount, Configuration.rCount, new TreeMap( ) );
		calStats( "Task: Run", RunThread.stats ); //$NON-NLS-1$

		exeRender( Configuration.tCount, Configuration.rCount, new TreeMap( ),
				"all" ); //$NON-NLS-1$
		calStats( "Task: Render (page-All)", RenderThread.stats ); //$NON-NLS-1$

		exeRender( Configuration.tCount, Configuration.rCount, new TreeMap( ),
				null );
		calStats( "Task: Render (page-N)", RenderThread.stats ); //$NON-NLS-1$

		exeRunRender( Configuration.tCount, Configuration.rCount, new TreeMap( ) );
		calStats( "Task: RunRender", RunRenderThread.stats ); //$NON-NLS-1$

		Configuration.resetConfig( );
		
		try
		{
			printWriter.close( );
			fileWriter.close( );
		}
		catch ( Exception e )
		{
			e.printStackTrace( );
			System.exit( -1 );
		}
	}

	// create output folder
	private void createFolder( File folder )
	{

		folder.mkdirs( );
	}

	// remove output folder
	private void removeFolder( File folder )
	{

		if ( folder.exists( ) )
		{
			File[] files = folder.listFiles( );
			for ( int x = 0; x < files.length; x++ )
			{
				if ( files[x].isDirectory( ) )
				{
					File[] images = files[x].listFiles( );
					for ( int y = 0; y < images.length; y++ )
					{
						images[y].delete( );
					}
				}
				files[x].delete( );
			}
			folder.delete( );
		}
	}

	// execute Run Task
	private void exeRun( int tCount, int rCount, Map stats )
	{

		RunThread.rCount = rCount;
		RunThread.input = Configuration.input;
		RunThread.output = Configuration.output;
		RunThread.engine = engine;
		RunThread.stats = stats;

		for ( int i = 0; i < tCount; i++ )
		{
			// acquire semaphore
			semaphore.acquire( );
			runThread = new RunThread( new Integer( i + 1 ).toString( ),
					semaphore, basePath );
			runThread.start( );
		}

		// pause semaphore
		semaphore.holdup( );

		System.out.println( "Run Task Completed" ); //$NON-NLS-1$

		// reset semaphore
		semaphore.reset( );
	}

	// execute Render task
	private void exeRender( int tCount, int rCount, Map stats, String renderAll )
	{

		RenderThread.rCount = rCount;
		RenderThread.format = Configuration.format;
		RenderThread.locale = Configuration.locale;
		RenderThread.encoding = Configuration.encoding;
		RenderThread.output = Configuration.output;
		RenderThread.engine = engine;
		RenderThread.renderAll = renderAll;
		RenderThread.stats = stats;

		for ( int i = 0; i < tCount; i++ )
		{
			// acquire semaphore
			semaphore.acquire( );
			renderThread = new RenderThread( new Integer( i + 1 ).toString( ),
					semaphore, basePath );
			renderThread.start( );
		}

		// pause semaphore
		semaphore.holdup( );

		System.out.println( "Render Task Completed" ); //$NON-NLS-1$

		// reset semaphore
		semaphore.reset( );
	}

	// execute RunRender task
	private void exeRunRender( int tCount, int rCount, Map stats )
	{

		RunRenderThread.rCount = rCount;
		RunRenderThread.format = Configuration.format;
		RunRenderThread.locale = Configuration.locale;
		RunRenderThread.encoding = Configuration.encoding;
		RunRenderThread.input = Configuration.input;
		RunRenderThread.output = Configuration.output;
		RunRenderThread.engine = engine;
		RunRenderThread.stats = stats;

		for ( int i = 0; i < tCount; i++ )
		{
			// acquire semaphore
			semaphore.acquire( );
			runRenderThread = new RunRenderThread( new Integer( i + 1 )
					.toString( ), semaphore, basePath );
			runRenderThread.start( );
		}

		// pause semaphore
		semaphore.holdup( );

		System.out.println( "RunRender Task Completed" ); //$NON-NLS-1$

		// reset semaphore
		semaphore.reset( );
	}

	// calculate avg. execution time
	private void calStats( String task, Map stats )
	{

		// print task header
		printWriter.println( task );

		printWriter.println( "\n" ); //$NON-NLS-1$

		// print detail stats
		printWriter.println( stats );

		// print avg. stats
		long time = 0;
		long total = 0;
		Collection collection = stats.values( );
		Object[] objects = collection.toArray( );
		printWriter.println( "\n" ); //$NON-NLS-1$
		for ( int x = 0; x < objects.length; x++ )
		{
			time = ( (Long) objects[x] ).longValue( );
			total += time;
			if ( ( x + 1 ) % Configuration.tCount == 0 )
			{
				int y = ( x + 1 ) / Configuration.tCount - 1;
				printWriter.println( Configuration.output.get( y ) + " avg.: " + //$NON-NLS-1$
						total / Configuration.tCount + " ms" ); //$NON-NLS-1$
				time = 0;
				total = 0;
			}
		}
		printWriter.println( "\n" ); //$NON-NLS-1$
	}
}