package net.sf.fmj.ejmf.toolkit.gui.controls;

import java.awt.Component;
import java.util.EventListener;

import javax.media.Control;
import javax.media.Controller;
import javax.media.NotRealizedError;

/**
* An AbstractListenerControl provides a Component-Listener
* model for Controls, allowing the two elements to be
* changed independently of each other.
* <p>
* This is the root class from which all EJMF control panel
* controls extend.
* <p>
* Subclasses must implement these methods:
* <ul>
*    <li>createControlListener()
*    <li>createControlComponent()
*    <li>addControlListener(EventListener listener)
*    <li>removeControlListener(EventListener listener)
* </ul>
*/
public abstract class AbstractListenerControl implements Control {
    private EventListener	currListener;
    private EventListener	defaultListener;

    private Controller		controller;
    private Component		component;
    protected final Skin skin;

	/**
	* Create an AbstractListenerControl
	*/
    protected AbstractListenerControl(Skin skin) {
	this.skin = skin;
	component = createControlComponent(skin);
	initListener(createControlListener());
    }

	/**
	* Create an AbstractListenerControl
	* @param controller Controller with which this control is associated.
	*/
    protected AbstractListenerControl(Skin skin, Controller controller) {
	this(skin);
	setController(controller);
    }

	/**
	* Associates Controller with this Control.
	* @throws IllegalArgumentException
	* is thrown if attempt is made to set Controller	
	* a second time.
	* @param controller Controller with which this control is associated.
	*/
    public void setController(Controller controller) {

	// Do not allow Controller to be set a second time.
	if (this.controller != null)  {
	    throw new IllegalArgumentException("Controller already set on Control");
	}

	if (controller.getState() < Controller.Realized) {
	    throw new NotRealizedError("Control requires realized Controller");
	}
	this.controller = controller;
	setControllerHook(controller);
    }

	/**
	* Subclasses override and provide the guts to
	* setControllerHook if they want to augment the
	* the work of setController.
	* @param controller Controller with which this control is associated.
	*/
    protected void setControllerHook(Controller controller)  {
    }

	/**
	* Initializes listener, establishes it as default
	* and registers it with Control Compoent.	
	* @param listener Listener object for this control
	*/
    protected void initListener(EventListener listener) {
	defaultListener = listener;
	currListener = defaultListener;
	addControlListener(defaultListener);
    }

	/**
 	* Return Control Component as required by
	* Control inteface.
	* @return Control component
	*/	
    public Component getControlComponent() {
	return component;
    }

	/**
	* Set the control listener associated with this Control.
	*<p>	
	* Subclasses should override this method to provide
	* a type-safe public version.
	* @param listener Listener object for this control
	*/
    protected void setControlListener(EventListener listener) {

	if (currListener != null)
	    removeControlListener(currListener);
        addControlListener(listener);
	currListener = listener;
    }

	/**
	* Set the GUI component associated with this Control.
	*<p>	
	* Subclasses should override this method to provide
	* a type-safe public version.
	* @param component Component to associate with this control.
        */
    protected void setComponent(Component component) {
	setComponentAndListener(component, getDefaultControlListener());
    }

	/**
	* Set both GUI component and control listener associated
	* with this Control.
	*<p>	
	* Subclasses should override this method to provide
	* a type-safe public version.
	* @param component Component to associate with this control.
	* @param listener Listener object for this control
      	*/
    protected void setComponentAndListener(Component component, 
					EventListener listener) {
	this.component = component;
	setControlListener(listener);
    }

	/**
	* @return the default listener for this Control.
    	*/
    protected EventListener getDefaultControlListener() {
	return defaultListener;
    }

    /**
      * @return fully-qualified class name. This value
      * can be used as search key in response to Controller.getControl(name)
      */
    @Override
	public String toString() {
	return getClass().getName();
    }

	/**	
	  * Subclasses of AbstractListenerControl
          * must define this method to supply an
          * EventListener to handle events originating
          * from control's component.
	  * <p>
	  * Subclasses must not return a null listener.
	  * @return an EventListener
 	  */
    protected abstract EventListener createControlListener();
	/** 
	* Subclasses defined this method to supply their
        * GUI component. Subclasses must not return a null
	* component. 
	* <p>
	* Listener registration is handled
        * by AbstractListenerControl.
	  * @return a Component
	*/
    protected abstract Component createControlComponent(Skin skin);
	/**
	* Since different controls may have different types
   	* of listeners, each subclass should define addControlListener
    	* to register the correct type of listener with component.
	* @param listener Listener object to be added.
	*/
    protected abstract void addControlListener(EventListener listener);
	/**
	* Since different controls may have different types
   	* of listeners, each subclass should define removeControlListener
    	* to remove listener from component listener list in a type-safe 
	* way.
	* @param listener Listener object to be removed.
	*/
   protected abstract void removeControlListener(EventListener listener);

   private boolean isOperational = true;

	/**	
	* Set the operational state of this Control.	
	* @param flag Set to true to make control operational.
	*/
   protected void setOperational(boolean flag) {
	isOperational = flag;
   }

	/**	
	* Return the operational state of this Control.	
	* @return true if control is operational
	*/
   public boolean isOperational() {
	return isOperational;
   }

	/**
	* @return associated Controller.
	*/
   public Controller getController() {
	return controller;
   }
}
