// Purpose.  State design pattern lab
//
// Problem.  A large monolithic case  drives the application.  As
// the program evolves, this case statemen may have to be duplicated in
// multiple places.  We want to migrate to an OO design, but we need to be
// careful not to encapsulate multiple distinct "behaviors" in a single
// object.
//
// FSM:  Message ==> on    off   ack
//       State A     A     B     C
//       State B           A     C
//       State C                 B
//
// Assignment.
// o Define an FSM context (wrapper, handle, interface) class
// o FSM declares a private data member of type FSMstate* to remember its
//   "current state"
// o FSM defines the member functions on(), off(), and ack().  Each of these
//   functions delegates to the contained object by calling its member func-
//   tion of the same name, and passing the FSM's "this" pointer. [GOF, p310,
//   bottom]
// o FSM declares class FSMstate as friend (because FSMstate will be setting
//   the "current state" member)
// o Define an FSMstate base class
// o FSMstate defines a default implementation for all FSM messages (on, off,
//   ask).  The implementation of each will look like -
//      virtual void on( FSM* ) { cout << "undefined combo" << endl; }
// o FSMstate defines a protected member function -
//      void changeState( FSM*, FSMstate* )
//   This is used by the derived state classes to set the "current state"
//   member of the FSM.  This "indirection" is necessary because "friendship"
//   is not inherited by derived classes. [GOF, p311]
// o Declare states A, B, and C as derived classes of FSMstate
// o Design states A, B, and C as Singletons
// o Each of these classes override only those messages that they respond to
// o The bodies of the on(), off(), and ack() methods are simply the bodies
//   of the "case" clauses below, except that the state machine's state is
//   changed by calling the base class's changeState() method and passing the
//   "instance" of the desired FSMstate derived class.  [Note: state
//   transitions are defined in each individual state object.  This results
//   in coupling between the derived classes, but it also provides for
//   decentralization of intelligence or control.]
// o FSM's constructor initializes its "current state" member to state "B"
// o The body of main() reduces to "FSM fsm;" plus a minimal driver loop

#include <iostream.h>

enum State   { A, B, C };
enum Message { on, off, ack };
State        currentState;
Message      messageArray[10] = { on,off,off,ack,ack,ack,ack,on,off,off };

void main( void ) {
   currentState = B;
   for (int index=0; index < 10; index++) {
      if (currentState == A) {
         if (messageArray[index] == on) {
            cout << "A, on ==> A" << endl;
            currentState = A;
         } else if (messageArray[index] == off) {
            cout << "A, off ==> B" << endl;
            currentState = B;
         } else if (messageArray[index] == ack) {
            cout << "A, ack ==> C" << endl;
            currentState = C;
         }
      } else if (currentState == B) {
         if (messageArray[index] == on) {
            cout << "undefined combo" << endl;
         } else if (messageArray[index] == off) {
            cout << "B, off ==> A" << endl;
            currentState = A;
         } else if (messageArray[index] == ack) {
            cout << "B, ack ==> C" << endl;
            currentState = C;
         }
      } else if (currentState == C) {
         if (messageArray[index] == on) {
            cout << "undefined combo" << endl;
         } else if (messageArray[index] == off) {
            cout << "undefined combo" << endl;
         } else if (messageArray[index] == ack) {
            cout << "C, ack ==> B" << endl;
            currentState = B;
         }
      }
   }
}

// undefined combo
// B, off ==> A
// A, off ==> B
// B, ack ==> C
// C, ack ==> B
// B, ack ==> C
// C, ack ==> B
// undefined combo
// B, off ==> A
// A, off ==> B