// ---------------------------------------------------------------------------
// - Method.cpp                                                              -
// - standard object library - generic method 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 "Cons.hpp"
#include "Method.hpp"
#include "Recycle.hpp"
#include "Runnable.hpp"

namespace afnix {

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

  // the method recycler
  static Recycle recycler;

  // allocate a new cons cell
  void* Method::operator new (const t_size size) {
    return recycler.pop (size);
  }

  // delete a cons cell
  void Method::operator delete (void* handle) {
    recycler.push (handle);
  }

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

  // create a new method with a quark

  Method::Method (const long quark, Object* object) {
    d_type        = QUARK;
    d_method.qval = quark;
    p_object      = Object::iref (object);
    d_aflg        = false;
  }

  // create a new method with a method object

  Method::Method (Object* method, Object* object) {
    d_type        = OBJECT;
    d_method.oval = Object::iref (method);
    p_object      = Object::iref (object);
    d_aflg        = false;
  }

  // create a new method with a quark and an apply flag

  Method::Method (const long quark, Object* object, const bool aflg) {
    d_type        = QUARK;
    d_method.qval = quark;
    p_object      = Object::iref (object);
    d_aflg        = aflg;
  }

  // create a new method with a method object and an apply flag

  Method::Method (Object* method, Object* object, const bool aflg) {
    d_type        = OBJECT;
    d_method.oval = Object::iref (method);
    p_object      = Object::iref (object);
    d_aflg        = aflg;
  }

  // destroy this method

  Method::~Method (void) {
    if (d_type == OBJECT) Object::dref (d_method.oval);
    Object::dref (p_object);
  }

  // return the class name

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

  // get the apply flag

  bool Method::getaflg (void ) const {
    rdlock ();
    bool result = d_aflg;
    unlock ();
    return result;
  }

  // set the apply flag

  void Method::setaflg (const bool aflg) {
    wrlock ();
    d_aflg = aflg;
    unlock ();
  }

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

  // apply this object with a set of arguments
  
  Object* Method::apply (Runnable* robj, Nameset* nset, Cons* args) {
    Object* result = nilp;
    switch (d_type) {
    case Method::QUARK:
      if (d_aflg == true) 
	result = p_object->Object::apply (robj, nset, d_method.qval, args);
      else
	result = p_object->apply (robj, nset, d_method.qval, args);
      break;
    case Method::OBJECT:
      // evaluate first the argument in the original nameset
      Cons* carg = Cons::mkcons (robj, nset, args);
      // call the object apply method
      try {
	if (d_aflg == true)
	  result = p_object->Object::apply (robj, nset, d_method.oval, carg);
	else
	  result = p_object->apply (robj, nset, d_method.oval, carg);
      } catch (...) {
	delete carg;
	throw;
      }
      delete carg;
      break;
    }
    robj->post (result);
    return result;
  }
}
