/*******************************************************************************
 * 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.report.tests.viewer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.cactus.ServletTestCase;

import com.meterware.httpunit.WebResponse;

/**
 * Base test case for viewer cases.
 */

public class BaseViewerTestCase extends ServletTestCase
{

	protected static String SERVER = null;
	protected static String RUNTIME = null;
	protected static String CATALINAHOME = null;

	protected static final String TEST_FOLDER = "src/"; //$NON-NLS-1$
	protected static final String OUTPUT_FOLDER = "/output/"; //$NON-NLS-1$
	protected static final String INPUT_FOLDER = "/input/"; //$NON-NLS-1$

	/**
	 * Servlet path for running model.
	 */
	public static final String SERVLET_PATH_RUN = "/run"; //$NON-NLS-1$

	/**
	 * Servlet path for frameset model.
	 */
	public static final String SERVLET_PATH_FRAMESET = "/frameset"; //$NON-NLS-1$

	/**
	 * running viewer model.
	 */
	public static final String VIEWER_RUN = "run"; //$NON-NLS-1$

	/**
	 * request GET method.
	 */
	public static final String REQUEST_GET = "get"; //$NON-NLS-1$

	/**
	 * request POST method.
	 */
	public static final String REQUEST_POST = "post"; //$NON-NLS-1$

	/**
	 * URL parameter name that gives the report design name.
	 */
	public static final String PARAM_REPORT = "__report"; //$NON-NLS-1$

	/**
	 * URL parameter name that gives the format to display the report
	 */
	public static final String PARAM_FORMAT = "__format"; //$NON-NLS-1$

	/**
	 * URL parameter name that determines whether to overwrite report document
	 */
	public static final String PARAM_OVERWRITE = "__overwrite";

	/**
	 * URL parameter name that gives the locale to display the report
	 */
	public static final String PARAM_LOCALE = "__locale"; //$NON-NLS-1$

	/**
	 * URL parameter name that determines whether to show master page
	 */
	public static final String PARAM_MASTERPAGE = "__masterpage";

	/**
	 * URL parameter name that gives the report document name.
	 */
	public static final String PARAM_REPORT_DOCUMENT = "__document"; //$NON-NLS-1$

	/**
	 * Mime type
	 */
	public static final String MIME_TYPE = "text/xml"; //$NON-NLS-1$

	/**
	 * Default viewer image directory. Default to ${birt home}/report/images
	 */
	protected static final String IMAGE_DIR = "report/images"; //$NON-NLS-1$

	private String reportMatcher = null;
	private String formatMatcher = null;
	private String localeMatcher = null;
	private String masterpageMatcher = null;
	private String overwriteMatcher = null;
	private String documentMatcher = null;
	private List parametersMatcher = new ArrayList( );

	private boolean parsed = false;

	static
	{
		// static initialization, load the default configuration.

		init( );
	}

	private static void init( )
	{
		Properties config = new Properties( );
		try
		{
			config.load( BaseViewerTestCase.class.getResourceAsStream( "config.properties" ) ); //$NON-NLS-1$
		}
		catch ( IOException e )
		{
			e.printStackTrace( );
		}

		final String DEFAULT_SERVER = config.getProperty( "server" ); //$NON-NLS-1$
		final String DEFAULT_RUNTIME = config.getProperty( "runtime" ); //$NON-NLS-1$
		final String DEFAULT_CATALINAHOME = config.getProperty( "catalina.home" ); //$NON-NLS-1$

		SERVER = System.getProperty( "server" ); //$NON-NLS-1$
		if ( SERVER == null )
		{
			System.out.println( "Using default server" ); //$NON-NLS-1$
			System.setProperty( "server", DEFAULT_SERVER );//$NON-NLS-1$
			SERVER = DEFAULT_SERVER;
		}

		RUNTIME = System.getProperty( "runtime" );//$NON-NLS-1$
		if ( RUNTIME == null )
		{
			System.out.println( "Using default runtime" );//$NON-NLS-1$
			System.setProperty( "runtime", DEFAULT_RUNTIME );//$NON-NLS-1$
			RUNTIME = DEFAULT_RUNTIME;
		}

		CATALINAHOME = System.getProperty( "catalina.home" ); //$NON-NLS-1$
		if ( CATALINAHOME == null )
		{
			System.out.println( "Using default deployDir" ); //$NON-NLS-1$
			System.setProperty( "catalina.home", DEFAULT_CATALINAHOME ); //$NON-NLS-1$
			CATALINAHOME = DEFAULT_CATALINAHOME;
		}

		if ( System.getProperty( "cactus.contextURL" ) == null )//$NON-NLS-1$
			System.setProperty( "cactus.contextURL", "http://" + SERVER + "/"//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
					+ RUNTIME );

		if ( System.getProperty( "cactus.servletRedirectorURL" ) == null ) //$NON-NLS-1$
			System.setProperty( "cactus.servletRedirectorURL", "http://" //$NON-NLS-1$//$NON-NLS-2$
					+ SERVER + "/" + RUNTIME + "/ServletRedirector" ); //$NON-NLS-1$//$NON-NLS-2$
	}

	/**
	 * Return the folder of the web component in the app server.
	 */
	protected String getRuntimeFolder( )
	{
		return CATALINAHOME + "/webapps/" + RUNTIME + "/"; //$NON-NLS-1$//$NON-NLS-2$
	}

	/**
	 * Locates the folder where the unit test java source file is saved.
	 * 
	 * @return the path name where the test java source file locates.
	 */
	protected String getClassFolder( )
	{
		String pathBase = null;

		ProtectionDomain domain = this.getClass( ).getProtectionDomain( );
		if ( domain != null )
		{
			CodeSource source = domain.getCodeSource( );
			if ( source != null )
			{
				URL url = source.getLocation( );
				pathBase = url.getPath( );

				if ( pathBase.endsWith( "bin/" ) ) //$NON-NLS-1$
					pathBase = pathBase.substring( 0, pathBase.length( ) - 4 );
				if ( pathBase.endsWith( "bin" ) ) //$NON-NLS-1$
					pathBase = pathBase.substring( 0, pathBase.length( ) - 3 );
			}
		}

		pathBase = pathBase + TEST_FOLDER;
		String className = this.getClass( ).getName( );
		int lastDotIndex = className.lastIndexOf( "." ); //$NON-NLS-1$
		className = className.substring( 0, lastDotIndex );
		className = pathBase + className.replace( '.', '/' );

		return className;
	}

	/**
	 * Make a copy of a given file to the target file.
	 * 
	 * @param from
	 *            the file where to copy from
	 * @param to
	 *            the target file to copy to.
	 * @throws IOException
	 */
	protected final void copyFile( String from, String to ) throws IOException
	{

		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;

		try
		{
			new File( to ).createNewFile( );

			bis = new BufferedInputStream( new FileInputStream( from ) );
			bos = new BufferedOutputStream( new FileOutputStream( to ) );

			int nextByte = 0;
			while ( ( nextByte = bis.read( ) ) != -1 )
			{
				bos.write( nextByte );
			}
		}
		catch ( IOException e )
		{
			throw e;
		}
		finally
		{
			try
			{
				if ( bis != null )
					bis.close( );

				if ( bos != null )
					bos.close( );
			}
			catch ( IOException e )
			{
				// ignore
			}

		}
	}

	protected void setUp( ) throws Exception
	{
		super.setUp( );
		Locale.setDefault( Locale.ENGLISH );
	}

	protected void tearDown( ) throws Exception
	{
		super.tearDown( );
	}

	/**
	 * getHyperlink, utility methods for getting hyperlink from webResponse
	 * 
	 * @param webResponse -
	 *            wrapped webResponse
	 * @param identifier -
	 *            hyperlink identifier
	 * @return encoded hyperlink
	 * @throws Exception
	 */
	protected String getHyperlink( WebResponse webResponse, String identifier )
			throws Exception
	{
		String delimiter = "&";
		// for html webResponse
		if ( webResponse.isHTML( ) )
			return webResponse.getLinkWith( identifier ).getURLString( )
					+ delimiter;
		// for soap webResponse
		String content = webResponse.getText( );
		String prefix = "frameset?";
		String suffix = "; target=";
		int sIndex = content.indexOf( prefix );
		int eIndex = content.indexOf( suffix );
		return content.substring( sIndex + prefix.length( ), eIndex )
				+ delimiter;
	}

	protected String getReportParam( String hyperlink )
	{
		parseHyperlink( hyperlink );
		return getParamValue( reportMatcher );
	}

	protected String getFormatParam( String hyperlink )
	{
		parseHyperlink( hyperlink );
		return getParamValue( formatMatcher );
	}

	protected String getOverwriteParam( String hyperlink )
	{
		parseHyperlink( hyperlink );
		return getParamValue( overwriteMatcher );
	}

	protected String getLocaleParam( String hyperlink )
	{
		parseHyperlink( hyperlink );
		return getParamValue( localeMatcher );
	}

	protected String getMasterpageParam( String hyperlink )
	{
		parseHyperlink( hyperlink );
		return getParamValue( masterpageMatcher );
	}

	protected String getDocumentParam( String hyperlink )
	{
		parseHyperlink( hyperlink );
		return getParamValue( documentMatcher );
	}

	protected String[] getParameters( String hyperlink )
	{
		parseHyperlink( hyperlink );
		int size = parametersMatcher.size( );
		String[] parameters = new String[size];
		// this is used to remove delimiter "&"
		for ( int i = 0; i < size; i++ )
		{
			String matcher = ( (String) parametersMatcher.get( i ) );
			parameters[i] = matcher.substring( 0, matcher.length( ) - 1 );
		}
		return parameters;
	}

	private void parseHyperlink( String hyperlink )
	{
		if ( parsed )
			return;

		assert hyperlink != null;

		assert !hyperlink.equals( "" );

		String expression = "__\\w+=[^&]+&|\\w+=[^&]+&";
		Pattern pattern = Pattern.compile( expression );
		Matcher matcher = pattern.matcher( hyperlink );
		String textBlock = null;
		while ( matcher.find( ) )
		{
			textBlock = matcher.group( );
			System.out.println( textBlock );
			if ( textBlock.startsWith( PARAM_REPORT ) )
			{
				reportMatcher = textBlock;
				continue;
			}
			else if ( textBlock.startsWith( PARAM_FORMAT ) )
			{
				formatMatcher = textBlock;
				continue;
			}
			else if ( textBlock.startsWith( PARAM_OVERWRITE ) )
			{
				overwriteMatcher = textBlock;
				continue;
			}
			else if ( textBlock.startsWith( PARAM_LOCALE ) )
			{
				localeMatcher = textBlock;
				continue;
			}
			else if ( textBlock.startsWith( PARAM_MASTERPAGE ) )
			{
				masterpageMatcher = textBlock;
				continue;
			}
			else if ( textBlock.startsWith( PARAM_REPORT_DOCUMENT ) )
			{
				documentMatcher = textBlock;
				continue;
			}
			else
			{
				parametersMatcher.add( textBlock );
				continue;
			}
		}
		parsed = true;
	}

	private String getParamValue( String matcher )
	{
		String decodedMatcher = URLDecoder.decode( matcher );
		int sIndex = decodedMatcher.indexOf( "=" );
		int eIndex = decodedMatcher.indexOf( "&" );
		int offset = 1; // =
		if ( decodedMatcher.startsWith( PARAM_REPORT )
				|| decodedMatcher.startsWith( PARAM_REPORT_DOCUMENT ) )
			offset = 2; // =/
		return decodedMatcher.substring( sIndex + offset, eIndex );
	}

	/**
	 * getPageSoapMessage, get the inputstream of getPage soap message
	 * 
	 * @param pageN -
	 *            page number
	 * @return inputstream
	 */
	protected InputStream getPageSoapMessage( String pageN )
	{
		StringBuffer sb = new StringBuffer( );
		sb.append( "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\"><Body><GetUpdatedObjects xmlns=\"http://schemas.eclipse.org/birt\"><Operation><Target><Id>Document</Id><Type>Document</Type></Target><Operator>GetPage</Operator><Oprand><Name>page</Name><Value>" );
		sb.append( pageN );
		sb.append( "</Value></Oprand><Oprand><Name>svg</Name><Value>false</Value></Oprand></Operation></GetUpdatedObjects></Body></Envelope>" );

		return new ByteArrayInputStream( sb.toString( ).getBytes( ) );
	}

	/**
	 * getTOCSoapMessage, get the inputstream of getTOC soap message
	 * 
	 * @param toc -
	 *            toc value
	 * @return inputstream
	 */
	protected InputStream getTOCSoapMessage( String toc )
	{
		StringBuffer sb = new StringBuffer( );
		sb.append( "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\"><Body><GetUpdatedObjects xmlns=\"http://schemas.eclipse.org/birt\"><Operation><Target><Id>Document</Id><Type>Document</Type></Target><Operator>GetPage</Operator><Oprand><Name>bookmark</Name><Value>" );
		sb.append( toc );
		sb.append( "</Value></Oprand><Oprand><Name>svg</Name><Value>false</Value></Oprand></Operation></GetUpdatedObjects></Body></Envelope>" );
		return new ByteArrayInputStream( sb.toString( ).getBytes( ) );
	}

	/**
	 * getParamSoapMessage, get the inputstream of getParam soap message
	 * 
	 * @param name[] -
	 *            parameter's name(s)
	 * @param value[] -
	 *            parameter's value(s)
	 * @return inputstream
	 */
	protected InputStream getParamSoapMessage( String name[], String value[] )
	{
		if ( name.length != value.length )
			throw new RuntimeException( "parameter's name and value should match" );

		StringBuffer sb = new StringBuffer( );
		sb.append( "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\"><Body><GetUpdatedObjects xmlns=\"http://schemas.eclipse.org/birt\"><Operation><Target><Id>Document</Id><Type>Document</Type></Target><Operator>ChangeParameter</Operator>" );
		for ( int i = 0; i < name.length; i++ )
			sb.append( "<Oprand><Name>"
					+ name[i] + "</Name><Value>" + value[i]
					+ "</Value></Oprand>" );
		sb.append( "<Oprand><Name>svg</Name><Value>false</Value></Oprand></Operation></GetUpdatedObjects></Body></Envelope>" );

		return new ByteArrayInputStream( sb.toString( ).getBytes( ) );
	}

}