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

import java.io.IOException;
import java.util.Date;
import java.util.List;

import org.eclipse.birt.core.format.DateFormatter;
import org.eclipse.birt.report.engine.EngineCase;
import org.eclipse.birt.report.engine.toc.TOCTree;
import org.eclipse.birt.report.engine.toc.TOCTreeNode;

import com.ibm.icu.util.ULocale;

public class TOCNodeTest extends EngineCase
{
	static final String REPORT_DESIGN_RESOURCE = "/org/eclipse/birt/report/engine/api/TOCNodeTest.xml";
	static final String REPORT_DESIGN_RESOURCE1 = "/org/eclipse/birt/report/engine/api/TOCNodeTest1.xml";
	static final String REPORT_DESIGN_RESOURCE2 = "/org/eclipse/birt/report/engine/api/TOCNodeTest2.xml";
	static final String REPORT_DESIGN_RESOURCE3 = "/org/eclipse/birt/report/engine/api/TOCNodeTest3.xml";
	static final String REPORT_DESIGN_RESOURCE4 = "/org/eclipse/birt/report/engine/api/TOCNodeTest4.xml";
	static final String REPORT_DESIGN_RESOURCE5 = "/org/eclipse/birt/report/engine/api/TOCNodeTest5.xml";

	public void setUp( ) throws IOException
	{
		removeFile( REPORT_DOCUMENT );
		removeFile( REPORT_DESIGN );
		copyResource( REPORT_DESIGN_RESOURCE, REPORT_DESIGN );
		// create the report engine using default config
		engine = createReportEngine( );
	}

	public void tearDown( )
	{
		// shut down the engine.
		engine.shutdown( );
		removeFile( REPORT_DESIGN );
		removeFile( REPORT_DOCUMENT );
	}

	public void testRender( ) throws EngineException, IOException
	{
		createReportDocument( );
		IReportDocument reportDoc = engine.openReportDocument( REPORT_DOCUMENT );

		//node1 is set to hide under "html" format.
		TOCTreeNode root = ((TOCTree) reportDoc.getTOCTree( null, null ) ).getTOCRoot( );
		
		TOCTreeNode node1 = findNode( root, "hiddenLabel1" );
		assertEquals( "viewer", node1.getHiddenFormats( ) );
		assertTrue( node1.isHidden( "viewer" ));
	
		// Test if a container is hidden, the items in the container will also
		// be hidden.
		// node2 and node3 is in a table which is set to hide under "html"
		// format.
		// There is no visibility rule is set on node2. node3 is set to hide
		// under "pdf" format.
		TOCTreeNode node2 = findNode( root, "hiddenLabel2" );
		assertEquals( "viewer", node2.getHiddenFormats( ) );
		assertTrue( node2.isHidden( "viewer" ));
		TOCTreeNode node3 = findNode( root, "hiddenLabel3" );
		assertTrue( node3.isHidden( "viewer" ));
		assertTrue( node3.isHidden( "pdf" ));

		// Label "labelInHiddenGroup" is a lable in a table group which is set
		// to hide under "html" format.
		TOCTreeNode labelInHiddenGroup = findNode( root, "labelInHiddenGroup" );
		assertEquals( "viewer", labelInHiddenGroup.getHiddenFormats( ) );
		assertTrue( labelInHiddenGroup.isHidden( "viewer" ));
	}

	private TOCTreeNode findNode( TOCTreeNode root, String displayString )
	{
		if ( root == null )
		{
			return null;
		}
		String string = (String) root.getTOCValue( );
		if ( displayString == null )
		{
			if ( string == null )
			{
				return root;
			}
		}
		if ( displayString.equals( string ) )
		{
			return root;
		}
		List children = root.getChildren( );
		for ( int i = 0; i < children.size( ); i++ )
		{
			TOCTreeNode child = findNode( ( TOCTreeNode )children.get( i ), displayString );
			if ( child != null )
			{
				return child;
			}
		}
		return null;
	}

	/**
	 * root
	 *   leaf1
	 *     leaf11(hidden in html)
	 *   leaf2
	 *     leaf21(hidden in html)
	 * @throws EngineException 
	 *
	 */
	public void testExtractNode1( ) throws EngineException
	{
		TOCNode extractedTree = createTOCNode( null );
			createTOCNode( extractedTree, "__TOC_0", "leaf1" );
			createTOCNode( extractedTree, "__TOC_1", "leaf2" );
		
		TOCNode wholeTree = createTOCNode( null );
			TOCNode leaf1 = createTOCNode( wholeTree, "__TOC_0", "leaf1" );
				createTOCNode( leaf1, "__TOC_0_0", "leaf11", "viewer" );
			TOCNode leaf2 = createTOCNode( wholeTree, "__TOC_1", "leaf2" );
				createTOCNode( leaf2, "__TOC_1_0", "leaf21", "viewer" );
		testTOC( extractedTree, wholeTree, REPORT_DESIGN_RESOURCE1 );
	}

	/**
	 * root
	 *   group 
	 *     leaf1(hidden in html)
	 *     leaf2
	 *     leaf3
	 *
	 * result:
	 * 
	 * root
	 *   group leaf2
	 *     leaf3
	 * @throws EngineException 
	 *
	 */
	public void testExtractNode2( ) throws EngineException
	{
		TOCNode extractedTree = createTOCNode( null );
			TOCNode leaf = createTOCNode( extractedTree, "__TOC_0_1", "leaf2" );
			createTOCNode( leaf, "__TOC_0_2", "leaf3" );
		
		TOCNode wholeTree = createTOCNode( null );
			TOCNode leaf1 = createTOCNode( wholeTree, "__TOC_0_0", "leaf1", "viewer" );
				createTOCNode( leaf1, "__TOC_0_1", "leaf2" );
				createTOCNode( leaf1, "__TOC_0_2", "leaf3" );

		testTOC( extractedTree, wholeTree, REPORT_DESIGN_RESOURCE2 );
	}

	/**
	 * root
	 *   group
	 *   	table table(hidden in html)
	 *     		leaf11
	 *     		leaf12
	 *
	 * result:
	 * 
	 * root
	 * @throws EngineException 
	 *
	 */
	public void testExtractNode3( ) throws EngineException
	{
		TOCNode expected = createTOCNode( null );

		testTOC( expected, REPORT_DESIGN_RESOURCE3 );
	}

	/**
	 * root
	 *   group
	 *   	list(hidden in html)
	 *          leaf1
	 *          leaf2
	 *     	leaf3
	 *     	leaf4
	 *
	 * result:
	 * 
	 * root
	 * @throws EngineException 
	 *
	 */
	public void testExtractNode4( ) throws EngineException
	{
		TOCTreeNode extratedTree = createTOCNode( null );
			TOCTreeNode leaf = createTOCNode( extratedTree, "__TOC_0_2", "leaf3" );
				createTOCNode( leaf, "__TOC_0_3", "leaf4" );

		TOCTreeNode wholeTree = createTOCNode( null );
			TOCTreeNode list = createTOCNode( wholeTree, "__TOC_0_0", "leaf1", "viewer" );
				createTOCNode( list, "__TOC_0_1", "leaf2", "viewer" );
				createTOCNode( list, "__TOC_0_2", "leaf3" );
				createTOCNode( list, "__TOC_0_3", "leaf4" );

		testTOC( extratedTree, wholeTree, REPORT_DESIGN_RESOURCE4 );
	}

	public void testFindByNumber( )
	{
		TOCTree tocTree = new TOCTree( );
		TOCTreeNode root = tocTree.getTOCRoot( );
		createTOCNode( root, "123" );
		createTOCNode( root, new Integer( 123 ) );
		createTOCNode( root, new Long( 123 ) );
		createTOCNode( root, new Float( 123.0 ) );
		createTOCNode( root, new Double( 123.00 ) );
		assertEquals( 5, find( tocTree, "123" ).size( ) );
		assertEquals( 5, find( tocTree, new Integer( 123 ) ).size( ) );
		assertEquals( 5, find( tocTree, new Long( 123 ) ).size( ) );
		assertEquals( 4, find( tocTree, new Float( 123 ) ).size( ) );
		assertEquals( 4, find( tocTree, new Float( 123.00 ) ).size( ) );
		assertEquals( 4, find( tocTree, new Double( 123.000 ) ).size( ) );

	}

	public void testFindByDate( )
	{
		Date date = new Date( 1155761100000l );
		TOCTree tocTree = new TOCTree( );
		TOCTreeNode root = tocTree.getTOCRoot( );
		DateFormatter formatter = new DateFormatter( ULocale.getDefault( ) );
		String string = formatter.format( date );
		createTOCNode( root, string );
		createTOCNode( root, date );
		assertEquals( 2, find( tocTree, string ).size( ) );
		assertEquals( 1, find( tocTree, date ).size( ) );
	}

	public void testFindByOtherType( )
	{
		TOCTree tocTree = new TOCTree( );
		TOCTreeNode root = tocTree.getTOCRoot( );
		String stringKey = "key";
		TestType objectKey = new TestType( stringKey );
		createTOCNode( root, stringKey );
		createTOCNode( root, objectKey );
		assertEquals( 2, find( tocTree, objectKey ).size( ) );
		assertEquals( 2, find( tocTree, stringKey ).size( ) );
	}
	
	public void testHiddenFormats( )
	{
		TOCTree tocTree = new TOCTree( );
		TOCTreeNode root = tocTree.getTOCRoot( );
		createTOCNode( root, "__TOC_0", "viewer", "viewer" );
		createTOCNode( root, "__TOC_1", "pdf", "pdf" );
		createTOCNode( root, "__TOC_2", "all", "all" );
		checkFormat( tocTree, "viewer", 1);
		checkFormat( tocTree, "pdf", 1);
		checkFormat( tocTree, "all", 2);
	}

	private void checkFormat( TOCTree tocTree, String format, int size )
	{
		TOCTree viewerTree = new TOCTree( tocTree.getTOCRoot( ), format, null );
		List viewerNodes = viewerTree.getRoot( ).getChildren( );
		assertEquals( size, viewerNodes.size( ) );
	}
	
	private List find( TOCTree tocTree, Object value)
	{
		return tocTree.findTOCByValue( value );
	}
	
	protected TOCTreeNode createTOCNode( TOCNode parent )
	{
		return createTOCNode( parent, null, null );
	}

	protected TOCTreeNode createTOCNode( TOCNode parent, Object value)
	{
		return createTOCNode( parent, null, value );
	}

	protected TOCTreeNode createTOCNode( TOCNode parent, String id,
			Object value )
	{
		return createTOCNode( parent, id, id, value, null );
	}

	protected TOCTreeNode createTOCNode( TOCNode parent, String id,
			Object value, String formats )
	{
		return createTOCNode( parent, id, id, value,
				formats );
	}

	protected TOCTreeNode createTOCNode( TOCNode parent, String id,
			String bookmark, Object value, String formats )
	{
		TOCTreeNode node = new TOCTreeNode();
		node.setNodeID(id);
		node.setBookmark(bookmark);
		node.setParent(parent);
		node.setHideFormats( formats );
		node.setTOCValue( value );
		if ( value instanceof String )
		node.setDisplayString( ( String ) value );
		if (parent != null)
		{
			parent.getChildren().add(node);
		}
		return node;
	}

	private void assertEquals( TOCNode expected, TOCNode actual )
	{
		if ( checkNull( expected, actual ) )
		{
			return;
		}
		assertEqualsWithoutChildren( expected, actual );
		for ( int i = 0; i < expected.getChildren( ).size( ); i++ )
		{
			TOCNode node1 = (TOCNode) expected.getChildren( ).get( i );
			TOCNode node2 = (TOCNode) actual.getChildren( ).get( i );
			assertEquals( node1, node2 );
		}
	}

	private void assertEqualsWithoutChildren( TOCNode expected, TOCNode actual )
	{
		if ( checkNull( expected, actual ) )
		{
			return;
		}
		assertEquals( expected.getNodeID( ), actual.getNodeID( ) );
		assertEquals( expected.getDisplayString( ), actual.getDisplayString( ) );
		assertEquals( expected.getBookmark( ), actual.getBookmark( ) );
		assertEquals( expected.getChildren( ).size( ), actual.getChildren( ).size( ) );
	}

	private boolean checkNull( TOCNode expected, TOCNode actual )
	{
		if ( expected == null && actual == null )
		{
			return true;
		}
		if ( expected == null || actual == null )
		{
			fail( );
		}
		return false;
	}

	private void testTOC( TOCNode extractedTree, String designFile )
			throws EngineException
	{
		testTOC( extractedTree, extractedTree, designFile);
	}

	private void testTOC( TOCNode extractedTree, TOCNode wholeTree,
			String designFile ) throws EngineException
	{
		IReportDocument document = createReportDocument( designFile );
		ITOCTree tree = document.getTOCTree( "viewer", null );
		assertEquals( extractedTree, tree.findTOC( "/" ) );
		assertEquals( extractedTree, tree.getRoot( ) );
		testFindTOC( extractedTree, document );
	}

	private void testFindTOC( TOCNode wholeTree, IReportDocument document )
	{
		ITOCTree tocTree = document.getTOCTree( "viewer", null );
		assertEquals( wholeTree , tocTree
				.findTOC( wholeTree.getNodeID( ) ) );
		testFindByValue( wholeTree, tocTree );
		List children = wholeTree.getChildren( );
		for ( int i = 0; i < children.size( ); i++ )
		{
			testFindTOC( (TOCNode)children.get( i ), document);
		}
	}
	
	private void testFindByValue( TOCNode root, ITOCTree tocTree )
	{
		if ( root.getNodeID( ) == null )
		{
			return;
		}
		List list = (List) tocTree.findTOCByValue( root.getDisplayString( ) );
		assertEquals( 1, list.size( ) );
		assertEquals( root, (TOCNode)list.get( 0 ) );
	}
}

class TestType
{
	private String content;
	public TestType( String content )
	{
		this.content = content;
	}
	public String toString()
	{
		return content;
	}
}