// Purpose.  State design pattern (distributed transition logic)

// 1. Create a "wrapper" class that models the state machine
// 2. The wrapper class contains an array of state concrete objects
// 3. The wrapper class contains an index to its "current" state
// 4. Client requests are delegated to the current state and "this" is passed
// 5. Create a state base class that makes the concrete states interchangeable
// 6. The State base class specifies default behavior for all messages
// 7. The State derived classes only override the messages they need to
// 8. The derived classes "call back to" the wrapper class to change its current

class FSM {                                                 // 1. The "wrapper"
   private State[] states  = { new A(), new B(), new C() }; // 2. States array
   private int     current = 0;                             // 3. Current state
   public void on()  { states[current].on(  this ); }       // 4. Delegation
   public void off() { states[current].off( this ); }       //    and pass the
   public void ack() { states[current].ack( this ); }       //    this pointer
   public void changeState( int index ) { current = index; }
}

abstract class State {                               // 5. The State base class
   public void on(  FSM fsm ) { System.out.println( "error" ); } // 6. Default
   public void off( FSM fsm ) { System.out.println( "error" ); } //    behavior
   public void ack( FSM fsm ) { System.out.println( "error" ); }
}

class A extends State {
   public void on(  FSM fsm ) { System.out.println( "A + on  = C" );
      fsm.changeState( 2 ); }
   public void off( FSM fsm ) { System.out.println( "A + off = B" );
      fsm.changeState( 1 ); }
   public void ack( FSM fsm ) { System.out.println( "A + ack = A" );
      fsm.changeState( 0 ); }
}

class B extends State {
   public void on(  FSM fsm ) { System.out.println( "B + on  = A" );
      fsm.changeState( 0 ); }
   public void off( FSM fsm ) { System.out.println( "B + off = C" );
      fsm.changeState( 2 ); }
}

class C extends State {                  // 7. Only override some messages
   public void on( FSM fsm ) { System.out.println( "C + on  = B" );
      fsm.changeState( 1 ); }            // 8. "call back to" the wrapper class
}

public class StateDemo2 {
   public static void main( String[] args ) {
      FSM   fsm  = new FSM();
      int[] msgs = { 2, 1, 2, 1, 0, 2, 0, 0 };
      for (int i=0; i < msgs.length; i++)
         if      (msgs[i] == 0) fsm.on();
         else if (msgs[i] == 1) fsm.off();
         else if (msgs[i] == 2) fsm.ack();
}  }

// A + ack = A
// A + off = B
// error
// B + off = C
// C + on  = B
// error
// B + on  = A
// A + on  = C

