/* -----------------------------------------------------------------------------
 * See the LICENSE file for information on copyright, usage and redistribution
 * of SWIG, and the README file for authors - http://www.swig.org/release.html.
 *
 * director.swg
 *
 * This file contains support for director classes that proxy
 * method calls from C++ to Ruby extensions.
 * ----------------------------------------------------------------------------- */

/*
  Use -DSWIG_DIRECTOR_NOUEH if you prefer to avoid the use of the
  Undefined Exception Handler provided by swift
*/
#ifndef SWIG_DIRECTOR_NOUEH
#ifndef SWIG_DIRECTOR_UEH
#define SWIG_DIRECTOR_UEH
#endif
#endif

#ifdef __cplusplus

#include <string>
#include <iostream>
#include <map>

namespace Swig {
  /* memory handler */
  struct GCItem 
  {
    virtual ~GCItem()
    {
    }

    virtual ruby_owntype get_own() const
    {
      return 0;
    }
  };
  
  struct GCItem_var
  {
    GCItem_var(GCItem *item = 0) : _item(item)
    {
    }

    GCItem_var& operator=(GCItem *item)
    {
      GCItem *tmp = _item;
      _item = item;
      delete tmp;
      return *this;
    }
    
    ~GCItem_var() 
    {
      delete _item;
    }
    
    GCItem * operator->() const
    {
      return _item;
    }
    
  private:
    GCItem *_item;
  };


  template <typename Type>
  struct GCItem_T : GCItem
  {
    GCItem_T(Type *ptr) : _ptr(ptr)
    {
    }
    
    virtual ~GCItem_T() 
    {
      delete _ptr;
    }
    
  private:
    Type *_ptr;
  };

  struct GCItem_Object : GCItem
  {
    GCItem_Object(ruby_owntype own) : _own(own)
    {
    }
    
    virtual ~GCItem_Object() 
    {
    }

    ruby_owntype get_own() const
    {
      return _own;
    }
    
  private:
    ruby_owntype _own;
  };


  template <typename Type>
  struct GCArray_T : GCItem
  {
    GCArray_T(Type *ptr) : _ptr(ptr)
    {
    }
    
    virtual ~GCArray_T() 
    {
      delete[] _ptr;
    }
    
  private:
    Type *_ptr;
  };


  /* body args */

  struct body_args {
    VALUE recv;
    ID id;
    int argc;
    VALUE *argv;
  };
  
  /* Base class for director exceptions */
  class DirectorException {
  protected:
    VALUE swig_error;
    std::string swig_msg;
  protected:
    DirectorException(VALUE error)
      : swig_error(error)
    {
    }
    
    DirectorException(VALUE error, const char* hdr, const char* msg ="") 
      : swig_error(error), swig_msg(hdr) {
      if (strlen(msg)) {
	swig_msg += " ";
	swig_msg += msg;
      }
      if (swig_msg.size()) {
	VALUE str = rb_str_new(swig_msg.data(), swig_msg.size());
	swig_error = rb_exc_new3(error, str);
      } else {
	swig_error = error;
      }
    }
  public:
    VALUE getType() const  { 
      return CLASS_OF(swig_error); 
    }
    VALUE getError() const {
      return swig_error;
    }
    const std::string& getMessage() const 
    {
      return swig_msg;
    }
    
    virtual ~DirectorException() {}
  };
  
  /* unknown exception handler  */

  class UnknownExceptionHandler 
  {
#ifdef SWIG_DIRECTOR_UEH
    static void handler() {
      try {
	throw;
      } catch (DirectorException& e) {
	std::cerr << "Swig Director exception caught:" << std::endl
		  << e.getMessage() << std::endl;
      } catch (std::exception& e) {
	std::cerr << "std::exception caught: "<< e.what() << std::endl;
      } catch (...) {
	std::cerr << "Unknown exception caught." << std::endl;
      }      
      std::cerr << std::endl
		<< "Ruby interpreter traceback:" << std::endl;
      std::cerr << std::endl;      
      std::cerr << "This exception was caught by the SWIG unexpected exception handler." << std::endl
		<< "Try using %feature(\"director:except\") to avoid reaching this point." << std::endl
		<< std::endl
		<< "Exception is being re-thrown, program will like abort/terminate." << std::endl;
      throw;
    }
    
  public:    
    std::unexpected_handler old;
    UnknownExceptionHandler(std::unexpected_handler nh = handler)
    {
      old = std::set_unexpected(nh);
    }

    ~UnknownExceptionHandler()
    {
      std::set_unexpected(old);
    }
#endif
  };


  /* Type mismatch in the return value from a Ruby method call */
  class DirectorTypeMismatchException : public Swig::DirectorException {
  public:
    DirectorTypeMismatchException(VALUE error, const char *msg="")
      : Swig::DirectorException(error, "Swig director type mismatch", msg) 
    {
    }

    DirectorTypeMismatchException(const char *msg="")
      : Swig::DirectorException(rb_eTypeError, "Swig director type mismatch", msg) 
    {
    }

    static void raise(VALUE error, const char *msg) {
      throw DirectorTypeMismatchException(error, msg);
    }

    static void raise(const char *msg) {
      throw DirectorTypeMismatchException(msg);
    }
  };

  /* Any Ruby exception that occurs during a director method call */
  class DirectorMethodException : public Swig::DirectorException {
  public:
    DirectorMethodException(VALUE error) 
      : Swig::DirectorException(error) {
    }

    DirectorMethodException(const char* msg = "") 
      : Swig::DirectorException(rb_eRuntimeError, "Swig director method error", msg) {
    }
    
    static void raise(VALUE error)
    {
      throw DirectorMethodException(error);
    }    
  };

  /* Attempted to call a pure virtual method via a director method */
  class DirectorPureVirtualException : public Swig::DirectorException
  {
  public:
    DirectorPureVirtualException(const char* msg = "") 
      : DirectorException(rb_eRuntimeError, "Swig director pure virtal method called", msg)
    { 
    }

    static void raise(const char *msg) 
    {
      throw DirectorPureVirtualException(msg);
    }
  };

  /* Simple thread abstraction for pthreads on win32 */
#ifdef __THREAD__
# define __PTHREAD__
# if defined(_WIN32) || defined(__WIN32__)
#  define pthread_mutex_lock EnterCriticalSection
#  define pthread_mutex_unlock LeaveCriticalSection
#  define pthread_mutex_t CRITICAL_SECTION
#  define SWIG_MUTEX_INIT(var) var
# else
#  include <pthread.h>
#  define SWIG_MUTEX_INIT(var) var = PTHREAD_MUTEX_INITIALIZER 
# endif
#endif

#ifdef  __PTHREAD__
  struct Guard
  {
    pthread_mutex_t *_mutex;
    
    Guard(pthread_mutex_t &mutex) : _mutex(&mutex)
    {
      pthread_mutex_lock(_mutex);
    }
    
    ~Guard()
    {
      pthread_mutex_unlock(_mutex);
    }
  };
# define SWIG_GUARD(mutex) Guard _guard(mutex)
#else
# define SWIG_GUARD(mutex) 
#endif

  /* director base class */
  class Director {
    private:
      /* pointer to the wrapped Ruby object */
      VALUE swig_self;
      /* flag indicating whether the object is owned by Ruby or c++ */
      mutable bool swig_disown_flag;
      /* shared flag for breaking recursive director calls */
      static bool swig_up;

#ifdef __PTHREAD__
      /* locks for sharing the swig_up flag in a threaded environment */
      static pthread_mutex_t swig_mutex_up;
      static bool swig_mutex_active;
      static pthread_t swig_mutex_thread;
#endif

      /* reset the swig_up flag once the routing direction has been determined */
#ifdef __PTHREAD__
      void swig_clear_up() const { 
        Swig::Director::swig_up = false; 
        Swig::Director::swig_mutex_active = false;
        pthread_mutex_unlock(&swig_mutex_up);
      }
#else
      void swig_clear_up() const { 
        Swig::Director::swig_up = false; 
      }
#endif

    public:
      /* wrap a Ruby object, optionally taking ownership */
      Director(VALUE self) : swig_self(self), swig_disown_flag(false) {
      }

      /* discard our reference at destruction */
      virtual ~Director() {
      }

      /* return a pointer to the wrapped Ruby object */
      VALUE swig_get_self() const { 
        return swig_self; 
      }

      /* get the swig_up flag to determine if the method call should be routed
       * to the c++ base class or through the wrapped Ruby object
       */
#ifdef __PTHREAD__
      bool swig_get_up() const { 
        if (Swig::Director::swig_mutex_active) {
          if (pthread_equal(Swig::Director::swig_mutex_thread, pthread_self())) {
            bool up = swig_up;
            swig_clear_up();
            return up;
          }
        }
        return false;
      }
#else 
      bool swig_get_up() const { 
        bool up = swig_up;
        swig_up = false;
        return up;
      }
#endif

      /* set the swig_up flag if the next method call should be directed to
       * the c++ base class rather than the wrapped Ruby object
       */
#ifdef __PTHREAD__
      void swig_set_up() const { 
        pthread_mutex_lock(&Swig::Director::swig_mutex_up);
        Swig::Director::swig_mutex_thread = pthread_self();
        Swig::Director::swig_mutex_active = true;
        Swig::Director::swig_up = true;
      }
#else 
      void swig_set_up() const { 
        Swig::Director::swig_up = true; 
      }
#endif

      /* acquire ownership of the wrapped Ruby object (the sense of "disown"
       * is from Ruby) */
      void swig_disown() const { 
        if (!swig_disown_flag) { 
          swig_disown_flag = true;
        } 
      }

    /* ownership managing */
  private:
    typedef std::map<void*, GCItem_var> ownership_map;
    mutable ownership_map owner;
#ifdef __PTHREAD__
    static pthread_mutex_t swig_mutex_own;
#endif
    

  public:
    template <typename Type>
    void swig_acquire_ownership_array(Type *vptr)  const
    {
      if (vptr) {
	SWIG_GUARD(swig_mutex_own);
	owner[vptr] = new GCArray_T<Type>(vptr);
      }
    }
    
    template <typename Type>
    void swig_acquire_ownership(Type *vptr)  const
    {
      if (vptr) {	
	SWIG_GUARD(swig_mutex_own);
	owner[vptr] = new GCItem_T<Type>(vptr);
      }
    }

    void swig_acquire_ownership_obj(void *vptr, ruby_owntype own) const
    {
      if (vptr && own) {
	SWIG_GUARD(swig_mutex_own);
	owner[vptr] = new GCItem_Object(own);
      }
    }
    
    ruby_owntype swig_release_ownership(void *vptr) const
    {
      ruby_owntype own = 0;
      if (vptr) {
	SWIG_GUARD(swig_mutex_own);
	ownership_map::iterator iter = owner.find(vptr);
	if (iter != owner.end()) {
	  own = iter->second->get_own();
	  owner.erase(iter);
	}
      }
      return own;
    }

  };

  bool Swig::Director::swig_up = false;

#ifdef __PTHREAD__
  MUTEX_INIT(Swig::Director::swig_mutex_up);
  pthread_t Swig::Director::swig_mutex_thread;
  bool Swig::Director::swig_mutex_active = false;
#endif
}

#endif /* __cplusplus */


