// file      : Example/ExH/HelloWorld/hello_world.cpp
// author    : Boris Kolpackov <boris@kolpackov.net>
// copyright : Copyright (c) 2002-2003 Boris Kolpackov
// license   : http://kolpackov.net/license.html

#include <cstdlib> // for std::abort ()

#include <string>
#include <iostream>

#include "Utility/ExH/System/Exception.hpp"
#include "Utility/ExH/Logic/Exception.hpp"

using std::cerr;
using std::cout;
using std::endl;

using namespace Utility;

class Application
{
public:
  class Exception : public ExH::Logic::Exception {};

  // Hint: you may want to try again...
  class FeelingDizzy  : public Exception {};

  class InvalidArg : public Exception {};

public:
  Application () throw (ExH::System::Exception)
      : // The std::string c-tor may throw any kind of exceptions besides
        // quite possible std::bad_alloc.
        greeting_ ("Hello, world!")
  {
  }

  Application (char const * greeting) throw (InvalidArg,
                                             ExH::System::Exception)
      : greeting_ (greeting == 0 ? "" : greeting)
  {
    if (greeting == 0) throw InvalidArg ();
  }

public:

  void
  run () throw (FeelingDizzy, ExH::System::Exception)
  {
    static unsigned int dizzy_count (0);

    if (dizzy_count++ < 5) throw FeelingDizzy ();

    // The next line can throw full bucket of exceptions
    // not to mention ios_base::failure.
    cout << greeting_.c_str () << endl;
  }

private:

  std::string  greeting_;
};



int
main ()
{
  // This is a catch-all layer that should be in use only
  // if we are really in trouble.
  try
  {
    // This is a catch-system layer. Here we will catch exceptions like
    // bad_alloc, etc. If we get here it means that nobody wanted/managed
    // to recover from this kind of errors.
    try
    {
      // This is a catch-logic layer. If we get here it usually
      // indicates an application logic error.
      try
      {

        // Ok, here we go about our application logic.
        try
        {
          for (int i = 0; i < 10; i++)
          {
            try
            {
              Application app ("Hi dude!");
              app.run ();
              break;
            }
            catch (Application::FeelingDizzy const& )
            {
              if (i == 9)
              {
                cerr << "Given up!" << endl;
                return -1;
              }
              else
              {
                cerr << "Application is feeling dizzy. Trying again..."
                     << endl;
              }
            }
          }
        }
        catch (Application::InvalidArg const& )
        {
          cerr << "Cought Application::InvalidArg : ...hmm... strange!"
               << endl;
          return -1;
        }
      }
      catch (ExH::Logic::Exception const& e)
      {
        cerr << "Caught Logic::Exception : " << e.what () << endl;
        return -1;
      }
    }
    catch (const ExH::System::Exception& e)
    {
      cerr << "Caught System::Exception : " << e.what () << endl;
      return -1;
    }
    catch (...)
    {
      cerr << "Caught unknown exception using catch-all handler. " << endl;
      return -1;
    }
  }
  catch (...)
  {
    // We get here in cases of some hard failure. For example when handling
    // exception, operator << throws another exception. Usually application
    // cannot handle such failures itself so we just propagate it futher.
    std::abort ();
  }
}