// ---------------------------------------------------------------------------
// - Thread.cpp                                                              -
// - standard object library - thread 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 "Vector.hpp"
#include "Thread.hpp"
#include "Boolean.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "cthr.hpp"

namespace afnix {

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

  // this function is the object destructor as void pointer
  static void thrclean (void* obj) {
    Object* object = reinterpret_cast <Object*> (obj);
    Object::dref (object);
  }

  // this method start the thread
  static void* thrstart (void* args) {
    Runnable* pobj = reinterpret_cast <Runnable*> (args);
    return Object::iref (pobj->run ());
  }

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

  // create a new thread and call the run method

  Thread::Thread (t_tmode mode, Runnable* robj) {
    // no thread with nil parallel
    if (robj == nilp) 
      throw Exception ("thread-error", "invalid nil parallel object");
    // save the arguments
    Object::iref (p_robj = robj);
    d_mode = mode;

    // let the ball rolling - mark the parallel object as used so it is
    // unreferenced at the exit of the thread via thrclean
    Object::iref (p_robj);
    if (d_mode == NORMAL) 
      p_tid = c_thrstart (THR_NORMAL, thrstart, p_robj, thrclean);
    else
      p_tid = c_thrstart (THR_DAEMON, thrstart, p_robj, thrclean);

    if (p_tid == nilp)
      throw Exception ("thread-error", "cannot start thread");
  }

  // destroy this thread

  Thread::~Thread (void) {
    Object::dref (p_robj);
    c_thrdestroy (p_tid);
  }

  // return the class name

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

  // return true if we are the master thread

  bool Thread::ismaster (void) {
    return c_thrmaster ();
  }

  // set the main thread runnable object

  void Thread::setrobj (Runnable* robj) {
    c_thrsetmain (robj);
  }

  // get the main thread runnable

  Runnable* Thread::getrobj (void) {
    void* robj = c_thrgetobj ();
    return reinterpret_cast <Runnable*> (robj);
  }

  // wait for one thread to terminate

  void Thread::wait (const Thread& thr) {
    c_thrwait (thr.p_tid);
  }

  // wait for all threads to terminate

  void Thread::waitall (void) {
    if (c_thrmaster () == false) return;
    c_thrwaitall ();
  }

  // make the current thread terminate itself

  void Thread::exit (void) {
    c_threxit ();
  }

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

  // the reseult quark
  static const long QUARK_RESULT = String::intern ("result");  

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

  // the object supported quarks
  static const long QUARK_WAIT   = zone.intern ("wait");  
  static const long QUARK_NORMAL = zone.intern ("normal-p");  
  static const long QUARK_DAEMON = zone.intern ("daemon-p");  

  // return true if the given quark is defined

  bool Thread::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 this object with a quark
  
  Object* Thread::eval (Runnable* robj, Nameset* nset, const long quark) {
    if (quark == QUARK_RESULT) {
      Object* result = (Object*) c_thrgetres (p_tid);
      robj->post (result);
      return result;
    }
    return Object::eval (robj, nset, quark);
  }

  // apply this object with a quark and a set of arguments

  Object* Thread::apply (Runnable* robj, Nameset* nset, const long quark,
			 Vector* argv) {
    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_WAIT)   {
	c_thrwait (p_tid);
	return nilp;
      }
      if (quark == QUARK_NORMAL) {
	return new Boolean (d_mode == NORMAL);
      }
      if (quark == QUARK_DAEMON) {
	return new Boolean (d_mode == DAEMON);
      }
    }
    // call the object method
    return Object::apply (robj, nset, quark, argv);
  }
}
