using System;
using System.Collections;
using System.Collections.Generic;
using Chernobyl.Collections.Generic;
namespace Chernobyl.StateMachine
{
///
/// A default implementation of the interface. This
/// class is used to provide a basic state changing mechanism for states and
/// make it easier to create new types.
///
public class State : IState
{
///
/// This event is raised when this state has become the active state.
/// This can happen if this instances' has been
/// set and the has had
/// its set to this instance. It can also
/// happen if this instance has decided to activate itself. This event
/// will only be raised once when the state has become active. It will not be
/// invoked a second time unless it is deactivated first.
///
public event EventHandler Entered
{
add { _entered += value; }
remove { _entered -= value; }
}
///
/// This event is raised when this state is no longer an active state.
/// This can happen if this instances' has
/// been set and the has
/// had its set to this instance. It can also
/// happen if this instance has decided to deactivate itself. This event
/// will only be raised once when the state has been deactivated. It will
/// not be raised a second time unless it was previously in an active
/// state.
///
public event EventHandler Left
{
add { _left += value; }
remove { _left -= value; }
}
///
/// The that this is a
/// sub-state of or null if this is not sub-state
/// of another state. When set this property will set the incoming
/// instances property
/// to this instance (unless the value set is null of course). If this
/// property is set to null (or in other words, this state is
/// deactivated), two things will occur: (1) the event
/// will be raised and (2), immediately prior to raising the
/// event, this state will deactivate its child state
/// (or sub-state) which will deactivate its child states and so and so
/// forth. Deactivating sub-states is done prior to invoking the
/// event so that sub-states will be deactivated from
/// the lowest sub-state to the highest sub-state. For example, in a
/// state machine setup like so: state1 -> state2 -> state3 -> state4
/// If state2 was deactivated, then state4 would be deactivated first
/// (i.e., its event would be raised first),
/// then state3, and finally state2.
///
public IState ParentState
{
get { return _parentState; }
set { SetParent(ref _parentState, value, this, _entered, _left); }
}
///
/// The currently active sub-state of this or null
/// if this doesn't currently have an active
/// sub-state. When set this property will set the incoming
/// instances property
/// to this instance (unless the value set is null, in which case, the
/// this instances
/// is set to null).
///
public IState ChildState
{
get { return _childState; }
set { SetChild(ref _childState, value, this); }
}
///
/// Returns an enumerator that iterates through a down the children of
/// an system starting at this .
///
///
/// The that allows iteration over
/// a set of linked instance.
///
public IEnumerator GetLinkedEnumerator()
{
return new LinkedEnumerator(this, LinkedNextState);
}
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through
/// the collection.
///
public IEnumerator GetEnumerator()
{
return GetLinkedEnumerator();
}
///
/// A helper method for types deriving from . This
/// method is used in the property to properly
/// set the parent and handle any other tasks that are associated with
/// setting that property. This method will ensure that the parent isn't
/// set twice if the is the same as the
/// , ensures that the
/// of the old parent is set to null (if
/// applicable), ensures that the of the
/// is set to null (if applicable), and ensures
/// the and are
/// invoked using and
/// respectively.
///
/// The previous value of the
/// and the instance that is to take the
/// new value (if applicable).
/// The value being set on
/// .
/// The instance that was the child of
/// and will be the child of
/// . Typically this is the value of 'this'.
/// The method that is to be invoked if the
/// event is to be raised.
/// The method that is to be invoked if the
/// event is to be raised.
public static void SetParent(ref IState parent, IState newParent, IState child,
EventHandler entered, EventHandler left)
{
// Make sure we don't enter or leave a state more than once.
if (parent != newParent)
{
IState previousParentState = parent;
parent = newParent;
// The previous parent should no longer reference this
// IState as it's child.
if (previousParentState != null &&
previousParentState.ChildState == child)
previousParentState.ChildState = null;
if (parent != null)
{
if (entered != null)
entered(child, EventArgs.Empty);
parent.ChildState = child;
}
else
{
// Because this state is being deactivated, that means
// that all of the sub-states of this state need to be
// deactivated. We can do that by setting our child
// state to null which will deactivate our sub-state and
// it's sub-states and so on and so forth.
child.ChildState = null;
if (left != null)
left(child, EventArgs.Empty);
}
}
}
///
/// A helper method for types deriving from . This
/// method is used in the property to properly
/// set the child and handle any other tasks that are associated with
/// setting that property. This method will ensure that the parent isn't
/// set twice if the is the same as the
/// , and ensures that the
/// of the old old child is set to null
/// (if applicable).
///
/// The previous value of the
/// and the instance that is to take the
/// new value (if applicable).
/// The value being set on
/// .
/// The instance that was the parent of
/// and will be the parent of
/// . Typically this is the value of 'this'.
public static void SetChild(ref IState child, IState newChild, IState parent)
{
if (child != newChild)
{
IState previousChildState = child;
child = newChild;
if (child != null)
child.ParentState = parent;
if (previousChildState != null &&
previousChildState.ParentState == parent)
{
// The previous child should no longer reference this
// IState as it's parent.
previousChildState.ParentState = null;
}
}
}
///
/// Retrieves the of the passed in
/// state if it has one. This method is
/// passed to the to allow it to
/// iterate over the linked machine.
///
/// The whose
/// is to be retrieved.
/// The next in the link or the
/// .
/// True if this has a child state and
/// is valid upon exit of this method. False if
/// otherwise.
public static bool LinkedNextState(IState current, out IState next)
{
next = current.ChildState;
return next != null;
}
///
/// Returns an enumerator that iterates through a collection.
///
///
/// An object that can be used to iterate
/// through the collection.
///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// The backing field to .
///
EventHandler _entered;
///
/// The backing field to .
///
EventHandler _left;
///
/// The backing field to .
///
IState _parentState;
///
/// The backing field to .
///
IState _childState;
}
}