/*
 * Finite State Machine using XML4C/SAXParser (IBM XML Parser for C++ 3.0.1)
 */

#include <iostream>
#include <set>
#include <string>

using namespace std;

#include <util/PlatformUtils.hpp>    // PlatformUtils
#include <parsers/SAXParser.hpp>     // SAXParser
#include <sax/HandlerBase.hpp>       // HandlerBase
#include <sax/SaxParseException.hpp> // SaxParseException

/*
 * UNICODEからstringへの変換
 */
inline string transcode(const XMLCh* xmlch) {
  return XMLString::transcode(xmlch);
}

/*
 * Event
 */
struct Event {
  string name;
  string action;
  string transition;
  Event(const string& n) : name(n) {}
  Event(const string& n, const string& a, const string& t)
    : name(n), action(a), transition(t) {}
};

inline bool operator<(const Event& x, const Event& y) {
  return x.name < y.name;
}

typedef set<Event> Events;
typedef set<string> EventNames;

/*
 * State
 */
struct State {
  string name;
  Events events;
  State() {}
  State(const string& n) : name(n) {}
};

inline bool operator<(const State& x, const State& y) {
  return x.name < y.name;
}

typedef set<State> States;

/*
 * FSM
 */
struct FSM {
  string name;
  string initial;
  string terminal;
  States states;
  EventNames event_names;
};

/*
 * XMLからFSMを構築するためのハンドラ
 */
class Handler : public HandlerBase {
  State current_state;
  FSM&  fsm;
public:

  Handler(FSM& f) : fsm(f) {}

  // DocumentHandler

  // エレメント(タグ)の開始
  virtual void startElement(const XMLCh* const tag_name, AttributeList& attrs) {
    string tag = transcode(tag_name);
    if ( tag == "FSM" ) {
      fsm.name     = transcode(attrs.getValue(L"name"));
      fsm.initial  = transcode(attrs.getValue(L"initial"));
      fsm.terminal = transcode(attrs.getValue(L"terminal"));
    } else
    if ( tag == "State" ) {
      current_state.name = transcode(attrs.getValue(L"name"));
      current_state.events.clear();
    } else
    if ( tag == "Event" ) {
      current_state.events.insert(Event(transcode(attrs.getValue(L"name")),
                                        transcode(attrs.getValue(L"action")),
                                        transcode(attrs.getValue(L"transition"))));
      fsm.event_names.insert(transcode(attrs.getValue(L"name")));
    }
  }

  // エレメント(タグ)の終了
  virtual void endElement(const XMLCh* const tag_name) {
    string tag = transcode(tag_name);
    if ( tag == "State" ) {
      fsm.states.insert(current_state);
    }
  }

  // ErrorHandler

  virtual void warning(const SAXParseException& exception) {
    cout << "Err::warning";
    cout << transcode(exception.getMessage()) << endl;
  }

  virtual void error(const SAXParseException& exception) {
    cout << "Err::error:";
    cout << transcode(exception.getMessage()) << endl;
  }

  virtual void fatalError(const SAXParseException& exception) {
    cout << "Err::fatalError:";
    cout << transcode(exception.getMessage()) << endl;
  }

};

/*
 * ----- main -----
 */
int main(int argc, char* argv[]) {

  FSM fsm;

  /*
   * 初期化とFSMの構築
   */
  try {
    XMLPlatformUtils::Initialize();
    SAXParser parser;
    Handler   handler(fsm);

    parser.setDoValidation(true);
    parser.setDocumentHandler(&handler);
    parser.setErrorHandler(&handler);

    parser.parse(argv[1]);

  } catch ( const XMLException& er ) {
    cout << er.getMessage() << endl;
  }

  /*
   * 初期状態 / 終了状態 を取得する
   */
  string state_name     = fsm.initial;
  string terminal_name  = fsm.terminal;

  string fsm_name       = fsm.name;
  cout << "FSM : " << fsm_name << endl;

  /*
   * 状態遷移表を駆動する
   */
  while ( state_name != terminal_name ) {

   /*
    * state_name で示される State を見つける
    */
    States::const_iterator sit = fsm.states.find(State(state_name));
    if ( sit == fsm.states.end() ) {
      cerr << "invalid state : " << state_name << endl;
      break;
    }

    /*
     * 現在の状態とイベント一覧を出力する
     */
    cout << "state : " << state_name << endl;
    cout << "enter an event (";
    copy(fsm.event_names.begin(), fsm.event_names.end(), ostream_iterator<string>(cout,","));
    cout << "exit) ? " << flush;

    /*
     * ユーザからのイベント入力
     */
    string event_name;
    cin >> event_name;
    if ( event_name == "exit" )
      break;
    if ( fsm.event_names.find(event_name) == fsm.event_names.end() ) {
      cerr << event_name << " is not valid." << endl;
      continue;
    }

   /*
    * event_name で示される Event を見つける
    */

    Events::const_iterator eit = sit->events.find(Event(event_name));
    if ( eit == sit->events.end() ) {
      cerr << "invalid event : " << event_name << endl;
      break;
    }

   /*
    * アクションを実行する
    */
    string action = eit->action;
    if ( action != "none" )
      cout << "action : " << action << endl;

   /*
    * 次の状態へ遷移する
    */
    string transition = eit->transition;
    if ( transition != "none" )
      state_name = transition;

  }

  return 0;
}

