// ====================================================================
// File:		AbstractState
//
// Project:
//
// Purpose:
//
// Author:		rammi
// Date: 		31.03.2002
// Time: 		10:00:40
// --------------------------------------------------------------------
// Copyright Notice:	(c) 2002 Rammi (rammi@caff.de)
//                      This code is in the public domain.
//                      Use at own risk.
//                      No guarantees given.
//
// Latest change:       $Date$
//
// History: 		$Log$
// ====================================================================
package de.caff.gimmicks.swing;

import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.HashMap;
import java.util.Map;

/**
 *  Basic implementation of a state.
 *  It takes care of listeners and activation.
 *
 *  @author  <a href="mailto:rammi@caff.de">Rammi</a>
 *  @version $Revision$
 */
public abstract class AbstractState
  implements State
{
  /** Property change support for standard handling of property changes. */
  protected PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

  /** Property list. */
  protected Map properties = new HashMap();

  /** The state manager where we are registered. */
  protected StateManager manager;

  /**
   *  Create a new abstract state.
   */
  public AbstractState()
  {
    setPropertyInternal(ACTIVATION_PROPERTY, Boolean.FALSE);
    setPropertyInternal(ENABLE_PROPERTY, Boolean.TRUE);
  }

  /**
   *  Set the state manager which handles this state.
   *  @param stateManager state manager
   */
  void setManager(StateManager stateManager)
  {
    manager = stateManager;
  }

  /**
   *  Add a property change listener.
   *  @param listener listener to add
   */
  public void addPropertyChangeListener(PropertyChangeListener listener)
  {
    propertyChangeSupport.addPropertyChangeListener(listener);
  }

  /**
   *  Add a property change listener for a special property.
   *  @param property propert< identifier
   *  @param listener listener to add
   */
  public void addPropertyChangeListener(String property, PropertyChangeListener listener)
  {
    propertyChangeSupport.addPropertyChangeListener(property, listener);
  }

  /**
   *  Remove a property change listener.
   *  @param listener listener to remove
   */
  public void removePropertyChangeListener(PropertyChangeListener listener)
  {
    propertyChangeSupport.removePropertyChangeListener(listener);
  }

  /**
   *  Remove a property change listener for a special property.
   *  @param property propert< identifier
   *  @param listener listener to remove
   */
  public void removePropertyChangeListener(String property, PropertyChangeListener listener)
  {
    propertyChangeSupport.removePropertyChangeListener(property, listener);
  }

  /**
   *  Activate this state.
   *  @return <code>true</code> if the activation was possible,
   *          <code>false<code> otherwise
   */
  public boolean activate()
  {
    if (!isActivated()) {
      if (manager == null  ||   manager.mayActivate(this)) {
        setPropertyInternal(ACTIVATION_PROPERTY, Boolean.TRUE);
        return true;
      }
      else {
        return false;
      }
    }
    else {
      return true;
    }
  }

  /**
   *  An action is performed. This is just handled as a call to activate().
   *  @param e unused
   */
  public void actionPerformed(ActionEvent e)
  {
    activate();
  }

  /**
   *  Is this state activated?
   *  @return the answer
   */
  public boolean isActivated()
  {
    return ((Boolean)properties.get(ACTIVATION_PROPERTY)).booleanValue();
  }

  /**
   *  Deactivate this state.
   */
  protected void deactivate()
  {
    if (isActivated()) {
      setPropertyInternal(ACTIVATION_PROPERTY, Boolean.FALSE);
    }
  }

  /**
   *  Enable or disable this state.
   *  @param state if <code>true</code> enable this state, otherwise disable it
   */
  public void setEnabled(boolean state)
  {
    if (isEnabled() != state) {
      if (manager.mayEnable(this, state)) {
        setPropertyInternal(ENABLE_PROPERTY, state ? Boolean.TRUE : Boolean.FALSE);
      }
    }
  }

  /**
   *  Is this state enabled?
   *  @return the answer
   */
  public boolean isEnabled()
  {
    return ((Boolean)properties.get(ENABLE_PROPERTY)).booleanValue();
  }

  /**
   *  Get the value of a given property.
   *  @param name property name
   */
  public Object getValue(String name)
  {
    return properties.get(name);
  }

  /**
   *  Set the value of a given property,
   *  @param key   property key
   *  @param value new value of property
   */
  public void putValue(String key, Object value)
  {
    Object oldValue = getValue(key);
    if (oldValue != value  ||  (oldValue != null  &&  !oldValue.equals(value))) {
      properties.put(key, value);
      propertyChangeSupport.firePropertyChange(key, oldValue, value);
    }
  }

  /**
   *  Set a property to a new value.
   *  @param name  property name
   *  @param value  new value
   */
  protected void setProperty(String name, Object value)
  {
    Object oldValue = getValue(name);

    // handle special values
    if (ACTIVATION_PROPERTY.equals(name)  ||
        ENABLE_PROPERTY.equals(name)) {
      throw new RuntimeException("Don't set property "+name+" via setProperty(). Use dedicated method instead.");
    }
    if (oldValue != value) {
      if (value == null  ||  oldValue == null  ||  !value.equals(oldValue)) {
        properties.put(name, value);
        propertyChangeSupport.firePropertyChange(name, oldValue, value);
      }
    }
  }

  /**
   *  Set a property to a new value (internal version).
   *  @param name  property name
   *  @param value  new value
   */
  private void setPropertyInternal(String name, Object value)
  {
    Object oldValue = getValue(name);

    properties.put(name, value);
    propertyChangeSupport.firePropertyChange(name, oldValue, value);
  }
}
