// ---------------------------------------------------------------------------
// - Exception.cpp                                                           -
// - standard object library - exception class implementation                -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Integer.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a exception with an id
  
  Exception::Exception (const String& eid) {
    d_eid    = eid;
    p_obj    = nilp;
    d_nlf    = false; 
    d_abf    = false; 
    d_lnum   = 0;
  }

  // create a exception with an id and a reason
  
  Exception::Exception (const String& eid, const String& reason) {
    d_eid    = eid;
    d_reason = reason;
    p_obj    = nilp;
    d_nlf    = false;
    d_abf    = false;
    d_lnum   = 0;
  }

  // create a exception with an eid, an indicator and a long reason
  
  Exception::Exception (const String& eid, const String& reason,
			const String& name) {
    d_eid    = eid;
    d_reason = reason + " " + name;
    p_obj    = nilp;
    d_nlf    = false;
    d_abf    = false;
    d_lnum   = 0;
  }

  // create a exception with an id and an object
  
  Exception::Exception (const String& eid, Object* object) {
    d_eid    = eid;
    p_obj    = Object::iref (object);
    d_nlf    = false;
    d_abf    = false;
    d_lnum   = 0;
  }

  // create a exception with a short type, a reason and an object
  
  Exception::Exception (const String& eid, const String& reason,
			Object* object) {
    d_eid    = eid;
    d_reason = reason;
    p_obj    = Object::iref (object);
    d_nlf    = false;
    d_abf    = false;
    d_lnum   = 0;
  }

  // copy constructor for this exception
  
  Exception::Exception (const Exception& that) {
    that.rdlock ();
    d_eid    = that.d_eid;
    d_reason = that.d_reason;
    p_obj    = Object::iref (that.p_obj);
    d_nlf    = that.d_nlf;
    d_abf    = that.d_abf;
    d_name   = that.d_name;
    d_lnum   = that.d_lnum;
    that.unlock ();
  }

  // destroy this exception

  Exception::~Exception (void) {
    Object::dref (p_obj);
  }

  // return the class name

  String Exception::repr (void) const {
    return "Exception";
  }

  // return the exception id

  String Exception::geteid (void) const {
    rdlock ();
    String result = d_eid;
    unlock ();
    return result;
  }
  
  // return the exception reason

  String Exception::getval (void) const {
    rdlock ();
    String result = d_reason;
    unlock ();
    return result;
  }
  
  // return the exception object

  Object* Exception::getobj (void) const {
    rdlock ();
    Object* result = p_obj;
    unlock ();
    return result;
  }

  // set the new line flag

  void Exception::setnlf (const bool flag) {
    wrlock ();
    d_nlf = flag;
    unlock ();
  }

  // return the new line flag

  bool Exception::getnlf (void) const {
    rdlock ();
    bool result = d_nlf;
    unlock ();
    return result;
  }

  // set the abort flag

  void Exception::setabf (const bool flag) {
    wrlock ();
    d_abf = flag;
    unlock ();
  }

  // return the abort flag

  bool Exception::getabf (void) const {
    rdlock ();
    bool result = d_abf;
    unlock ();
    return result;
  }

  // update the exception file name - do not touch it if already set

  void Exception::updname (const String& name) {
    wrlock ();
    if (d_name.length () == 0) d_name = name;
    unlock ();
  }

  // set the exception file name

  void Exception::setname (const String& name) {
    wrlock ();
    d_name = name;
    unlock ();
  }

  // return the exception file name

  String Exception::getname (void) const {
    rdlock ();
    String result = d_name;
    unlock ();
    return result;
  }

  // update the exception line number

  void Exception::updlnum (const long lnum) {
    wrlock ();
    if (d_lnum == 0) d_lnum = lnum;
    unlock ();
  }

  // set the exception line number

  void Exception::setlnum (const long lnum) {
    wrlock ();
    d_lnum = lnum;
    unlock ();
  }

  // return the exception line number

  long Exception::getlnum (void) const {
    rdlock ();
    long result = d_lnum;
    unlock ();
    return result;
  }

  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

  // the quark zone
  static const long QUARK_ZONE_LENGTH = 5;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);

  // the object supported quarks
  static const long QUARK_EID    = zone.intern ("eid");
  static const long QUARK_NAME   = zone.intern ("name");
  static const long QUARK_LNUM   = zone.intern ("line");
  static const long QUARK_REASON = zone.intern ("reason");
  static const long QUARK_OBJECT = zone.intern ("object");

  // return true if the given quark is defined

  bool Exception::isquark (const long quark, const bool hflg) const {
    rdlock ();
    if (zone.exists (quark) == true) {
      unlock ();
      return true;
    }
    bool result = hflg ? Object::isquark (quark, hflg) : false;
    unlock ();
    return result;
  }

  // evaluate an object member by quark

  Object* Exception::eval (Runnable* robj, Nameset* nset, const long quark) {
    // check the exception first
    if (quark == QUARK_EID)    return new String  (geteid  ());
    if (quark == QUARK_NAME)   return new String  (getname ());
    if (quark == QUARK_REASON) return new String  (getval  ());
    if (quark == QUARK_OBJECT) {
      rdlock ();
      Object* result = getobj ();
      robj->post (result);
      unlock ();
      return result;
    }
    if (quark == QUARK_LNUM) {
      Integer iobj = getlnum ();
      return new String (iobj.tostring ());
    }
    // evaluate in the object
    return Object::eval (robj, nset, quark);
  }
}
