/// \ingroup rbd_common
///@{

/// \file myexcept.cpp
/// Exception handler.
/// The low level classes for
/// - my exception class hierarchy
/// - the functions needed for my simulated exceptions
/// - the Tracer mechanism
/// - routines for checking whether new and delete calls are balanced
///

// Copyright (C) 1993,4,6: R B Davies


#define WANT_STREAM                    // include.h will get stream fns
#define WANT_STRING

#include "include.h"                   // include standard files


#include "myexcept.h"                  // for exception handling
#include "newmat.h"                  // for exception handling

#ifdef use_namespace
namespace RBD_COMMON {
#endif

#ifdef USE_STD_NAMESPACE
using namespace std;
#endif

//#define REG_DEREG                    // for print out uses of new/delete
//#define CLEAN_LIST                   // to print entries being added to
                                       // or deleted from cleanup list

#ifdef SimulateExceptions

void Throw()
{
   for (Janitor* jan = JumpBase::jl->janitor; jan; jan = jan->NextJanitor)
      jan->CleanUp();
   JumpItem* jx = JumpBase::jl->ji;    // previous jumpbase;
   if ( !jx ) { Terminate(); }         // jl was initial JumpItem
   JumpBase::jl = jx;                  // drop down a level; cannot be in front
                                       // of previous line
   Tracer::last = JumpBase::jl->trace;
   longjmp(JumpBase::jl->env, 1);
}

#endif                                 // end of simulate exceptions


BaseException::BaseException(const char* a_what) {
  _what = new char[MAXSIZE];
  if (!_what) {                // fail to make space
    _what = (char *)"No heap space for exception message\n";
    _deleteMe = false;
  }
  else {
    _deleteMe = true;
  }
  AddMessage("\n\nAn exception has been thrown\n");
  AddMessage(a_what);
}

BaseException::BaseException(const BaseException& oth) {
  _what = new char[MAXSIZE];
  if (!_what) {                // fail to make space
    _what = (char *)"No heap space for exception message\n";
    _deleteMe = false;
  }
  else {
    _deleteMe = true;
    strcpy(_what, oth._what);
  }
}
  
BaseException& BaseException::operator=(const BaseException& oth) {
  if (this == &oth) return *this; 
  _what = new char[MAXSIZE];
  if (!_what) {                // fail to make space
    _what = (char *)"No heap space for exception message\n";
    _deleteMe = false;
  }
  else {
    _deleteMe = true;
    strcpy(_what, oth._what);
  }
  return *this;
}
  
BaseException::~BaseException() {
  if (_deleteMe) delete[] _what;
}

void BaseException::AddMessage(const char* a_what) {
  if (a_what) {
    size_t nn = MAXSIZE - strlen(_what) - 1;
    strncat(_what, a_what, nn);
  }
}

void BaseException::AddInt(int value) {
  bool negative = false;
   if (value == 0) {
     AddMessage("0");
     return;
   }
   else if (value < 0) {
     value = -value;
     negative = true;
   }
   int nn = (negative ? 1 : 0); // how many digits will we need?
   int vv = value;        
   while (vv > 0) { vv /= 10; ++nn; }
   if ((int)(MAXSIZE - strlen(_what)) < nn) {
     AddMessage("***");
     return;
   }

   nn += strlen(_what);
   _what[nn] = 0;
   while (value > 0) {
      int nv = value / 10; int rm = value - nv * 10;  value = nv;
      _what[--nn] = (char)(rm + '0');
   }
   if (negative) _what[--nn] = '-';
}

  
void BaseException::MatrixDetails(const GeneralMatrix& A)
{
   MatrixBandWidth bw = A.bandwidth();
   int ubw = bw.upper_val; int lbw = bw.lower_val;
   AddMessage("MatrixType = ");
   AddMessage(A.Type().Value());
   AddMessage("  # Rows = "); BaseException::AddInt(A.Nrows());
   AddMessage("; # Cols = "); BaseException::AddInt(A.Ncols());
   if (lbw >=0)
   {
      AddMessage("; lower BW = ");
      AddInt(lbw);
   }
   if (ubw >=0)
   {
      AddMessage("; upper BW = ");
      AddInt(ubw);
   }
   AddMessage("\n");
}


#ifdef SimulateExceptions


Janitor::Janitor()
{
   if (do_not_link)
   {
      do_not_link = false; NextJanitor = 0; OnStack = false;
#ifdef CLEAN_LIST
      cout << "Not added to clean-list " << (unsigned long)this << "\n";
#endif
   }
   else
   {
      OnStack = true;
#ifdef CLEAN_LIST
      cout << "Add to       clean-list " << (unsigned long)this << "\n";
#endif
      NextJanitor = JumpBase::jl->janitor; JumpBase::jl->janitor=this;
   }
}

Janitor::~Janitor()
{
   // expect the item to be deleted to be first on list
   // but must be prepared to search list
   if (OnStack)
   {
#ifdef CLEAN_LIST
      cout << "Delete from  clean-list " << (unsigned long)this << "\n";
#endif
      Janitor* lastjan = JumpBase::jl->janitor;
      if (this == lastjan) JumpBase::jl->janitor = NextJanitor;
      else
      {
	 for (Janitor* jan = lastjan->NextJanitor; jan;
	    jan = lastjan->NextJanitor)
	 {
	    if (jan==this)
	       { lastjan->NextJanitor = jan->NextJanitor; return; }
	    lastjan=jan;
	 }

	 Throw(BaseException(
"Cannot resolve memory linked list\nSee notes in myexcept.cpp for details\n"
         ));


// This message occurs when a call to ~Janitor() occurs, apparently
// without a corresponding call to Janitor(). This could happen if my
// way of deciding whether a constructor is being called by new
// fails.

// It may happen if you are using my simulated exceptions and also have
// your compiler s exceptions turned on.

// It can also happen if you have a class derived from Janitor
// which does not include a copy constructor [ eg X(const &X) ].
// Possibly also if delete is applied an object on the stack (ie not
// called by new). Otherwise, it is a bug in myexcept or your compiler.
// If you do not #define TEMPS_DESTROYED_QUICKLY you will get this
// error with Microsoft C 7.0. There are probably situations where
// you will get this when you do define TEMPS_DESTROYED_QUICKLY. This
// is a bug in MSC. Beware of "operator" statements for defining
// conversions; particularly for converting from a Base class to a
// Derived class.

// You may get away with simply deleting this error message and Throw
// statement if you can not find a better way of overcoming the
// problem. In any case please tell me if you get this error message,
// particularly for compilers apart from Microsoft C 7.0.


      }
   }
}

JumpItem* JumpBase::jl;              // will be set to zero
jmp_buf JumpBase::env;
bool Janitor::do_not_link;           // will be set to false


int JanitorInitializer::ref_count;

JanitorInitializer::JanitorInitializer()
{
   if (ref_count++ == 0) new JumpItem;
                                    // need JumpItem at head of list
}

#endif                              // end of SimulateExceptions

Tracer* Tracer::last;               // will be set to zero


void Terminate()
{
   cout << "\n\nThere has been an exception with no handler - exiting";
   exit(1);
}



#ifdef DO_FREE_CHECK
// Routines for tracing whether new and delete calls are balanced

FreeCheckLink::FreeCheckLink() : next(FreeCheck::next)
   { FreeCheck::next = this; }

FCLClass::FCLClass(void* t, char* name) : ClassName(name) { ClassStore=t; }

FCLRealArray::FCLRealArray(void* t, char* o, int s)
  : Operation(o), size(s) { ClassStore=t; }

FCLIntArray::FCLIntArray(void* t, char* o, int s)
  : Operation(o), size(s) { ClassStore=t; }

FreeCheckLink* FreeCheck::next;
int FreeCheck::BadDelete;

void FCLClass::Report()
{ cout << "   " << ClassName << "   " << (unsigned long)ClassStore << "\n"; }

void FCLRealArray::Report()
{
   cout << "   " << Operation << "   " << (unsigned long)ClassStore <<
      "   " << size << "\n";
}

void FCLIntArray::Report()
{
   cout << "   " << Operation << "   " << (unsigned long)ClassStore <<
      "   " << size << "\n";
}

void FreeCheck::Register(void* t, char* name)
{
   FCLClass* f = new FCLClass(t,name);
   if (!f) { cout << "Out of memory in FreeCheck\n"; exit(1); }
#ifdef REG_DEREG
   cout << "Registering   " << name << "   " << (unsigned long)t << "\n";
#endif
}

void FreeCheck::RegisterR(void* t, char* o, int s)
{
   FCLRealArray* f = new FCLRealArray(t,o,s);
   if (!f) { cout << "Out of memory in FreeCheck\n"; exit(1); }
#ifdef REG_DEREG
   cout << o << "   " << s << "   " << (unsigned long)t << "\n";
#endif
}

void FreeCheck::RegisterI(void* t, char* o, int s)
{
   FCLIntArray* f = new FCLIntArray(t,o,s);
   if (!f) { cout << "Out of memory in FreeCheck\n"; exit(1); }
#ifdef REG_DEREG
   cout << o << "   " << s << "   " << (unsigned long)t << "\n";
#endif
}

void FreeCheck::DeRegister(void* t, char* name)
{
   FreeCheckLink* last = 0;
#ifdef REG_DEREG
   cout << "Deregistering " << name << "   " << (unsigned long)t << "\n";
#endif
   for (FreeCheckLink* fcl = next; fcl; fcl = fcl->next)
   {
      if (fcl->ClassStore==t)
      {
	 if (last) last->next = fcl->next; else next = fcl->next;
	 delete fcl; return;
      }
      last = fcl;
   }
   cout << "\nRequest to delete non-existent object of class and location:\n";
   cout << "   " << name << "   " << (unsigned long)t << "\n";
   BadDelete++;
   Tracer::PrintTrace();
   cout << "\n";
}

void FreeCheck::DeRegisterR(void* t, char* o, int s)
{
   FreeCheckLink* last = 0;
#ifdef REG_DEREG
   cout << o << "   " << s << "   " << (unsigned long)t << "\n";
#endif
   for (FreeCheckLink* fcl = next; fcl; fcl = fcl->next)
   {
      if (fcl->ClassStore==t)
      {
	 if (last) last->next = fcl->next; else next = fcl->next;
	 if (s >= 0 && ((FCLRealArray*)fcl)->size != s)
	 {
	    cout << "\nArray sizes do not agree:\n";
	    cout << "   " << o << "   " << (unsigned long)t
	       << "   " << ((FCLRealArray*)fcl)->size << "   " << s << "\n";
	    Tracer::PrintTrace();
	    cout << "\n";
	 }
	 delete fcl; return;
      }
      last = fcl;
   }
   cout << "\nRequest to delete non-existent real array:\n";
   cout << "   " << o << "   " << (unsigned long)t << "   " << s << "\n";
   BadDelete++;
   Tracer::PrintTrace();
   cout << "\n";
}

void FreeCheck::DeRegisterI(void* t, char* o, int s)
{
   FreeCheckLink* last = 0;
#ifdef REG_DEREG
   cout << o << "   " << s << "   " << (unsigned long)t << "\n";
#endif
   for (FreeCheckLink* fcl = next; fcl; fcl = fcl->next)
   {
      if (fcl->ClassStore==t)
      {
	 if (last) last->next = fcl->next; else next = fcl->next;
	 if (s >= 0 && ((FCLIntArray*)fcl)->size != s)
	 {
	    cout << "\nArray sizes do not agree:\n";
	    cout << "   " << o << "   " << (unsigned long)t
	       << "   " << ((FCLIntArray*)fcl)->size << "   " << s << "\n";
	    Tracer::PrintTrace();
	    cout << "\n";
	 }
	 delete fcl; return;
      }
      last = fcl;
   }
   cout << "\nRequest to delete non-existent int array:\n";
   cout << "   " << o << "   " << (unsigned long)t << "   " << s << "\n";
   BadDelete++;
   Tracer::PrintTrace();
   cout << "\n";
}

void FreeCheck::Status()
{
   if (next)
   {
      cout << "\nObjects of the following classes remain undeleted:\n";
      for (FreeCheckLink* fcl = next; fcl; fcl = fcl->next) fcl->Report();
      cout << "\n";
   }
   else cout << "\nNo objects remain undeleted\n\n";
   if (BadDelete)
   {
      cout << "\nThere were " << BadDelete << 
         " requests to delete non-existent items\n\n";
   }
}

#endif                            // end of DO_FREE_CHECK

// derived exception bodies

Logic_error::Logic_error(const char* a_what) : BaseException()
{
   Select = BaseException::Select;
   AddMessage("Logic error:- "); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

Runtime_error::Runtime_error(const char* a_what)
   : BaseException()
{
   Select = BaseException::Select;
   AddMessage("Runtime error:- "); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

Domain_error::Domain_error(const char* a_what) : Logic_error()
{
   Select = BaseException::Select;
   AddMessage("domain error\n"); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

Invalid_argument::Invalid_argument(const char* a_what) : Logic_error()
{
   Select = BaseException::Select;
   AddMessage("invalid argument\n"); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

Length_error::Length_error(const char* a_what) : Logic_error()
{
   Select = BaseException::Select;
   AddMessage("length error\n"); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

Out_of_range::Out_of_range(const char* a_what) : Logic_error()
{
   Select = BaseException::Select;
   AddMessage("out of range\n"); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

//Bad_cast::Bad_cast(const char* a_what) : Logic_error()
//{
//   Select = BaseException::Select;
//   AddMessage("bad cast\n"); AddMessage(a_what);
//   if (a_what) Tracer::AddTrace();
//}

//Bad_typeid::Bad_typeid(const char* a_what) : Logic_error()
//{
//   Select = BaseException::Select;
//   AddMessage("bad type id.\n"); AddMessage(a_what);
//   if (a_what) Tracer::AddTrace();
//}

Range_error::Range_error(const char* a_what) : Runtime_error()
{
   Select = BaseException::Select;
   AddMessage("range error\n"); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

Overflow_error::Overflow_error(const char* a_what) : Runtime_error()
{
   Select = BaseException::Select;
   AddMessage("overflow error\n"); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}

Bad_alloc::Bad_alloc(const char* a_what) : BaseException()
{
   Select = BaseException::Select;
   AddMessage("bad allocation\n"); AddMessage(a_what);
   if (a_what) Tracer::AddTrace();
}




unsigned long BaseException::Select;
unsigned long Logic_error::Select;
unsigned long Runtime_error::Select;
unsigned long Domain_error::Select;
unsigned long Invalid_argument::Select;
unsigned long Length_error::Select;
unsigned long Out_of_range::Select;
//unsigned long Bad_cast::Select;
//unsigned long Bad_typeid::Select;
unsigned long Range_error::Select;
unsigned long Overflow_error::Select;
unsigned long Bad_alloc::Select;

#ifdef use_namespace
}
#endif


///@}

