/*******************************************************************************
 * Copyright (c) 2004, 2005 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.layout.html;

import java.util.Stack;

import org.eclipse.birt.report.engine.content.IBandContent;
import org.eclipse.birt.report.engine.content.ICellContent;
import org.eclipse.birt.report.engine.content.IRowContent;
import org.eclipse.birt.report.engine.content.ITableBandContent;
import org.eclipse.birt.report.engine.content.ITableContent;
import org.eclipse.birt.report.engine.content.ITableGroupContent;
import org.eclipse.birt.report.engine.emitter.BufferedReportEmitter;
import org.eclipse.birt.report.engine.emitter.ContentEmitterAdapter;
import org.eclipse.birt.report.engine.emitter.IContentEmitter;
import org.eclipse.birt.report.engine.executor.buffermgr.TableContentLayout;
import org.eclipse.birt.report.engine.ir.CellDesign;
import org.eclipse.birt.report.engine.ir.EngineIRConstants;

/**
 * 
 */

public class HTMLTableLayoutNestEmitter extends HTMLTableLayoutEmitter
{

	public HTMLTableLayoutNestEmitter( IContentEmitter emitter )
	{
		super( new TableNormalizeEmitter( emitter ) );
	}

	public void startTable( ITableContent table )
	{
		if ( cellEmitter != null )
		{
			cellEmitter.startTable( table );
		}
		else
		{
			emitter.startTable( table );
		}
		pushTableLayout( table );
	}

	public void endTable( ITableContent table )
	{
		popTableLayout( table );
		if ( cellEmitter != null )
		{
			cellEmitter.endTable( table );
		}
		else
		{
			emitter.endTable( table );
		}
	}

	public void startTableGroup( ITableGroupContent group )
	{
		int groupLevel = group.getGroupLevel( );
		groupStack.push( new Integer( groupLevel ) );
		if ( hasDropCell( ) )
		{
			layoutEvents
					.push( new LayoutEvent( LayoutEvent.START_GROUP, group ) );
		}
		else
		{
			emitter.startTableGroup( group );
		}
	}

	public void endTableGroup( ITableGroupContent group )
	{
		// if there is no group footer, we still need to do with the drop.
		int groupLevel = getGroupLevel( );
		resolveCellsOfDrop( groupLevel, false );
		resolveCellsOfDrop( groupLevel, true );
		assert !groupStack.isEmpty( );
		groupStack.pop( );
		// ContentEmitterUtil.endContent( group, emitter );
		if ( hasDropCell( ) )
		{
			layoutEvents.push( new LayoutEvent( LayoutEvent.END_GROUP, group ) );
		}
		else
		{
			emitter.endTableGroup( group );
		}
	}

	public void startTableBand( ITableBandContent band )
	{
		if ( band.getBandType( ) == IBandContent.BAND_GROUP_FOOTER )
		{
			int groupLevel = getGroupLevel( );
			resolveCellsOfDrop( groupLevel, false );
		}
		if ( hasDropCell( ) )
		{
			layoutEvents.push( new LayoutEvent( LayoutEvent.START_BAND, band ) );
		}
		else
		{
			flush( );
			emitter.startTableBand( band );
		}
	}

	public void endTableBand( ITableBandContent band )
	{
		if ( band.getBandType( ) == IBandContent.BAND_GROUP_FOOTER )
		{
			int groupLevel = getGroupLevel( );
			resolveCellsOfDrop( groupLevel, true );
		}
		if ( hasDropCell( ) )
		{
			layoutEvents.push( new LayoutEvent( LayoutEvent.END_BAND, band ) );
		}
		else
		{
			flush( );
			emitter.endTableBand( band );
		}
	}

	public void startRow( IRowContent row )
	{
		if ( !layout.isRowHidden( row ) )
		{
			layout.createRow( row );
			row.setRowID( layout.getCurrentRowID( ) );
			if ( hasDropCell( ) )
			{
				layoutEvents.push( new LayoutEvent( LayoutEvent.ON_ROW,
						new Integer( row.getRowID( ) ) ) );
			}
			else
			{
				emitter.startRow( row );
			}
		}
	}

	public void endRow( IRowContent row )
	{
		if ( layout.isRowHidden( row ) )
		{
			return;
		}
		if ( !hasDropCell( ) )
		{
			flush( );
			emitter.endRow( row );
		}
	}

	public void startCell( ICellContent cell )
	{
		if ( !layout.isVisible( cell ) )
		{
			cellEmitter = new ContentEmitterAdapter( );
			return;
		}
		int colId = cell.getColumn( );
		int colSpan = cell.getColSpan( );
		int rowSpan = cell.getRowSpan( );
		String dropType = null;
		BufferedReportEmitter buffer = null;

		// the current executed cell is rowIndex, columnIndex
		// get the span value of that cell.
		CellDesign cellDesign = (CellDesign) cell.getGenerateBy( );
		if ( cellDesign != null )
		{
			dropType = cellDesign.getDrop( );
			if ( dropType != null && !"none".equals( dropType ) )
			{
				rowSpan = createDropID( getGroupLevel( ), dropType );
			}
		}

		// the table has no cache, the cell is the first drop cell
		if ( !hasDropCell( ) && rowSpan < 0 )
		{
			layoutEvents.push( new LayoutEvent( LayoutEvent.ON_FIRST_DROP_CELL,
					new Integer( layout.getRowCount( ) - 1 ) ) );
		}
		if ( hasDropCell( ) || rowSpan < 0 )
		{
			buffer = new BufferedReportEmitter( emitter );
			cellEmitter = buffer;
		}
		// we need cache the cell
		createCell( colId, rowSpan, colSpan, new CellContent( cell, buffer ) );
		if ( hasDropCell( ) )
		{
			return;
		}

		emitter.startCell( cell );
	}

	public void endCell( ICellContent cell )
	{
		//that means the cell is drop cell or is a hidden cell.
		if ( cellEmitter != null )
		{
			cellEmitter = null;
		}
		else 
		{
			//the cell is visible and it has no buffer.
			emitter.endCell( cell );
		}
	}

	void pushTableLayout( ITableContent table )
	{
		TableLayout tableLayout = new TableLayout( );
		tableLayout.layout = this.layout;
		tableLayout.layoutEvents = this.layoutEvents;
		tableLayout.emitter = this.emitter;
		tableLayout.cellEmitter = this.cellEmitter;
		tableLayout.groupStack = this.groupStack;
		tableLayout.hasDropCell = this.hasDropCell;

		tableLayouts.push( tableLayout );

		this.layout = new TableContentLayout( table,
				EngineIRConstants.FORMAT_TYPE_VIEWER );
		this.layoutEvents = new Stack( );
		this.groupStack = new Stack( );
		this.emitter = tableLayout.cellEmitter != null
				? tableLayout.cellEmitter
				: tableLayout.emitter;
		this.cellEmitter = null;
		this.hasDropCell = false;
	}

	void popTableLayout( ITableContent table )
	{
		assert layout != null;
		resolveAll( );
		flush( );

		TableLayout tableLayout = (TableLayout) tableLayouts.pop( );
		this.layout = tableLayout.layout;
		this.layoutEvents = tableLayout.layoutEvents;
		this.emitter = tableLayout.emitter;
		this.cellEmitter = tableLayout.cellEmitter;
		this.groupStack = tableLayout.groupStack;
		this.hasDropCell = tableLayout.hasDropCell;
	}

	protected class TableLayout
	{

		TableContentLayout layout;
		Stack layoutEvents;
		IContentEmitter emitter;
		IContentEmitter cellEmitter;
		Stack groupStack;
		boolean hasDropCell;
	}

	protected Stack tableLayouts = new Stack( );

}
