// Change History
// 11/5/03 AV Production: make copy/clone flag

#include <string.h>
#include <memory>

#include "base/at_types.h"
#include "base/at_error.h"

#include "base/at_reporterr.h"
#include <string>


class AT_Error::info
{
public:
    enum { MAX_ERRORS = 10};
    enum { INVALID_LINE = -1 };
    enum { NOT_IN_STACK = -1 };

public:
    static std::string opjectNULL;

private:
  std::string objectType;
  std::string objectName;

  std::string module;
  uint lineNum;

  AT_Error *prev;
  bool clone;
  static AT_Error::Reporter *reporter;
  uint ErrorNum;

private:
  static uint error_cnt;

public:
  static AT_Error::info * getCurrentInfo();
  // see if exception has been thrown previously
  static bool atError
    ()
    { return !!error_cnt; };
  static void setReporter
    (Reporter *reporter) { AT_Error::info * e = getCurrentInfo();
    if(e) e->reporter = reporter; }
  static Reporter * GetReporter() { AT_Error::info * e = getCurrentInfo();
    return e ? e->reporter : NULL; }

public:
    info() : clone(false), prev(NULL), lineNum(INVALID_LINE), ErrorNum(NOT_IN_STACK) {}

    uint GetNum() const { return ErrorNum; }
    void SetNum(uint num) { ErrorNum = num; }

    const byte * GetType() const { return (const byte*) objectType.c_str(); }
    const byte * GetName() const { return (const byte*) objectName.c_str(); }
    const byte * GetModule() const { return (const byte*) module.c_str(); }
    uint GetLine() const { return lineNum; }
    bool IsClone() const { return clone; }
    AT_Error * GetPrev() const { return prev; }

    void SetType(const byte * type) { objectType = type ? (char *)type : ""; }
    void SetName(const byte * name) { objectName = name ? (char *)name : ""; }
    void SetModule(const byte * mod) { module = mod ? (char *)mod : ""; }
    void SetLine(uint line) { lineNum = line; }
    void SetClone(bool c) { clone = c; }
    void SetPrev(const AT_Error * err) { if(!err) { prev = NULL; return ; }
        info * iprev = err->GetInfo();
        if(iprev) iprev->SetClone(true);
        prev = err->clone();  }

public:
    static AT_Error::info * PushInfo();
    static AT_Error::info * PopInfo();
};

static AT_Error::info error_info[AT_Error::info::MAX_ERRORS];
AT_Error::Reporter *AT_Error::info::reporter = NULL;
uint AT_Error::info::error_cnt = 0;
std::string AT_Error::info::opjectNULL = "";

AT_Error::info * AT_Error::info::getCurrentInfo()
{
    if(!error_cnt)
        return NULL;

return error_cnt <= MAX_ERRORS ? &error_info[error_cnt - 1] : NULL;
}

AT_Error::info * AT_Error::info::PushInfo()
{
    if(error_cnt >= MAX_ERRORS)
        return NULL;
    error_info[error_cnt].SetNum(error_cnt);
return &error_info[error_cnt++];
}

AT_Error::info * AT_Error::info::PopInfo()
{
    // PS fixed
    if(!error_cnt || (error_cnt >= MAX_ERRORS))
        return NULL;
    error_info[--error_cnt].SetNum(NOT_IN_STACK);
    return &error_info[error_cnt];
}

// ================================== AT_ERROR ================

AT_Error::AT_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String objectName,
   const AT_Error *const prev) :
    //deletable(false),
    i(info::PushInfo())
{
    if(i)
        {
        i->SetType(objectType);
        i->SetName(objectName);
        i->SetModule((const byte*) module);

        i->SetLine(lineNum);
        i->SetClone(false);
        i->SetPrev(prev);
        }
}

AT_Error::AT_Error
  (const AT_Error &rhs) //:
    //deletable(true),         // cloning
    //i(rhs.i)
{
    if(rhs.i)
        {
        if(rhs.i->IsClone())
          i = rhs.i;
        else
          {
          i = info::PushInfo();
          if(i)
              {
              i->SetType(rhs.i->GetType());
              i->SetName(rhs.i->GetName());
              i->SetModule(rhs.i->GetModule());
              i->SetLine(rhs.i->GetLine());
              i->SetClone(false);
              i->SetPrev(rhs.i->GetPrev());
              }
          }
        }
}

AT_Error::~AT_Error
  ()
{
  //if (!deletable && (cur_except > info_stack))
  if (i && !i->IsClone() && i->GetNum() != info::NOT_IN_STACK)
    info::PopInfo();
}

const AT_String AT_Error::getObjectType
  ()
  const
{
  return i ? i->GetType() : (const byte*) info::opjectNULL.c_str();
}

const AT_String AT_Error::getObjectName
  ()
  const
{
  return i ? i->GetName() : (const byte*) info::opjectNULL.c_str();
}

const AT_String AT_Error::getModule
  ()
  const
{
  return i ? i->GetModule() : (const byte*) info::opjectNULL.c_str();
}

uint32 AT_Error::getLineNum
  ()
  const
{
  return i ? i->GetLine() : 0;
}

AT_Error *AT_Error::getCause
  ()
{
    if(!i)
        return NULL;

  AT_Error *prev = i->GetPrev();
  // clear prev
  i->SetPrev(NULL);

  //if (deletable)
  if (i->IsClone())
    delete this;
  else
    i = info::PopInfo();

  return prev;
}

bool AT_Error::atError()
{
return info::atError();
}

// ================================== GENERAL ERROR ===========
void AT_General_Error::setReporter(Reporter *reporter)
{
    AT_Error::info::setReporter(reporter);
}


bool AT_General_Error::retry
  (uint try_num,
   AT_String info)
{
  if (!try_num)
    // first try
    return true;
  AT_General_Error::Reporter * reporter = (AT_General_Error::Reporter * )info::GetReporter();
  if (!reporter)
    // cannot retry without reporter
    return false;

  if (reporter->isAbend(try_num, info))
    // abending retries
    return false;

  // wait for next retry
  reporter->wait();
  // retry
  return true;
}
// ================================== LOGIC ERROR =============

AT_Logic_Error::AT_Logic_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String objectName) :
     AT_General_Error(module, lineNum, objectType, objectName)
{
}

AT_Logic_Error* AT_Logic_Error::clone
  ()
  const
{
  return new AT_Logic_Error(*this);
}

void AT_Logic_Error::report
  (AT_General_Error::Reporter &reporter)
  const
{
  reporter.report(this);
}

// ================================== FUNCTION ERROR ==========

AT_Function_Error::AT_Function_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String functionName) :
     AT_General_Error(module, lineNum, objectType, functionName)
{
}

AT_Function_Error* AT_Function_Error::clone
  ()
  const
{
  return new AT_Function_Error(*this);
}

void AT_Function_Error::report
  (AT_General_Error::Reporter &reporter)
  const
{
  reporter.report(this);
}

// ================================== CORRUPT ERROR ===========

AT_Corrupt_Error::AT_Corrupt_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String objectName,
   uint32 badValue) :
     AT_General_Error(module, lineNum, objectType, objectName),
     bad_value(badValue)
{
}

AT_Corrupt_Error* AT_Corrupt_Error::clone
  ()
  const
{
  return new AT_Corrupt_Error(*this);
}

uint32 AT_Corrupt_Error::getBadValue
  ()
  const
{
  return bad_value;
}

void AT_Corrupt_Error::report
  (AT_General_Error::Reporter &reporter)
  const
{
  reporter.report(this);
}

// ================================== BOUNDS ERROR ===========

AT_Bounds_Error::AT_Bounds_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String objectName,
   uint32 badValue) :
     AT_General_Error(module, lineNum, objectType, objectName),
     bad_value(badValue)
{
}

AT_Bounds_Error* AT_Bounds_Error::clone
  ()
  const
{
  return new AT_Bounds_Error(*this);
}

uint32 AT_Bounds_Error::getBadValue
  ()
  const
{
  return bad_value;
}

void AT_Bounds_Error::report
  (AT_General_Error::Reporter &reporter)
  const
{
  reporter.report(this);
}

// ================================== SYSTEM ERROR ============

AT_System_Error::AT_System_Error
  (const char* module,
   uint lineNum) :
    AT_Error(module, lineNum),
    #ifdef WIN16
    error_num(0)   // how do we get errors from Win16?
    #else
    error_num(0)   // @todo: GetLastError())
    #endif
{
}

AT_System_Error* AT_System_Error::clone
  ()
  const
{
  return new AT_System_Error(*this);
}

uint32 AT_System_Error::getErrorNum
  ()
  const
{
  return error_num;
}

void AT_System_Error::report
  (Reporter &reporter)
  const
{
  reporter.report(this);
}

// ================================== CONSTRUCTOR ERROR =======

AT_Constructor_Error::AT_Constructor_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   const AT_Error *const prev) :
     AT_General_Error(module, lineNum, objectType, 0, prev)
{
}

AT_Constructor_Error *AT_Constructor_Error::clone
  ()
  const
{
  return new AT_Constructor_Error(*this);
}

void AT_Constructor_Error::report
  (Reporter& reporter)
  const
{
  reporter.report(this);
}

// ================================== UNEXPECTED ERROR ========

AT_Unexpected_Error::AT_Unexpected_Error
  (const char* module,
   uint lineNum) :
    AT_General_Error(module, lineNum)
{
}

AT_Unexpected_Error *AT_Unexpected_Error::clone
  ()
  const
{
  return new AT_Unexpected_Error(*this);
}

void AT_Unexpected_Error::report
  (Reporter& reporter)
  const
{
  reporter.report(this);
}

// ================================== FILE ERRORS =============

AT_File_Open_Error::AT_File_Open_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String objectName) :
     AT_General_Error(module,
                      lineNum,
                      objectType,
                      objectName,
                      std::auto_ptr<AT_System_Error>(new AT_System_Error(module, lineNum)).get())
{
}

AT_File_Open_Error* AT_File_Open_Error::clone
  ()
  const
{
  return new AT_File_Open_Error(*this);
}

void AT_File_Open_Error::report
  (Reporter &reporter)
  const
{
  reporter.report(this);
}

AT_File_Type_Error::AT_File_Type_Error
  (const char* module,
   uint lineNum,
   uint exp,
   uint act,
   AT_String objectType,
   AT_String objectName) :
     AT_General_Error(module,
                      lineNum,
                      objectType,
                      objectName,
                      std::auto_ptr<AT_System_Error>(new AT_System_Error(module, lineNum)).get()),
     expected(exp),
     actual(act)
{
}

AT_File_Type_Error* AT_File_Type_Error::clone
  ()
  const
{
  return new AT_File_Type_Error(*this);
}

void AT_File_Type_Error::report
  (Reporter &reporter)
  const
{
  reporter.report(this);
}

AT_File_Read_Error::AT_File_Read_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String objectName) :
     AT_General_Error(module,
                      lineNum,
                      objectType,
                      objectName,
                      std::auto_ptr<AT_System_Error>(new AT_System_Error(module, lineNum)).get())
{
}

AT_File_Read_Error* AT_File_Read_Error::clone
  ()
  const
{
  return new AT_File_Read_Error(*this);
}

void AT_File_Read_Error::report
  (Reporter &reporter)
  const
{
  reporter.report(this);
}

AT_File_Write_Error::AT_File_Write_Error
  (const char* module,
   uint lineNum,
   AT_String objectType,
   AT_String objectName) :
     AT_General_Error(module,
                      lineNum,
                      objectType,
                      objectName,
                      std::auto_ptr<AT_System_Error>(new AT_System_Error(module, lineNum)).get())
{
}

AT_File_Write_Error* AT_File_Write_Error::clone
  ()
  const
{
  return new AT_File_Write_Error(*this);
}

void AT_File_Write_Error::report
  (Reporter &reporter)
  const
{
  reporter.report(this);
}



