text: /**
   * @fileOverview
   * PureMVC State Machine Utility JS Native Port by Saad Shams
   * Copyright(c) 2006-2012 Futurescale, Inc., Some rights reserved.
   * Reuse governed by Creative Commons Attribution 3.0 
   * http://creativecommons.org/licenses/by/3.0/us/
   * @author saad.shams@puremvc.org 
   */
  
  var puremvc = require( './puremvc-1.0.1-mod.js' );
      
  /**
   * Constructor
   *
   * Defines a State.
   * @method State
   * @param {string} name id the id of the state
   * @param {string} entering an optional notification name to be sent when entering this state
   * @param {string} exiting an optional notification name to be sent when exiting this state
   * @param {string} changed an optional notification name to be sent when fully transitioned to this state
   * @return 
   */
  
  function State(name, entering, exiting, changed) {  
      this.name = name;
      if(entering) this.entering = entering;
      if(exiting) this.exiting = exiting;
      if(changed) this.changed = changed;
      this.transitions = {};
  }
  
  /**
   * Define a transition.
   * @method defineTrans
   * @param {string} action the name of the StateMachine.ACTION Notification type.
   * @param {string} target the name of the target state to transition to.
   * @return 
   */
  State.prototype.defineTrans = function(action, target) {
      if(this.getTarget(action) != null) return;
      this.transitions[action] = target;
  }
  
  /**
   * Remove a previously defined transition.
   * @method removeTrans
   * @param {string} action
   * @return 
   */
  State.prototype.removeTrans = function(action) {
      delete this.transitions[action];
  }
  
  /**
   * Get the target state name for a given action.
   * @method getTarget
   * @param {string} action
   * @return State
   */
  /**
   * 
   */
  State.prototype.getTarget = function(action) {
      return this.transitions[action] ? this.transitions[action] : null;
  }
  
  // The state name
  State.prototype.name = null;
  
  // The notification to dispatch when entering the state
  State.prototype.entering = null;
  
  // The notification to dispatch when exiting the state
  State.prototype.exiting = null;
  
  // The notification to dispatch when the state has actually changed
  State.prototype.changed = null;
  
  /**
   *  Transition map of actions to target states
   */ 
  State.prototype.transitions = null;
      
  
      
   /**
   * A Finite State Machine implimentation.
   * <P>
   * Handles regisistration and removal of state definitions, 
   * which include optional entry and exit commands for each 
   * state.</P>
   */
  
  /**
   * Constructor
   *
   * @method StateMachine
   * @return 
   */
  function StateMachine() {
      puremvc.Mediator.call(this, StateMachine.NAME, null);
      this.states = {};
  }
      
  StateMachine.prototype = new puremvc.Mediator;
  StateMachine.prototype.constructor = StateMachine;
  
  /**
   * Transitions to initial state once registered with Facade
   * @method onRegister
   * @return 
   */
  StateMachine.prototype.onRegister = function() {
      if(this.initial) this.transitionTo(this.initial, null);
  }
  
  /**
   * Registers the entry and exit commands for a given state.
   * @method registerState
   * @param {State} state the state to which to register the above commands
   * @param {boolean} initial boolean telling if this is the initial state of the system
   * @return 
   */
  StateMachine.prototype.registerState = function(state, initial) {
      if(state == null || this.states[state.name] != null) return;
      this.states[state.name] = state;
      if(initial) this.initial = state;
  }
  
  /**
   * Remove a state mapping. Removes the entry and exit commands for a given state as well as the state mapping itself.
   * @method removeState
   * @param {string} stateName
   * @return 
   */
  StateMachine.prototype.removeState = function(stateName) {
      var state = this.states[stateName];
      if(state == null) return;
      this.states[stateName] = null;
  }
  
  /**
   * Transitions to the given state from the current state.
   * <P>
   * Sends the <code>exiting</code> notification for the current state 
   * followed by the <code>entering</code> notification for the new state.
   * Once finally transitioned to the new state, the <code>changed</code> 
   * notification for the new state is sent.</P>
   * <P>
   * If a data parameter is provided, it is included as the body of all
   * three state-specific transition notes.</P>
   * <P>
   * Finally, when all the state-specific transition notes have been
   * sent, a <code>StateMachine.CHANGED</code> note is sent, with the
   * new <code>State</code> object as the <code>body</code> and the name of the 
   * new state in the <code>type</code>.
   *
   * @method transitionTo
   * @param {State} nextState the next State to transition to.
   * @param {Object} data is the optional Object that was sent in the <code>StateMachine.ACTION</code> notification body
   * @return 
   */
  StateMachine.prototype.transitionTo = function(nextState, data) {
      // Going nowhere?
      if(nextState == null) return;
      
      // Clear the cancel flag
      this.canceled = false;
      
      // Exit the current State 
      if(this.getCurrentState() && this.getCurrentState().exiting) 
          this.sendNotification(this.getCurrentState().exiting, data, nextState.name);
      
      // Check to see whether the exiting guard has canceled the transition
      if(this.canceled) {
          this.canceled = false;
          return;
      }
      
      // Enter the next State 
      if(nextState.entering)
          this.sendNotification(nextState.entering, data);
      
      // Check to see whether the entering guard has canceled the transition
      if(this.canceled) {
          this.canceled = false;
          return;
      }
      
      // change the current state only when both guards have been passed
      this.setCurrentState(nextState);
      
      // Send the notification configured to be sent when this specific state becomes current 
      if(nextState.changed) {
          this.sendNotification(this.getCurrentState().changed, data);
      }
      
      // Notify the app generally that the state changed and what the new state is 
      this.sendNotification(StateMachine.CHANGED, this.getCurrentState(), this.getCurrentState().name);
  }
  
  /**
   * Notification interests for the StateMachine.
   * @method listNotificationInterests
   * @return {Array} Array of Notifications
   */
  
  StateMachine.prototype.listNotificationInterests = function() {
      return [
          StateMachine.ACTION,
          StateMachine.CANCEL
      ];
  }
  
  /**
   * Handle notifications the <code>StateMachine</code> is interested in.
   * <P>
   * <code>StateMachine.ACTION</code>: Triggers the transition to a new state.<BR>
   * <code>StateMachine.CANCEL</code>: Cancels the transition if sent in response to the exiting note for the current state.<BR>
   *
   * @method handleNotification
   * @param {Notification} notification
   * @return 
   */
  StateMachine.prototype.handleNotification = function(notification) {
      switch(notification.getName()) {
          case StateMachine.ACTION:
              var action = notification.getType();
              var target = this.getCurrentState().getTarget(action);
              var newState = this.states[target];
              if(newState) this.transitionTo(newState, notification.getBody());
              break;
              
          case StateMachine.CANCEL:
              this.canceled = true;
              break;
      }
  }
  
  /**
   * Get the current state.
   * @method getCurrentState
   * @return a State defining the machine's current state
   */
  StateMachine.prototype.getCurrentState = function() {
      return this.viewComponent;
  }
  
  /**
   * Set the current state.
   * @method setCurrentState
   * @param {State} state
   * @return 
   */
  StateMachine.prototype.setCurrentState = function(state) {
      this.viewComponent = state;
  }
  
  /**
   * Map of States objects by name.
   */
  StateMachine.prototype.states = null;
  
  /**
   * The initial state of the FSM.
   */
  StateMachine.prototype.initial = null;
  
  /**
   * The transition has been canceled.
   */
  StateMachine.prototype.canceled = null;
      
  StateMachine.NAME = "StateMachine";
  
  /**
   * Action Notification name. 
   */ 
  StateMachine.ACTION = StateMachine.NAME + "/notes/action";
  
  /**
   *  Changed Notification name  
   */ 
  StateMachine.CHANGED = StateMachine.NAME + "/notes/changed";
  
  /**
   *  Cancel Notification name  
   */ 
  StateMachine.CANCEL = StateMachine.NAME + "/notes/cancel";
      
      
  /**
   * Creates and registers a StateMachine described in JSON.
   * 
   * <P>
   * This allows reconfiguration of the StateMachine 
   * without changing any code, as well as making it 
   * easier than creating all the <code>State</code> 
   * instances and registering them with the 
   * <code>StateMachine</code> at startup time.
   * 
   * @ see State
   * @ see StateMachine
   */
  
  /**
   * Constructor
   * @method FSMInjector
   * @param {Object} fsm JSON Object
   * @return 
   */
  function FSMInjector(fsm) {
      puremvc.Notifier.call(this);
      this.fsm = fsm;
  }
    
  FSMInjector.prototype = new puremvc.Notifier;
  FSMInjector.prototype.constructor = FSMInjector;
  
  /**
   * Inject the <code>StateMachine</code> into the PureMVC apparatus.
   * <P>
   * Creates the <code>StateMachine</code> instance, registers all the states
   * and registers the <code>StateMachine</code> with the <code>IFacade</code>.
   * @method inject
   * @return 
   */
  FSMInjector.prototype.inject = function() {
      // Create the StateMachine
      var stateMachine = new puremvc.statemachine.StateMachine();
      
      // Register all the states with the StateMachine
      var states = this.getStates();
      for(var i=0; i<states.length; i++) {
          stateMachine.registerState(states[i], this.isInitial(states[i].name));
      }
      
      // Register the StateMachine with the facade
      this.facade.registerMediator(stateMachine);
  }
  
  /**
   * Get the state definitions.
   * <P>
   * Creates and returns the array of State objects 
   * from the FSM on first call, subsequently returns
   * the existing array.</P>
   *
   * @method getStates
   * @return {Array} Array of States
   */
  FSMInjector.prototype.getStates = function() {
      if(this.stateList == null) {
          this.stateList = [];
  
          var stateDefs = this.fsm.state ? this.fsm.state : [];
          for(var i=0; i<stateDefs.length; i++) {
              var stateDef = stateDefs[i];
              var state = this.createState(stateDef);
              this.stateList.push(state);
          }
      }
      return this.stateList;
  }
  
  /**
   * Creates a <code>State</code> instance from its JSON definition.
   * @method createState
   * @param {Object} stateDef JSON Object
   * @return {State} 
   */
  /**
  
   */
  FSMInjector.prototype.createState = function(stateDef) {
      // Create State object
      var name = stateDef['@name'];
      var exiting = stateDef['@exiting'];
      var entering = stateDef['@entering'];
      var changed = stateDef['@changed'];
      var state = new puremvc.statemachine.State(name, entering, exiting, changed);
      
      // Create transitions
      var transitions = stateDef.transition ? stateDef.transition : [];
      for(var i=0; i<transitions.length; i++) {
          var transDef = transitions[i];
          state.defineTrans(transDef['@action'], transDef['@target']);
      }
      return state;
  }
  
  /**
   * Is the given state the initial state?
   * @method isInitial
   * @param {string} stateName
   * @return {boolean}
   */
  FSMInjector.prototype.isInitial = function(stateName) {
      var initial = this.fsm['@initial'];
      return stateName == initial;
  }
  
  // The JSON FSM definition
  FSMInjector.prototype.fsm = null;
  
  // The List of State objects
  FSMInjector.prototype.stateList = null;
  
  
  puremvc.statemachine =
  {
      State: State
      ,	StateMachine: StateMachine
      ,	FSMInjector: FSMInjector
  };
  
  module.exports = puremvc.statemachine;