/*
  CoreLinux++ 
  Copyright (C) 2000 CoreLinux Consortium
  
   The CoreLinux++ Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The CoreLinux++ Library Library is distributed in the hope that it will 
   be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  
*/   

/** \example examp18.cpp
   This example is to show use of the Command pattern. There are
   three(3) self documenting tests that are run:
   
   1. Create a command which converts a mixed case string to
      upper case.
   2. Same as #1 but add a reverse command and intentionaly
      undo the change.
   3. Add the command and reverse and a little nasty suprise
      to force the CommandFrame to roll-back the commands
      automatically because of a problem in the transaction.
            
*/                   


#include <corelinux/Common.hpp>

#if   !defined(__COMMANDFRAME__HPP)
#include <corelinux/CommandFrame.hpp>
#endif

#include <UpperCaseCommand.hpp>
#include <RestoreCaseCommand.hpp>

using namespace corelinux;
using namespace std;

#include <iostream>
#include <exception>

//
// In module function prototypes
//

int   main( void );

//
// General Functions 
//

void  doTestOne( void );
void  doTestTwo( void );
void  doTestThree( void );

void  handleAssertion( AssertionCref );
void  handleException( ExceptionCref );

//
// Global data
//

//
// Main entry point
//


int main( void )
{

   cout << endl;

   //
   // Practice graceful exception management
   //


   try
   {
      //
      // Run the various tests
      //

      doTestOne();
      doTestTwo();
      doTestThree();

   }

   catch( CommandFrameException aException )
   {
      cerr  << "Received CommandFrameException!" << endl;
      handleException(aException);
   }
   catch( AssertionRef aAssert )
   {
      handleAssertion(aAssert);
   }
   catch( ExceptionRef aException )
   {
      handleException(aException);
   }
   catch( std::exception & e )
   {
      cerr  << e.what() << endl;
   }
   catch( ... )
   {
      cerr  << "Unknown exception." << endl;
   }

   return 0;               
}

//
// Some utility functions
//

CommandFramePtr   createTransaction( bool autoMode = true );
void              destroyTransaction( CommandFramePtr );
void              displayResults( CommandFramePtr );
CommandPtr        createUpperCommand( void );
void              cleanupCommands( CommandFramePtr );

// Test One Simple

void  doTestOne( void )
{
   cout << endl;
   cout << "With the first test we create a command that " << endl;
   cout << "converts a string to upper case." << endl;
   cout << endl;

   CommandFramePtr   aTransaction(NULLPTR);

   try
   {
      aTransaction = createTransaction( false );
      aTransaction->addCommand( createUpperCommand() );
      aTransaction->execute();
      displayResults( aTransaction );
      destroyTransaction( aTransaction );
   }
   catch( ExceptionRef aExcp )
   {
      if( aTransaction != NULLPTR )
      {
         destroyTransaction( aTransaction );
      }
      else
      {
         ;  // do nothing
      }
      throw;
   }


}

CommandPtr        createUpperAndReverseCommand( void );

// Test two (imagine a undo stack!!!

void  doTestTwo( void )
{
   cout << endl;
   cout << "With the next test we create a command that " << endl;
   cout << "converts a string to upper case. We also add " << endl;
   cout << "an undo command, we execute and then undo the " << endl;
   cout << "change displaying as we go. THINK UNDO STACK!!! " << endl;
   cout << endl;

   CommandFramePtr   aTransaction(NULLPTR);

   try
   {
      aTransaction = createTransaction( true );
      aTransaction->addCommand( createUpperAndReverseCommand() );
      aTransaction->execute();
      displayResults( aTransaction );
      aTransaction->executeReverse();
      displayResults( aTransaction );
      destroyTransaction( aTransaction );
   }
   catch( ExceptionRef aExcp )
   {
      if( aTransaction != NULLPTR )
      {
         destroyTransaction( aTransaction );
      }
      else
      {
         ;  // do nothing
      }
      throw;
   }
}

// Bogus run on purpose!

//
// The nasty ugly that excercises the CommandFrame
// rollback
//

class UglyCommand : public Command
{
public:
               UglyCommand( void )
                  :
                  Command()
               {
                  ;  // do nothing
               }

   virtual     ~UglyCommand( void )
   {
      ;  // do nothing
   }
   virtual void execute( void )
   {
      throw Exception("Candy gram for Mungo",LOCATION);
   }
};

void  doTestThree( void )
{
   cout << endl;
   cout << "In this test, we put a nasty little command that " << endl;
   cout << "forces the CommandFrame to rollback itself AFTER " << endl;
   cout << "the UpperCaseCommand. The resulting output should " << endl;
   cout << "show that the original string is restored." << endl;
   cout << endl;

   CommandFramePtr   aTransaction(NULLPTR);

   try
   {
      aTransaction = createTransaction( true );
      aTransaction->addCommand( createUpperAndReverseCommand() );
      aTransaction->addCommand( new UglyCommand );
      aTransaction->execute();
      displayResults( aTransaction );
      destroyTransaction( aTransaction );
   }
   catch( ExceptionRef aExcp )
   {
      if( aTransaction != NULLPTR )
      {
         destroyTransaction( aTransaction );
      }
      else
      {
         ;  // do nothing
      }
      throw;
   }
}

//
// Allocate a CommandFrame
//

CommandFramePtr   createTransaction( bool autoMode )
{
   return new CommandFrame( autoMode );
}

//
// Destroy the command frame
//

void destroyTransaction( CommandFramePtr aTransaction )
{
   cleanupCommands( aTransaction );
   delete aTransaction;
}

//
// Iterate the results
//

void displayResults( CommandFramePtr aTransaction )
{
   REQUIRE( aTransaction != NULLPTR );

   Commands    aCollection;
   WorkState   aRunFlag( aTransaction->getState() );
   
   cout << "Transaction state = " <<
      ( aRunFlag == COMPLETED ? "Success" :
         ( aRunFlag == REVERSED ? "Rolled back" : "Invalid" ) ) << endl;
            

   aTransaction->getCommands(aCollection);

   CommandsIterator  begin( aCollection.begin() );

   while( begin != aCollection.end() )
   {
      if( dynamic_cast<UpperCaseCommandPtr>((*begin)) != NULLPTR )
      {
         cout << "Upper case results = " << 
            dynamic_cast<UpperCaseCommandPtr>((*begin))->getNewValue() << endl;
      }
      ++begin;
   }

   cout << endl;
   
}

//
// Create the to UpperCase command
//

CommandPtr  createUpperCommand( void )
{
   string   aString;

   cout  << "Enter a mixed case string : ";
   cin >> aString;

   return new UpperCaseCommand( aString );
}

//
// Create the Upper command and the ability
// to restore it.
//

CommandPtr  createUpperAndReverseCommand( void )
{
   CommandPtr  baseCmd( createUpperCommand() );
   CHECK( baseCmd != NULLPTR );

   CommandPtr  undoCmd( new RestoreCaseCommand );
   CHECK( undoCmd != NULLPTR );

   baseCmd->setReverseCommand( undoCmd );

   return baseCmd;
}
//
// Iterates through the transaction and deletes
// the commands and reverse commands
//

void  cleanupCommands( CommandFramePtr aTransaction )
{
   REQUIRE( aTransaction != NULLPTR );

   Commands aCollection;
   
   aTransaction->getCommands(aCollection);

   CommandsIterator  begin( aCollection.begin() );

   while( begin != aCollection.end() )
   {
      CommandPtr  aCmdPtr( static_cast<CommandPtr>(*begin) );
      if( aCmdPtr->getReverseCommand() != NULLPTR )
      {
         delete aCmdPtr->getReverseCommand();
         aCmdPtr->setReverseCommand( NULLPTR );
      }
      else
      {
         ;  // do nothing
      }

      delete aCmdPtr;
      ++begin;
   }
}

//
// Error handlers
//

void  handleAssertion( AssertionCref aAssert )
{
   cerr << aAssert.getFile() << ":" << aAssert.getLine() << ":" << 
      "Assertion: ";

   if( aAssert.getType() == Assertion::NEVERGETHERE )
   {
      cerr << "NEVER_GET_HERE";
   }
   else
   {
      if( aAssert.getType() == Assertion::REQUIRE )
      {
         cerr  << "REQUIRE";
      }
      else if( aAssert.getType() == Assertion::ENSURE )
      {
         cerr  << "ENSURE";
      }
      else if( aAssert.getType() == Assertion::CHECK )
      {
         cerr  << "CHECK";
      }
      else 
      {
         cerr  << "ASSERT";
      }
      cerr << "( " << aAssert.getWhy() << " )";
   }

   cerr << endl;
}

void  handleException( ExceptionCref aExcp )
{
   cerr << aExcp.getFile() << ":" << aExcp.getLine() << ":" <<
      "Exception: " << aExcp.getWhy() << endl;
}

/*
   Common rcs information do not modify
   $Author: prudhomm $
   $Revision: 1.2 $
   $Date: 2000/08/31 22:49:02 $
   $Locker:  $
*/


