//////////////////////////////////////////////////////////////////////////////
// Copyright 2004-2007 Andreas Huber Doenni
// Distributed under the Boost Software License, Version 1.0. (See accompany-
// ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//////////////////////////////////////////////////////////////////////////////



#include "OuterOrthogonal.hpp"
#include "InnermostDefault.hpp"

#include <boost/statechart/state_machine.hpp>
#include <boost/statechart/null_exception_translator.hpp>
#include <boost/statechart/exception_translator.hpp>
#include <boost/statechart/event.hpp>
#include <boost/statechart/transition.hpp>
#include <boost/statechart/custom_reaction.hpp>

#include <boost/mpl/list.hpp>

#include <boost/test/test_tools.hpp>

#include <typeinfo>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <stdexcept>



namespace sc = boost::statechart;
namespace mpl = boost::mpl;



typedef std::string ActionDescription();
typedef ActionDescription * ActionDescriptionPtr;
typedef std::vector< ActionDescriptionPtr > ActionDescriptionSequence;
typedef ActionDescriptionSequence::const_iterator SequenceIterator;
typedef void Action( ActionDescriptionSequence & );
typedef Action * ActionPtr;

template< class State >
std::string EntryDescription()
{
  static const std::string entry = "Entry: ";
  return entry + typeid( State ).name() + "\n";
}

template< class State >
std::string ExitFnDescription()
{
  static const std::string exitFunction = "exit(): ";
  return exitFunction + typeid( State ).name() + "\n";
}

template< class State >
std::string DtorDescription()
{
  static const std::string destructor = "Destructor: ";
  return destructor + typeid( State ).name() + "\n";
}

template< class Context, class Event >
std::string TransDescription()
{
  static const std::string transition = "Transition: ";
  static const std::string event = " with Event: ";
  return transition + typeid( Context ).name() +
    event + typeid( Event ).name() + "\n";
}

template< ActionPtr pAction >
std::string ThrowDescription()
{
  static const std::string throwing = "Throwing exception in ";
  ActionDescriptionSequence sequence;
  pAction( sequence );
  return throwing + sequence.front()();
}


template< class State >
void Entry( ActionDescriptionSequence & sequence )
{
  sequence.push_back( &EntryDescription< State > );
}

template< class State >
void ExitFn( ActionDescriptionSequence & sequence )
{
  sequence.push_back( &ExitFnDescription< State > );
}

template< class State >
void Dtor( ActionDescriptionSequence & sequence )
{
  sequence.push_back( &DtorDescription< State > );
}

template< class State >
void Exit( ActionDescriptionSequence & sequence )
{
  ExitFn< State >( sequence );
  Dtor< State >( sequence );
}

template< class Context, class Event >
void Trans( ActionDescriptionSequence & sequence )
{
  sequence.push_back( &TransDescription< Context, Event > );
}

template< ActionPtr pAction >
void Throw( ActionDescriptionSequence & sequence )
{
  sequence.push_back( &ThrowDescription< pAction > );
}

const int arrayLength = 30;
typedef ActionPtr ActionArray[ arrayLength ];


class TransitionTestException : public std::runtime_error
{
  public:
    TransitionTestException() : std::runtime_error( "Oh la la!" ) {}
};


// This test state machine is a beefed-up version of the one presented in
// "Practical Statecharts in C/C++" by Miro Samek, CMP Books 2002
struct A : sc::event< A > {};
struct B : sc::event< B > {};
struct C : sc::event< C > {};
struct D : sc::event< D > {};
struct E : sc::event< E > {};
struct F : sc::event< F > {};
struct G : sc::event< G > {};
struct H : sc::event< H > {};


template< class M > struct S0;
template< class Translator >
struct TransitionTest : sc::state_machine<
  TransitionTest< Translator >, S0< TransitionTest< Translator > >,
  std::allocator< sc::none >, Translator >
{
  public:
    //////////////////////////////////////////////////////////////////////////
    TransitionTest() : pThrowAction_( 0 ), unconsumedEventCount_( 0 ) {}

    ~TransitionTest()
    {
      // Since state destructors access the state machine object, we need to
      // make sure that all states are destructed before this subtype
      // portion is destructed.
      this->terminate();
    }

    void CompareToExpectedActionSequence( ActionArray & actions )
    {
      expectedSequence_.clear();

      // Copy all non-null pointers in actions into expectedSequence_
      for ( ActionPtr * pCurrent = &actions[ 0 ];
        ( pCurrent != &actions[ arrayLength ] ) && ( *pCurrent != 0 );
        ++pCurrent )
      {
        ( *pCurrent )( expectedSequence_ );
      }

      if ( ( expectedSequence_.size() != actualSequence_.size() ) ||
        !std::equal( expectedSequence_.begin(),
          expectedSequence_.end(), actualSequence_.begin() ) )
      {
        std::string message = "\nExpected action sequence:\n";

        for ( SequenceIterator pExpected = expectedSequence_.begin();
          pExpected != expectedSequence_.end(); ++pExpected )
        {
          message += ( *pExpected )();
        }

        message += "\nActual action sequence:\n";

        for ( SequenceIterator pActual = actualSequence_.begin();
          pActual != actualSequence_.end(); ++pActual )
        {
          message += ( *pActual )();
        }

        BOOST_FAIL( message.c_str() );
      }

      actualSequence_.clear();
    }

    void ClearActualSequence()
    {
      actualSequence_.clear();
    }

    void ThrowAction( ActionPtr pThrowAction )
    {
      pThrowAction_ = pThrowAction;
    }

    template< class State >
    void ActualEntry()
    {
      StoreActualAction< &Entry< State > >();
    }

    template< class State >
    void ActualExitFunction()
    {
      StoreActualAction< &ExitFn< State > >();
    }
    
    template< class State >
    void ActualDestructor()
    {
      StoreActualAction< &Dtor< State > >();
    }
    
    template< class Context, class Event >
    void ActualTransition()
    {
      StoreActualAction< &Trans< Context, Event > >();
    }

    void unconsumed_event( const sc::event_base & )
    {
      ++unconsumedEventCount_;
    }

    unsigned int GetUnconsumedEventCount() const
    {
      return unconsumedEventCount_;
    }

  private:
    //////////////////////////////////////////////////////////////////////////
    template< ActionPtr pAction >
    void StoreActualAction()
    {
      if ( pAction == pThrowAction_ )
      {
        Throw< pAction >( actualSequence_ );
        throw TransitionTestException();
      }
      else
      {
        pAction( actualSequence_ );
      }
    }

    ActionPtr pThrowAction_;
    ActionDescriptionSequence actualSequence_;
    ActionDescriptionSequence expectedSequence_;
    unsigned int unconsumedEventCount_;
};

template< class M > struct S1;
template< class M > struct S211;
template< class M >
struct S0 : Orthogonal0< S0< M >, M, S1< M > >
{
  typedef Orthogonal0< S0< M >, M, S1< M > > my_base;
  public:
    typedef sc::transition< E, S211< M > > reactions;

    S0( typename my_base::my_context ctx ) : my_base( ctx ) {}

    void Transit( const A & evt ) { TransitImpl( evt ); }
    void Transit( const B & evt ) { TransitImpl( evt ); }
    void Transit( const C & evt ) { TransitImpl( evt ); }
    void Transit( const D & evt ) { TransitImpl( evt ); }
    void Transit( const F & evt ) { TransitImpl( evt ); }
    void Transit( const G & evt ) { TransitImpl( evt ); }
    void Transit( const H & evt ) { TransitImpl( evt ); }

  private:
    template< class Event >
    void TransitImpl( const Event & )
    {
      this->outermost_context().template ActualTransition< S0< M >, Event >();
    }
};

  template< class M > struct S11;
  template< class M > struct S21;
  template< class M >
  struct S2 : Orthogonal2< S2< M >, S0< M >, S21< M > >
  {
    typedef Orthogonal2< S2< M >, S0< M >, S21< M > > my_base;
    typedef mpl::list<
      sc::transition< C, S1< M >, S0< M >, &S0< M >::Transit >,
      sc::transition< F, S11< M >, S0< M >, &S0< M >::Transit >
    > reactions;

    S2( typename my_base::my_context ctx ) : my_base( ctx ) {}
  };

    template< class M >
    struct S21 : Orthogonal1<
      S21< M >, typename S2< M >::template orthogonal< 2 >, S211< M > >
    {
      typedef Orthogonal1<
        S21< M >, typename S2< M >::template orthogonal< 2 >, S211< M >
      > my_base;
      typedef mpl::list<
        sc::transition< H, S21< M >, S0< M >, &S0< M >::Transit >,
        sc::transition< B, S211< M >, S0< M >, &S0< M >::Transit >
      > reactions;

      S21( typename my_base::my_context ctx ) : my_base( ctx ) {}
    };

      template< class M >
      struct S211 : InnermostDefault<
        S211< M >, typename S21< M >::template orthogonal< 1 > >
      {
        typedef InnermostDefault<
          S211< M >, typename S21< M >::template orthogonal< 1 > > my_base;
        typedef mpl::list<
          sc::transition< D, S21< M >, S0< M >, &S0< M >::Transit >,
          sc::transition< G, S0< M > >
        > reactions;

        S211( typename my_base::my_context ctx ) : my_base( ctx ) {}
      };

  template< class M >
  struct S1 : Orthogonal1< S1< M >, S0< M >, S11< M > >
  {
    typedef Orthogonal1< S1< M >, S0< M >, S11< M > > my_base;
    typedef mpl::list<
      sc::transition< A, S1< M >, S0< M >, &S0< M >::Transit >,
      sc::transition< B, S11< M >, S0< M >, &S0< M >::Transit >,
      sc::transition< C, S2< M >, S0< M >, &S0< M >::Transit >,
      sc::transition< D, S0< M > >,
      sc::transition< F, S211< M >, S0< M >, &S0< M >::Transit >
    > reactions;

    S1( typename my_base::my_context ctx ) : my_base( ctx ) {}
  };

    template< class M >
    struct S11 : InnermostDefault<
      S11< M >, typename S1< M >::template orthogonal< 1 > >
    {
      typedef InnermostDefault<
        S11< M >, typename S1< M >::template orthogonal< 1 > > my_base;
      typedef mpl::list<
        sc::transition< G, S211< M >, S0< M >, &S0< M >::Transit >,
        sc::custom_reaction< H >
      > reactions;

      S11( typename my_base::my_context ctx ) : my_base( ctx ) {}

      sc::result react( const H & )
      {
        this->outermost_context().template ActualTransition< S11< M >, H >();
        return this->discard_event();
      }
    };


struct X1;
struct TransitionEventBaseTest :
  sc::state_machine< TransitionEventBaseTest, X1 >
{
  public:
    TransitionEventBaseTest() : actionCallCounter_( 0 ) {}

    void Transit( const sc::event_base & eventBase )
    {
      BOOST_REQUIRE(
        ( dynamic_cast< const B * >( &eventBase ) != 0 ) ||
        ( dynamic_cast< const D * >( &eventBase ) != 0 ) );
      ++actionCallCounter_;
    }

    unsigned int GetActionCallCounter() const
    {
      return actionCallCounter_;
    }

  private:
    unsigned int actionCallCounter_;
};

struct X2 : sc::simple_state< X2, TransitionEventBaseTest >
{
  typedef sc::transition< sc::event_base, X1,
    TransitionEventBaseTest, &TransitionEventBaseTest::Transit > reactions;
};

struct X1 : sc::simple_state< X1, TransitionEventBaseTest >
{
  typedef sc::transition< sc::event_base, X2 > reactions;
};

template< class M >
void TestTransitions( M & machine )
{
  machine.initiate();
  ActionArray init = 
  {
    Entry< S0< M > >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >
  };
  machine.CompareToExpectedActionSequence( init );

  machine.process_event( A() );
  ActionArray a1 =
  {
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Trans< S0< M >, A >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >
  };
  machine.CompareToExpectedActionSequence( a1 );

  machine.process_event( B() );
  ActionArray b1 =
  {
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Trans< S0< M >, B >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >
  };
  machine.CompareToExpectedActionSequence( b1 );

  machine.process_event( C() );
  ActionArray c1 =
  {
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Trans< S0< M >, C >,
    Entry< S2< M > >,
    Entry< Default0< S2< M > > >,
    Entry< Default1< S2< M > > >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >
  };
  machine.CompareToExpectedActionSequence( c1 );

  machine.process_event( D() );
  ActionArray d2 =
  {
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Trans< S0< M >, D >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >
  };
  machine.CompareToExpectedActionSequence( d2 );

  machine.process_event( E() );
  ActionArray e2 =
  {
    Exit< Default2< S0< M > > >,
    Exit< Default1< S0< M > > >,
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Exit< Default1< S2< M > > >,
    Exit< Default0< S2< M > > >,
    Exit< S2< M > >,
    Exit< S0< M > >,
    Entry< S0< M > >,
    Entry< S2< M > >,
    Entry< Default0< S2< M > > >,
    Entry< Default1< S2< M > > >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >
  };
  machine.CompareToExpectedActionSequence( e2 );

  machine.process_event( F() );
  ActionArray f2 =
  {
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Exit< Default1< S2< M > > >,
    Exit< Default0< S2< M > > >,
    Exit< S2< M > >,
    Trans< S0< M >, F >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >
  };
  machine.CompareToExpectedActionSequence( f2 );

  machine.process_event( G() );
  ActionArray g1 =
  {
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Trans< S0< M >, G >,
    Entry< S2< M > >,
    Entry< Default0< S2< M > > >,
    Entry< Default1< S2< M > > >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >
  };
  machine.CompareToExpectedActionSequence( g1 );

  machine.process_event( H() );
  ActionArray h2 =
  {
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Trans< S0< M >, H >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >
  };
  machine.CompareToExpectedActionSequence( h2 );

  BOOST_REQUIRE( machine.GetUnconsumedEventCount() == 0 );
  machine.process_event( A() );
  BOOST_REQUIRE( machine.GetUnconsumedEventCount() == 1 );
  ActionArray a2 =
  {
  };
  machine.CompareToExpectedActionSequence( a2 );

  machine.process_event( B() );
  ActionArray b2 =
  {
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Trans< S0< M >, B >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >
  };
  machine.CompareToExpectedActionSequence( b2 );

  machine.process_event( C() );
  ActionArray c2 =
  {
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Exit< Default1< S2< M > > >,
    Exit< Default0< S2< M > > >,
    Exit< S2< M > >,
    Trans< S0< M >, C >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >
  };
  machine.CompareToExpectedActionSequence( c2 );

  machine.process_event( D() );
  ActionArray d1 = 
  {
    Exit< Default2< S0< M > > >,
    Exit< Default1< S0< M > > >,
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Exit< S0< M > >,
    Entry< S0< M > >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >
  };
  machine.CompareToExpectedActionSequence( d1 );

  machine.process_event( F() );
  ActionArray f1 =
  {
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Trans< S0< M >, F >,
    Entry< S2< M > >,
    Entry< Default0< S2< M > > >,
    Entry< Default1< S2< M > > >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >
  };
  machine.CompareToExpectedActionSequence( f1 );

  machine.process_event( G() );
  ActionArray g2 =
  {
    Exit< Default2< S0< M > > >,
    Exit< Default1< S0< M > > >,
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Exit< Default1< S2< M > > >,
    Exit< Default0< S2< M > > >,
    Exit< S2< M > >,
    Exit< S0< M > >,
    Entry< S0< M > >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >
  };
  machine.CompareToExpectedActionSequence( g2 );

  machine.process_event( H() );
  ActionArray h1 =
  {
    Trans< S11< M >, H >
  };
  machine.CompareToExpectedActionSequence( h1 );

  machine.process_event( E() );
  ActionArray e1 =
  {
    Exit< Default2< S0< M > > >,
    Exit< Default1< S0< M > > >,
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Exit< S0< M > >,
    Entry< S0< M > >,
    Entry< S2< M > >,
    Entry< Default0< S2< M > > >,
    Entry< Default1< S2< M > > >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    Entry< S211< M > >,
    Entry< Default2< S21< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >
  };
  machine.CompareToExpectedActionSequence( e1 );

  machine.terminate();
  ActionArray term =
  {
    Exit< Default2< S0< M > > >,
    Exit< Default1< S0< M > > >,
    Exit< Default2< S21< M > > >,
    Exit< S211< M > >,
    Exit< Default0< S21< M > > >,
    Exit< S21< M > >,
    Exit< Default1< S2< M > > >,
    Exit< Default0< S2< M > > >,
    Exit< S2< M > >,
    Exit< S0< M > >
  };
  machine.CompareToExpectedActionSequence( term );

  machine.ThrowAction( &Entry< Default0< S1< M > > > );
  BOOST_REQUIRE_THROW( machine.initiate(), TransitionTestException );
  ActionArray initThrow1 = 
  {
    Entry< S0< M > >,
    Entry< S1< M > >,
    &::Throw< &::Entry< Default0< S1< M > > > >,
    Dtor< S1< M > >,
    Dtor< S0< M > >
  };
  machine.CompareToExpectedActionSequence( initThrow1 );
  BOOST_REQUIRE( machine.terminated() );

  machine.ThrowAction( &Entry< S11< M > > );
  BOOST_REQUIRE_THROW( machine.initiate(), TransitionTestException );
  ActionArray initThrow2 = 
  {
    Entry< S0< M > >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    &::Throw< &::Entry< S11< M > > >,
    Dtor< Default0< S1< M > > >,
    Dtor< S1< M > >,
    Dtor< S0< M > >
  };
  machine.CompareToExpectedActionSequence( initThrow2 );
  BOOST_REQUIRE( machine.terminated() );

  machine.ThrowAction( &Trans< S0< M >, A > );
  machine.initiate();
  BOOST_REQUIRE_THROW( machine.process_event( A() ), TransitionTestException );
  ActionArray a1Throw1 =
  {
    Entry< S0< M > >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >,
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    &::Throw< &::Trans< S0< M >, A > >,
    Dtor< Default2< S0< M > > >,
    Dtor< Default1< S0< M > > >,
    Dtor< S0< M > >
  };
  machine.CompareToExpectedActionSequence( a1Throw1 );
  BOOST_REQUIRE( machine.terminated() );

  machine.ThrowAction( &Entry< S211< M > > );
  machine.initiate();
  BOOST_REQUIRE_THROW( machine.process_event( C() ), TransitionTestException );
  ActionArray c1Throw1 =
  {
    Entry< S0< M > >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >,
    Exit< Default2< S1< M > > >,
    Exit< S11< M > >,
    Exit< Default0< S1< M > > >,
    Exit< S1< M > >,
    Trans< S0< M >, C >,
    Entry< S2< M > >,
    Entry< Default0< S2< M > > >,
    Entry< Default1< S2< M > > >,
    Entry< S21< M > >,
    Entry< Default0< S21< M > > >,
    &::Throw< &::Entry< S211< M > > >,
    Dtor< Default2< S0< M > > >,
    Dtor< Default1< S0< M > > >,
    Dtor< Default0< S21< M > > >,
    Dtor< S21< M > >,
    Dtor< Default1< S2< M > > >,
    Dtor< Default0< S2< M > > >,
    Dtor< S2< M > >,
    Dtor< S0< M > >
  };
  machine.CompareToExpectedActionSequence( c1Throw1 );
  BOOST_REQUIRE( machine.terminated() );

  machine.ThrowAction( &ExitFn< S11< M > > );
  machine.initiate();
  BOOST_REQUIRE_THROW( machine.process_event( C() ), TransitionTestException );
  ActionArray c1Throw2 =
  {
    Entry< S0< M > >,
    Entry< S1< M > >,
    Entry< Default0< S1< M > > >,
    Entry< S11< M > >,
    Entry< Default2< S1< M > > >,
    Entry< Default1< S0< M > > >,
    Entry< Default2< S0< M > > >,
    Exit< Default2< S1< M > > >,
    &::Throw< &::ExitFn< S11< M > > >,
    Dtor< S11< M > >,
    Dtor< Default2< S0< M > > >,
    Dtor< Default1< S0< M > > >,
    Dtor< Default0< S1< M > > >,
    Dtor< S1< M > >,
    Dtor< S0< M > >
  };
  machine.CompareToExpectedActionSequence( c1Throw2 );
  BOOST_REQUIRE( machine.terminated() );
  BOOST_REQUIRE( machine.GetUnconsumedEventCount() == 1 );
}


int test_main( int, char* [] )
{
  TransitionTest< sc::null_exception_translator > null_machine;
  TestTransitions( null_machine );
  TransitionTest< sc::exception_translator<> > machine;
  TestTransitions( machine );

  TransitionEventBaseTest eventBaseMachine;
  eventBaseMachine.initiate();
  BOOST_REQUIRE_NO_THROW( eventBaseMachine.state_cast< const X1 & >() );
  eventBaseMachine.process_event( A() );
  BOOST_REQUIRE_NO_THROW( eventBaseMachine.state_cast< const X2 & >() );
  BOOST_REQUIRE( eventBaseMachine.GetActionCallCounter() == 0 );
  eventBaseMachine.process_event( B() );
  BOOST_REQUIRE_NO_THROW( eventBaseMachine.state_cast< const X1 & >() );
  BOOST_REQUIRE( eventBaseMachine.GetActionCallCounter() == 1 );
  eventBaseMachine.process_event( C() );
  BOOST_REQUIRE_NO_THROW( eventBaseMachine.state_cast< const X2 & >() );
  BOOST_REQUIRE( eventBaseMachine.GetActionCallCounter() == 1 );
  eventBaseMachine.process_event( D() );
  BOOST_REQUIRE_NO_THROW( eventBaseMachine.state_cast< const X1 & >() );
  BOOST_REQUIRE( eventBaseMachine.GetActionCallCounter() == 2 );

  return 0;
}
