// ---------------------------------------------------------------------------
// - Object.cpp                                                              -
// - standard object library - base object 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-2011 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Cons.hpp"
#include "Combo.hpp"
#include "Vector.hpp"
#include "Lockrw.hpp"
#include "Monitor.hpp"
#include "Boolean.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "cmem.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - memory allocation section                                             -
  // -------------------------------------------------------------------------

  // the object memory allocator

  void* Object::operator new (const t_size size) {
    return c_galloc (size);
  }

  void* Object::operator new [] (const t_size size) {
    return c_galloc (size);
  }

  // the object memory deallocator

  void Object::operator delete (void* handle) {
    c_gfree (handle);
  }

  void Object::operator delete [] (void* handle) {
    c_gfree (handle);
  }

  // enable/disable the dynamic memory debug

  void Object::setmdbg (const bool flag) {
    c_setmdbg (flag);
  }

  // set a memory label

  void Object::setmlbl (const String& label) {
    // get a character representation
    char* cptr = label.tochar ();
    // set the memory label
    c_setmlbl (cptr);
    // clean the label
    delete [] cptr;
  }

  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------

  // the access control structure
  struct s_access {
    // the reference count
    long    d_rcnt;
    // the reference count monitor
    Monitor d_rmon;
    // the read-write lock
    Lockrw  d_lock;
    // constructor
    s_access (void) {
      d_rcnt = 0;
    }
  };

  // -------------------------------------------------------------------------
  // - public section                                                        -
  // -------------------------------------------------------------------------

  // increment the object reference count

  Object* Object::iref (Object* object) {
    // check for nil first
    if (object == nilp) return nilp;
    // check for access
    if (object->p_access != nilp) {
      object->p_access->d_rmon.enter ();
      object->p_access->d_rcnt++;
      object->p_access->d_rmon.leave ();
    }
    return object;
  }
    
  // decrement the reference count and destroy the object if null

  void Object::dref (Object* object) {
    // check for nil first
    if (object == nilp) return;
    // check for access
    if (object->p_access != nilp) {
      object->p_access->d_rmon.enter ();
      if (--object->p_access->d_rcnt <= 0) {
	object->p_access->d_rmon.leave ();
	delete object;
	return;
      }
      object->p_access->d_rmon.leave ();
    }
  }

  // clean this object if the reference count is null
  
  void Object::cref (Object* object) {
    // check for nil first
    if (object == nilp) return;
    // check for access
    if (object->p_access != nilp) {
      object->p_access->d_rmon.enter ();
      if (object->p_access->d_rcnt <= 0) {
	object->p_access->d_rmon.leave ();
	delete object;
	return;
      }
      object->p_access->d_rmon.leave ();
    }
  }
  
  // decrement the object reference count but do not destroy if null

  void Object::tref (Object* object) {
    // check for nil first
    if (object == nilp) return;
    // check for access
    if (object->p_access != nilp) {
      object->p_access->d_rmon.enter ();
      if (object->p_access->d_rcnt > 0) object->p_access->d_rcnt--;
      object->p_access->d_rmon.leave ();
    }
  }

  // return true if the object has reference equal to 0 or 1

  bool Object::uref (Object* object) {
    // check for nil first
    if (object == nilp) return false;
    // check for access
    if (object->p_access == nilp) return false;
    object->p_access->d_rmon.enter ();
    bool result = (object->p_access->d_rcnt <= 1);
    object->p_access->d_rmon.leave ();
    return result;
  }

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

  // create a new object with a 0 reference count

  Object::Object (void) {
    p_access = new s_access;
  }

  // destroy this object

  Object::~Object (void) {
    delete p_access;
  }

  // return an object class name

  const String Object::repr (Object* object) {
    if (object == nilp) return "nil";
    return object->repr ();
  }

  // return a clone of this object

  Object* Object::clone (void) const {
    throw Exception ("clone-error", "cannot clone object", repr ());
  }

  // get a read lock for this object

  void Object::rdlock (void) const {
    if (p_access == nilp) return;
    p_access->d_lock.rdlock ();
  }

  // get a write lock for this object

  void Object::wrlock (void) const {
    if (p_access == nilp) return;
    p_access->d_lock.wrlock ();
  }

  // get an adaptative read lock for this object

  void Object::arlock (void) const {
    if (p_access == nilp) return;
    p_access->d_lock.arlock ();
  }

  // unlock a previous lock

  void Object::unlock (void) const {
    if (p_access == nilp) return;
    p_access->d_lock.unlock ();
  }

  // return the minimal object associated with this one

  Object* Object::mini (void) const {
    return const_cast <Object*> (this);
  }

  // -------------------------------------------------------------------------
  // - quark section                                                         -
  // -------------------------------------------------------------------------

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

  // the object supported quarks
  static const long QUARK_VDEF   = zone.intern ("=");
  static const long QUARK_REPR   = zone.intern ("repr");
  static const long QUARK_CLONE  = zone.intern ("clone");
  static const long QUARK_RDLOCK = zone.intern ("rdlock");
  static const long QUARK_WRLOCK = zone.intern ("wrlock");
  static const long QUARK_UNLOCK = zone.intern ("unlock");

  // return true if the given quark is defined

  bool Object::isquark (const long quark, const bool hflg) const {
    rdlock ();
    bool result = zone.exists (quark);
    unlock ();
    return result;
  }
  
  // operate this object with another one

  Object* Object::oper (t_oper type, Object* object) {
    throw Exception ("operator-error", "invalid operator call for object",
		     repr ());
  }

  // create or set a const object to this object

  Object* Object::cdef (Runnable* robj, Nameset* nset, Object* object) {
    throw Exception ("const-error", "invalid const define with object",
		     repr ());
  }

  // create or set a const object to this object by quark

  Object* Object::cdef (Runnable* robj, Nameset* nset, const long quark,
			Object* object) {
    String mesg = "invalid const define with name ";
    mesg = mesg + String::qmap (quark);
    mesg = mesg + " from object type";
    throw Exception ("const-error", mesg, repr ());
  }

  // create or set an object to this object

  Object* Object::vdef (Runnable* robj, Nameset* nset, Object* object) {
    throw Exception ("trans-error", "invalid trans define with object",
		     repr ());
  }

  // create or set an object to this object by quark

  Object* Object::vdef (Runnable* robj, Nameset* nset, const long quark,
			Object* object) {
    String mesg = "invalid trans define with name ";
    mesg = mesg + String::qmap (quark);
    mesg = mesg + " from object type";
    throw Exception ("trans-error", mesg, repr ());
  }

  // unreference an object

  Object* Object::udef (Runnable* robj, Nameset* nset) {
    throw Exception ("unref-error", "invalid unreference with object",
		     repr ());
  }
  
  // unreference an object by quark

  Object* Object::udef (Runnable* robj, Nameset* nset, const long quark) {
    String mesg = "invalid object unref by name ";
    mesg = mesg + String::qmap (quark);
    mesg = mesg + " from object type";
    throw Exception ("const-error", mesg, repr ());
  }


  // evaluate an object in the current nameset

  Object* Object::eval (Runnable* robj, Nameset* nset) {
    return this;
  }

  // evaluate an object in the current nameset by quark

  Object* Object::eval (Runnable* robj, Nameset* nset, const long quark) {
    return new Combo (quark, this);
  }

  // apply an object with a set of arguments

  Object* Object::apply (Runnable* robj, Nameset* nset, Cons* args) {
    throw Exception ("apply-error", "invalid call to apply method with object",
		     repr ());
  }

  // apply an object by quark with a set of arguments

  Object* Object::apply (Runnable* robj, Nameset* nset, const long quark,
			 Cons* args) {
    Vector* argv   = Vector::eval (robj, nset, args);
    Object* result = nilp;
    try {
      result = apply (robj, nset, quark, argv);
    } catch (...) {
      delete argv;
      throw;
    }
    delete argv;
    return result;
  }

  // apply an object by object with a set of arguments

  Object* Object::apply (Runnable* robj, Nameset* nset, Object* object,
			 Cons* args) {
    String mesg = "invalid call to apply with object ";
    mesg = mesg + Object::repr (object);
    mesg = mesg + " from object type";
    throw Exception ("apply-error", mesg, repr ());
  }

  // apply an object by object with a vector of arguments

  Object* Object::apply (Runnable* robj, Nameset* nset, Object* object,
			 Vector* argv) {
    String mesg = "invalid call to apply with object ";
    mesg = mesg + Object::repr (object);
    mesg = mesg + " from object type";
    throw Exception ("apply-error", mesg, repr ());
  }

  // apply an object by name with a vector of arguments

  Object* Object::apply (Runnable* robj, Nameset* nset, const long quark,
			 Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();

    // dispatch 0 arguments
    if (argc == 0) {
      if (quark == QUARK_REPR) return new String  (repr ());
      if (quark == QUARK_RDLOCK) {
	rdlock ();
	robj->post (this);
	return this;
      }
      if (quark == QUARK_WRLOCK) {
	wrlock ();
	robj->post (this);
	return this;
      }
      if (quark == QUARK_UNLOCK) {
	unlock ();
	robj->post (this);
	return this;
      }
      if (quark == QUARK_CLONE) {
	Object* result = clone ();
	robj->post (result);
	return result;
      }
    }

    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_VDEF) {
	Object* obj = argv->get (0);
	return vdef (robj, nset, obj);
      }
    }

    // no way - error
    String mesg = "invalid call to apply with name ";
    mesg = mesg + String::qmap (quark);
    mesg = mesg + " from object type";
    throw Exception ("apply-error", mesg, repr ());
  }
}
