/*******************************************************************************
 * 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.designer.internal.ui.views.attributes.page;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.birt.report.designer.ui.views.attributes.IPropertyDescriptor;
import org.eclipse.birt.report.designer.ui.views.attributes.TabPage;
import org.eclipse.birt.report.designer.ui.views.attributes.providers.AttributeConstant;
import org.eclipse.birt.report.model.api.DesignElementHandle;
import org.eclipse.birt.report.model.api.ModuleHandle;
import org.eclipse.birt.report.model.api.ReportItemHandle;
import org.eclipse.birt.report.model.api.StyleHandle;
import org.eclipse.birt.report.model.api.activity.NotificationEvent;
import org.eclipse.birt.report.model.api.command.PropertyEvent;
import org.eclipse.birt.report.model.api.core.Listener;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Composite;

/**
 * The sup-class of all attribute page, provides common register/unregister
 * implementation to DE model, and default refresh process after getting a
 * notify from DE.
 */
public abstract class AttributePage extends TabPage implements Listener
{

	/**
	 * A default quick button height which if different in win32 from other OS.
	 */
	public static final int QUICK_BUTTON_HEIGHT = Platform.getOS( )
			.equals( Platform.OS_WIN32 ) ? 20 : 22;

	/**
	 * The list kept Property & PropertyDescriptor pair.
	 */
	protected HashMap propertiesMap = new HashMap( 7 );

	/**
	 * The current selection.
	 */
	protected List input = new ArrayList( );

	/**
	 * Keeps the properties that send a notify.
	 */
	private Set propertiesSet = new HashSet( );

	/**
	 * For multi-selections process, there will be as much notifies as the
	 * number of multi-selections, the int flag is used to filter these notifies
	 * that are the last.
	 */
	private int refreshCount = 0;

	/**
	 * Constructor
	 * 
	 * @param parent
	 *            A widget which will be the parent of the new instance (cannot
	 *            be null)
	 * @param style
	 *            The style of widget to construct
	 */
	public AttributePage( Composite parent, int style )
	{
		super( parent, style );
		this.addDisposeListener( new DisposeListener( ) {

			public void widgetDisposed( DisposeEvent e )
			{
				deRegisterListeners( );
			}
		} );
		buildUI( );

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.report.designer.ui.attributes.page.TabPage#setInput(java.util.List)
	 */
	public void setInput( List elements )
	{
		deRegisterListeners( );
		input = elements;

		Object[] descriptors = propertiesMap.values( ).toArray( );
		for ( int i = 0; i < descriptors.length; i++ )
		{
			IPropertyDescriptor descriptor = (IPropertyDescriptor) descriptors[i];
			descriptor.setModelList( elements );
		}
		refreshValues( new HashSet( propertiesMap.keySet( ) ) );
		registerListeners( );
	}

	/**
	 * Sets the UI values after the model had changed. For structure property
	 * pages, subclass should override this method, as well as elementChanged().
	 */
	protected void refreshValues( Set propertiesSet )
	{
		Object[] properties = propertiesSet.toArray( );
		for ( int i = 0; i < properties.length; i++ )
		{
			IPropertyDescriptor descriptor = (IPropertyDescriptor) propertiesMap.get( properties[i] );
			if ( descriptor != null )
				descriptor.resetUIData( );
		}
		propertiesSet.clear( );
	}

	/**
	 * Removes model change listener.
	 */
	protected void deRegisterListeners( )
	{
		if ( input == null )
			return;
		for ( int i = 0; i < input.size( ); i++ )
		{
			Object obj = input.get( i );
			if ( obj instanceof DesignElementHandle )
			{
				DesignElementHandle element = (DesignElementHandle) obj;
				element.removeListener( this );
			}
		}
	}

	/**
	 * Registers model change listener to DE elements.
	 */
	protected void registerListeners( )
	{
		if ( input == null )
			return;
		for ( int i = 0; i < input.size( ); i++ )
		{
			Object obj = input.get( i );
			if ( obj instanceof DesignElementHandle )
			{
				DesignElementHandle element = (DesignElementHandle) obj;
				element.addListener( this );
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.birt.model.core.Listener#elementChanged(org.eclipse.birt.model.core.DesignElement,
	 *      org.eclipse.birt.model.activity.NotificationEvent) @note: For
	 *      structure property pages, subclass should override this method, as
	 *      well as refreshValues().
	 */
	public void elementChanged( DesignElementHandle focus, NotificationEvent ev )
	{
		if ( refreshCount != input.size( ) - 1 )
		{
			refreshCount++;
			return;
		}
		int eventType = ev.getEventType( );
		if ( eventType == NotificationEvent.NAME_EVENT )
		{
			if ( propertiesMap.containsKey( DesignElementHandle.NAME_PROP ) )
				propertiesSet.add( DesignElementHandle.NAME_PROP );
		}
		else if ( eventType == NotificationEvent.PROPERTY_EVENT )
		{
			PropertyEvent event = (PropertyEvent) ev;
			String propertyName = event.getPropertyName( );
			if ( StyleHandle.TEXT_ALIGN_PROP.equals( propertyName ) )
			{
				propertyName = AttributeConstant.HORIZONTAL_ALIGN;
			}
			if ( propertiesMap.containsKey( propertyName ) )
				propertiesSet.add( propertyName );
		}
		else if ( eventType == NotificationEvent.STYLE_EVENT )
		{
			if ( propertiesMap.containsKey( ReportItemHandle.STYLE_PROP ) )
				propertiesSet.add( ReportItemHandle.STYLE_PROP );
		}
		else if ( eventType == NotificationEvent.THEME_EVENT)
		{
			if ( propertiesMap.containsKey( ModuleHandle.THEME_PROP ) )
				propertiesSet.add( ModuleHandle.THEME_PROP );
		}
		else
		{
			return;
		}
		if ( refreshCount == input.size( ) - 1 )
		{
			refreshCount = 0;
			refreshValues( propertiesSet );
		}
		
	}

	/**
	 * Builds UI controls of this page.
	 * 
	 * @param elements
	 *            The current selection.
	 */
	protected abstract void buildUI( );

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.swt.widgets.Widget#dispose()
	 */
	public void dispose( )
	{
		deRegisterListeners( );
		super.dispose( );
	}
}