package hibergui;

import java.util.ArrayList;
import java.util.List;

import hibergui.event.ChangeEvent;
import hibergui.event.ChangeListener;
import hibergui.event.FocusEvent;
import hibergui.event.FocusListener;
import hibergui.event.KeyEvent;
import hibergui.event.KeyListener;

import org.eclipse.swt.events.ControlListener; //import org.eclipse.swt.events.DisposeEvent;
//import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

/**
 * Common superclass of HiberGUI components.
 * 
 * Although HiberGUI components themselves are not SWT widgets, widgets can be
 * placed on them, and they can be placed on SWT composites as well. According
 * to the logic of SWT, the the elements of a component must be created
 * <b>during</b> the instantiation of component. This is done by the
 * createContents method. Subclasses should override this method to place
 * widgets or subcomponents on themselves. (In AWT or Swing widgets can be put
 * on containers using "add" methods <b>after</b> the container has been
 * instantiated.)
 * 
 * Any activities, which should be performed <b>before</b> the construction of
 * the subcomponents and widgets of the component should be done by the init
 * method. The constructor invokes it before createContents.
 * 
 * HiberGUI components cannot handle SWT events directly. They have own event
 * handling based on SWT.
 * 
 * @author Miklós Espák
 * 
 * @param <T>
 */
public abstract class Component<T> {

	/**
	 * The style of the component. It is a combination of constants defined in
	 * <a>hibergui.HiberGUI</a>.
	 */
	protected final int style;

	/**
	 * The SWT control containing the component.
	 */
	private final Control crl;

	/**
	 * Initializes the component and creates the GUI elements on them. These are
	 * done by <a>init</a> and <a>createContents</a>.
	 * 
	 * @param cptParent
	 *            The composite which the component will be placed on. The
	 *            parent of the control of the component.
	 * @param style
	 *            The style of the component. It is a combination of constants
	 *            defined in <a>hibergui.HiberGUI</a>.
	 */
	protected Component(Composite cptParent, int style) {
		this.style = style;

		init();

		crl = createContents(cptParent);
		// crl.addDisposeListener(new DisposeListener() {
		// public void widgetDisposed(DisposeEvent arg0) {
		// dispose();
		// }
		// });
	}

	/**
	 * Initializes the component. Initialization is done before creating the GUI
	 * contents of the component.
	 */
	protected void init() {
	}

	/**
	 * Disposes the component.
	 */
	protected void dispose() {
	}

	/**
	 * Creates the GUI contents of the component. Subclasses should override it.
	 * 
	 * @param cptParent
	 *            The composite which the component will be placed on. The
	 *            parent of the control of the component.
	 * @return The control of the component containing its GUI elements.
	 */
	protected abstract Control createContents(final Composite cptParent);

	/**
	 * Returns the SWT control of the component. It is created by
	 * <a>createContents</a>.
	 * 
	 * @return the control of the component
	 */
	public Control getControl() {
		return crl;
	}

	/**
	 * Sets the layout data of the component. The call is delegated to the
	 * control of the component.
	 * 
	 * @param data
	 *            the layout data
	 */
	public final void setLayoutData(Object data) {
		crl.setLayoutData(data);
	}

	// --------------------------------------------------------------
	// Listening for focus events
	// --------------------------------------------------------------

	/**
	 * This SWT listener fires a HiberGUI focus event when an SWT focus event is
	 * fired. The listener has to be added to the internal controls of HiberGUI
	 * components.
	 */
	protected final org.eclipse.swt.events.FocusListener lsn_WIDGET_FOCUS = new org.eclipse.swt.events.FocusListener() {
		private boolean focused;

		public void focusGained(org.eclipse.swt.events.FocusEvent arg0) {
			if (!focused) {
				focused = true;
				fireFocusGained(new FocusEvent<Component<?>>(Component.this,
						arg0));
			}
		}

		public void focusLost(org.eclipse.swt.events.FocusEvent arg0) {
			if (focused) {
				focused = false;
				fireFocusLost(new FocusEvent<Component<?>>(Component.this, arg0));
			}
		}
	};

	private List<FocusListener> focusListeners = new ArrayList<FocusListener>();

	public final void addFocusListener(FocusListener lsn) {
		focusListeners.add(lsn);
	}

	public final void removeFocusListener(FocusListener lsn) {
		focusListeners.remove(lsn);
	}

	private void fireFocusGained(FocusEvent<?> event) {
		for (FocusListener lsn : focusListeners)
			lsn.focusGained(event);
	}

	private void fireFocusLost(FocusEvent<?> event) {
		for (FocusListener lsn : focusListeners)
			lsn.focusLost(event);
	}

	public abstract boolean setFocus();

	public final void addControlListener(ControlListener lsn) {
		crl.addControlListener(lsn);
	}

	public final void removeControlListener(ControlListener lsn) {
		crl.removeControlListener(lsn);
	}

	// --------------------------------------------------------------
	// Listening for keys
	// --------------------------------------------------------------

	/**
	 * This SWT listener fires a HiberGUI key event when an SWT key event is
	 * fired. The listener has to be added to the internal controls of HiberGUI
	 * components.
	 */
	protected final org.eclipse.swt.events.KeyListener lsn_WIDGET_KEY = new org.eclipse.swt.events.KeyListener() {
		public void keyPressed(org.eclipse.swt.events.KeyEvent event) {
			if (event.doit != false)
				fireKeyPressed(new KeyEvent<Component<?>>(Component.this, event));
		}

		public void keyReleased(org.eclipse.swt.events.KeyEvent event) {
			if (event.doit != false)
				fireKeyReleased(new KeyEvent<Component<?>>(Component.this,
						event));
		}
	};

	private List<KeyListener> keyListeners = new ArrayList<KeyListener>();

	public final void addKeyListener(KeyListener lsn) {
		keyListeners.add(lsn);
	}

	public final void removeKeyListener(KeyListener lsn) {
		keyListeners.remove(lsn);
	}

	protected void fireKeyPressed(KeyEvent<?> event) {
		for (KeyListener lsn : keyListeners) {
			lsn.keyPressed(event);
			if (event.wrapped.doit == false)
				break;
		}
	}

	protected void fireKeyReleased(KeyEvent<?> event) {
		for (KeyListener lsn : keyListeners) {
			lsn.keyReleased(event);
			if (event.wrapped.doit == false)
				break;
		}
	}

	// --------------------------------------------------------------
	// Listening for changes
	// --------------------------------------------------------------

	private List<ChangeListener> changeListeners = new ArrayList<ChangeListener>();

	public final void addChangeListener(ChangeListener lsn) {
		changeListeners.add(lsn);
	}

	public final void removeChangeListener(ChangeListener lsn) {
		changeListeners.remove(lsn);
	}

	protected void fireChange(ChangeEvent<? extends Component<T>, T> event) {
		for (ChangeListener lsn : changeListeners)
			lsn.change(event);
	}

}
