// Purpose.  State design pattern (centralized 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. The wrapper class contains a state transition table
// 5. All client requests are simply delegated to the current state object
// 6. Create a state base class that makes the concrete states interchangeable
// 7. The State base class specifies default behavior for all messages
// 8. The State derived classes only override the messages they need to

// 1. Create a "wrapper" class that models the state machine
class FSM {
   private State[] states     = { new A(), new B(), new C() }; // 2. states
   private int[][] transition = { {2,1,0}, {0,2,1}, {1,2,2} }; // 4. transitions
   private int     current    = 0;                             // 3. current
   private void next( int msg ) { current = transition[current][msg]; }

   // 5. All client requests are simply delegated to the current state object
   public void on()  { states[current].on();   next( 0 ); }
   public void off() { states[current].off();  next( 1 ); }
   public void ack() { states[current].ack();  next( 2 ); }
}

// 6. Create a state base class that makes the concrete states interchangeable
abstract class State {
   public void on()  { System.out.println( "error" ); }  // 7. The State base 
   public void off() { System.out.println( "error" ); }  //    class specifies 
   public void ack() { System.out.println( "error" ); }  //    default behavior
}

class A extends State {
   public void on()  { System.out.println( "A + on  = C" ); }
   public void off() { System.out.println( "A + off = B" ); }
   public void ack() { System.out.println( "A + ack = A" ); }
}

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

class C extends State {
   // 8. The State derived classes only override the messages they need to
   public void on()  { System.out.println( "C + on  = B" ); }
}

public class StateDemo {
   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

