function StateMachine(options) {
  options = options || {};

  if (!(this instanceof StateMachine)) {
    return new StateMachine(options);
  }

  var states = this.states = (options.states || this.states),
      initialState = this.initialState = (options.initialState || this.initialState || Object.keys(states).shift());

  this.transitionTo(initialState);
}

StateMachine.prototype = {
  constructor: StateMachine,

  initialState: null,
  state: null,
  states: {},

  get currentState() {
    return this.states[this.state];
  },

  transitionTo: function(state) {
    var currentState = this.currentState,
        newState = this.states[state];

    if (typeof(newState) !== 'object') {
      throw "Invalid state '" + state + "'";
    }

    if (currentState && typeof(currentState.exit) === 'function') {
      currentState.exit(this);
    }

    this.state = state;

    if (typeof(newState.enter) === 'function') {
      newState.enter(this);
    }
  },

  send: function(method) {
    var args = [].slice.call(arguments, 1),
        currentState = this.currentState,
        func = currentState[method];

    if (typeof(func) !== 'function') {
      throw "Cannot call '" + method + "' on current state '" + this.state + "'";
    }

    args.unshift(this);
    func.apply(currentState, args);
  }
};