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

#include "Cons.hpp"
#include "Method.hpp"
#include "Runnable.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------
  
  // create a new method with a quark
  
  Method::Method (const long quark, Object* object) {
    d_type        = QUARK;
    d_method.qval = quark;
    p_iobj        = 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_iobj        = 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_iobj        = 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_iobj        = Object::iref (object);
    d_aflg        = aflg;
  }

  // destroy this method

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

  // 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                                                        -
  // -------------------------------------------------------------------------

  // set an object as a const object by quark
  
  Object* Method::cdef (Runnable* robj, Nameset* nset, const long quark,
			Object* object) {
    wrlock ();
    try {
      Object* result = nilp;
      if (d_type == OBJECT) {
	result = d_method.oval->cdef (robj, nset, quark, object);
      } else {
	result = p_iobj->cdef (robj, nset, quark, object);
      }
      robj->post (result);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // set an object by quark

  Object* Method::vdef (Runnable* robj, Nameset* nset, const long quark,
			Object* object) {
    wrlock ();
    try {
      Object* result = nilp;
      if (d_type == OBJECT) {	
	result = d_method.oval->vdef (robj, nset, quark, object);
      } else {
	result = p_iobj->vdef (robj, nset, quark, object);
      }
      robj->post (result);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // evaluate an object by quark

  Object* Method::eval (Runnable* robj, Nameset* nset, const long quark) {
    // get the read lock
    rdlock ();
    // define constant
    try {
      Object* result = p_iobj->eval (robj, nset, quark);
      robj->post (result);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // apply this object with a set of arguments
  
  Object* Method::apply (Runnable* robj, Nameset* nset, Cons* args) {
    // pre-evaluate argument list
    Cons* carg = nilp;
    try {
      Object* result = nilp;
      switch (d_type) {
      case Method::QUARK:
	if (d_aflg == true) 
	  result = p_iobj->Object::apply (robj, nset, d_method.qval, args);
	else
	  result = p_iobj->apply (robj, nset, d_method.qval, args);
	break;
      case Method::OBJECT:
	// evaluate the arguments as a list
	carg = Cons::eval (robj, nset, args);
	// object evaluation method
	if (d_aflg == true)
	  result = p_iobj->Object::apply (robj, nset, d_method.oval, carg);
	else
	  result = p_iobj->apply (robj, nset, d_method.oval, carg);
	break;
      }
      robj->post (result);
      delete carg;
      return result;
    } catch (...) {
      delete carg;
      throw;
    }
  }
}
