// ====================================================================
// File:		StateManager
// 
// Project:		General swing support.
//
// Purpose:		Handle a bunch of states and take care that
//                      only one is active.
//
// Author:		rammi
// Date: 		31.03.2002
// Time: 		10:22:36
// --------------------------------------------------------------------
// 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.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;

/**
 *  Handle a bunch of states and take care that only one is active.
 *
 *  @author  <a href="mailto:rammi@caff.de">Rammi</a>
 *  @version $Revision$
 */
public class StateManager
  implements PropertyChangeListener
{
  /** A collection of states. */
  private List stateList = new LinkedList();
  /** The active state. */
  private AbstractState activeState;

  /**
   *  Add a state.
   *  @param  state  state to add
   */
  public void addState(AbstractState state)
  {
    state.setManager(this);
    stateList.add(state);

    if (activeState == null) {
      // one state has to be active
      state.activate();
      activeState = state;
    }
    else {
      // if the state is added as active, use it instead
      setActiveState(state);
    }

    state.addPropertyChangeListener(this);
  }

  /**
   *  Set the currently active state.
   *  @param state  state to set active
   */
  void setActiveState(AbstractState state)
  {
    if (activeState != state) {
      if (stateList.contains(state)) {
        if (activeState != null) {
          activeState.deactivate();
        }

        activeState = state;
      }
      else {
        throw new RuntimeException("Trying to activate unregistered state");
      }
    }
  }

  /**
   *  Get the list of known states.
   *  @return list of states
   */
  public List getStates()
  {
    return Collections.unmodifiableList(stateList);
  }

  /**
   *  Get the active state.
   *  @return the active state
   */
  public State getActiveState()
  {
    return activeState;
  }

  /**
   *  May a state be activated?
   *  @param state state to check
   *  @return <code>true</code> if the state is known and enabled
   */
  boolean mayActivate(AbstractState state)
  {
    if (stateList.contains(state)) {
      return state.isEnabled();
    }
    else {
      throw new RuntimeException("Trying to access unregistered state");
    }
  }

  /**
   *  May the state be enabled or disabled?
   *  @param state the state
   *  @param enable enable state
   *  @return <code>true</code> if enable is true or this is not the only enabled state
   */
  boolean mayEnable(AbstractState state, boolean enable)
  {
    return enable || getFirstEnabledState(state) != null;
  }

  /**
   *  Get the first state which is enabled.
   *  @param ignore state to be ignored during search
   *  @return first state which is enabled
   */
  protected AbstractState getFirstEnabledState(AbstractState ignore)
  {
    for (Iterator it = stateList.iterator();  it.hasNext();  ) {
      AbstractState state = (AbstractState)it.next();
      if (state != ignore  &&  state.isEnabled()) {
        return state;
      }
    }

    // nothing found
    return null;
  }

  /**
   * This method gets called when a bound property is changed.
   * @param evt A PropertyChangeEvent object describing the event source
   *   	and the property that has changed.
   */
  public void propertyChange(PropertyChangeEvent evt)
  {
    AbstractState state = (AbstractState)evt.getSource();
    if (AbstractState.ACTIVATION_PROPERTY.equals(evt.getPropertyName())) {
      if (((Boolean)evt.getNewValue()).booleanValue()) {
        setActiveState(state);
      }
    }
    else if (AbstractState.ENABLE_PROPERTY.equals(evt.getPropertyName())) {
      if (!((Boolean)evt.getNewValue()).booleanValue()) {
        if (activeState == state) {
          // set first enabled state active
          getFirstEnabledState(state).activate();
        }
      }
    }
  }
}
