// ---------------------------------------------------------------------------
// - 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-2006 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Cons.hpp"
#include "Vector.hpp"
#include "Method.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);
  }

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

  // the structure for shared object
  struct s_shared {
    // the reference count monitor
    Monitor d_mon;
    // the read-write lock
    Lockrw  d_lock;
  };

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

  // increment the object reference count

  Object* Object::iref (Object* object) {
    // check for nil first
    if (object == nilp) return nilp;
    // check for shared
    if (object->p_shared != nilp) object->p_shared->d_mon.enter ();
    object->d_rcount++;
    if (object->p_shared != nilp) object->p_shared->d_mon.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;
    // if not shared we delete
    if (object->p_shared == nilp) {
      if (--object->d_rcount <= 0) delete object;
      return;
    }
    // here the object is shared
    object->p_shared->d_mon.enter ();
    if (--object->d_rcount <= 0) {
      object->p_shared->d_mon.leave ();
      delete object;
      return;
    }
    object->p_shared->d_mon.leave ();
  }

  // clean this object if the reference count is null
  
  void Object::cref (Object* object) {
    // cehck for nil first
    if (object == nilp) return;
    // if not shared we simply delete
    if (object->p_shared == nilp) {
      if (object->d_rcount <= 0) delete object;
      return;
    }
    // here the object is shared
    object->p_shared->d_mon.enter ();
    if (object->d_rcount <= 0) {
      object->p_shared->d_mon.leave ();
      delete object;
      return;
    }
    object->p_shared->d_mon.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 shared
    if (object->p_shared != nilp) object->p_shared->d_mon.enter ();
    if (object->d_rcount > 0) object->d_rcount--;
    if (object->p_shared != nilp) object->p_shared->d_mon.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 shared
    if (object->p_shared != nilp) object->p_shared->d_mon.enter ();
    bool result = (object->d_rcount <= 1);
    if (object->p_shared != nilp) object->p_shared->d_mon.leave ();
    return result;
  }

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

  // create a new object with a 0 reference count

  Object::Object (void) {
    p_shared = nilp;
    d_rcount = 0;
  }

  // destroy this object

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

  // return an object class name

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

  // clone this object

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

  // make this object a shared one

  void Object::mksho (void) {
    p_shared = new s_shared;
  }

  // lean the shared structure

  void Object::rmsho (void) {
    delete p_shared;
    p_shared = nilp;
  }

  // return true if the object is shared
    
  bool Object::issho (void) const {
    return (p_shared != nilp);
  }

  // get a read lock for this object
  void Object::rdlock (void) const {
    if (p_shared == nilp) return;
    p_shared->d_lock.rdlock ();
  }

  // get a write lock for this object
  void Object::wrlock (void) const {
    if (p_shared == nilp) return;
    p_shared->d_lock.wrlock ();
  }

  // unlock a previous lock

  void Object::unlock (void) const {
    if (p_shared == nilp) return;
    p_shared->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 = 7;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);

  // the object supported quarks
  static const long QUARK_EQUL   = zone.intern ("=");
  static const long QUARK_REPR   = zone.intern ("repr");
  static const long QUARK_CLONE  = zone.intern ("clone");
  static const long QUARK_SHARED = zone.intern ("shared-p");
  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 ());
  }

  // 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 Method (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_SHARED) return new Boolean (issho ());
      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) && (quark == QUARK_EQUL)) {
      Object* obj = argv->get (0);
      return this->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 ());
  }
}
