/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifndef _GRTDISPATCHER_H_
#define _GRTDISPATCHER_H_

#include <grtpp.h>
#include <grtpp_util.h>
#include <grtpp_shell.h>
#include "common.h"

#include "wbpublic_public_interface.h"

namespace bec {

  class WBPUBLICBACKEND_PUBLIC_FUNC GRTDispatcher;

  struct DispatcherCallbacks {
    grt::MessageSlot message_cb;
    grt::StatusQuerySlot status_query_cb;
  };

  // Mechanism for allowing queueing of callbacks to be executed
  // in the main thread by the GRT worked thread
  // The target object, method and arguments are all encapsulated
  // in the callback object.

  class DispatcherCallbackBase
  {
    GMutex *_mutex;
    GCond *_cond;
    int _refcount;

  public:
    DispatcherCallbackBase()
      : _refcount(1)
      {
        _mutex= g_mutex_new();
        _cond= g_cond_new();
      }
    
    DispatcherCallbackBase *retain()
    {
      _refcount++;
      return this;
    }
    
    void release()
    {
      _refcount--;
      if (_refcount <= 0)
        delete this;
    }
    
    virtual ~DispatcherCallbackBase()
    {
      signal();
      g_mutex_free(_mutex);
      g_cond_free(_cond);
    }
    
    virtual void execute()= 0;
    
    void wait()
    {
      g_mutex_lock(_mutex);
      g_cond_wait(_cond, _mutex);
      g_mutex_unlock(_mutex);
    }
    
    void signal()
    {
      g_cond_signal(_cond);
    }
  };
  
  
  template<class R>
    class DispatcherCallback : public DispatcherCallbackBase 
  {
    sigc::slot<R> _slot;
    
  public:
    R rvalue;
    
    DispatcherCallback(const sigc::slot<R> &slot)
      : DispatcherCallbackBase(), _slot(slot)
      {
      };
    
    void execute()
    {
      rvalue= _slot();
    }
    
    R get_result() { return rvalue; }
  };
  
  template<>
    class DispatcherCallback<void> : public DispatcherCallbackBase 
  {
    sigc::slot<void> _slot;
    
  public:
    DispatcherCallback(const sigc::slot<void> &slot= sigc::slot<void>())
      : DispatcherCallbackBase(), _slot(slot)
      {
      };
    
    void execute()
    {
      _slot();
    }
  };
  
  
  //---------------------------------------------------------------------------

  class WBPUBLICBACKEND_PUBLIC_FUNC GRTTaskBase 
  {
    friend class GRTDispatcher;
    
  public:
    GRTTaskBase(const std::string &name, GRTDispatcher *disp);
    virtual ~GRTTaskBase();

    inline bool is_finished() { return _finished; }

    virtual grt::ValueRef execute(grt::GRT *grt)= 0;

    // will make the task to not be executed by dispatcher when it's turn arrives
    // in the case of a GRTTask, the cancelled status will be returned for the
    // status_query callback
    void cancel();
    inline bool is_cancelled() { return _cancelled; }

    std::string name() { return _name; }
  
    void retain();
    void release();


    // _m suffix methods are called in the main thread
    // the other ones are called in the grt thread and 
    // schedule the call of their _m counterparts

    virtual void started();
    virtual void started_m();
    
    virtual void finished(const grt::ValueRef &result);
    virtual void finished_m(const grt::ValueRef &result);
    
    virtual void failed(const std::exception &exc);
    virtual void failed_m(const std::exception &exc);

    virtual void process_message(const grt::Message &msg);
    virtual void process_message_m(const grt::Message &msg);

    virtual int perform_status_query();

    grt::grt_runtime_error *get_error() { return _exception; };

  protected:
    GRTDispatcher *_dispatcher;
    DispatcherCallbacks _default_callbacks;

    grt::grt_runtime_error *_exception;

    void set_finished();

  private:
    std::string _name;
    int _refcount;
    bool _cancelled;
    bool _finished;

    grt::ValueRef __result;

    // should never be defined and called
    GRTTaskBase(GRTTaskBase&);
    GRTTaskBase& operator= (GRTTaskBase&);
  };
  
  
  class WBPUBLICBACKEND_PUBLIC_FUNC GRTTask : public GRTTaskBase 
  {
    typedef sigc::signal<void> StartedSignal;
    typedef sigc::signal<void,grt::ValueRef> FinishedSignal;
    typedef sigc::signal<void,std::exception> FailedSignal;
    typedef sigc::signal<void,const grt::Message&> ProcessMessageSignal; 

  public:
    GRTTask(const std::string &name, GRTDispatcher *owner, const sigc::slot1<grt::ValueRef, grt::GRT*> &function);

    //XXX replace with direct slots?
    StartedSignal &signal_started() { return _started; }
    FinishedSignal &signal_finished() { return _finished; }
    FailedSignal &signal_failed() { return _failed; }
    ProcessMessageSignal &signal_message() { return _message; }
    
  protected:
    sigc::slot1<grt::ValueRef, grt::GRT*> _function;
    
    StartedSignal _started;
    FinishedSignal _finished;
    FailedSignal _failed;
    ProcessMessageSignal _message;
    
    virtual grt::ValueRef execute(grt::GRT *grt);

    virtual void started_m();
    virtual void finished_m(const grt::ValueRef &result);
    virtual void failed_m(const std::exception &error);

    virtual void process_message_m(const grt::Message &msg);
  };

  
  class GRTShellTask : public GRTTaskBase
  {
    typedef sigc::signal<void,grt::ShellCommand,std::string> FinishedSignal;
    typedef sigc::signal<void,const grt::Message&> ProcessMessageSignal;

  public:
    GRTShellTask(const std::string &name, GRTDispatcher *owner, const std::string &command);

    FinishedSignal &signal_finished() { return _finished_signal; }
    ProcessMessageSignal &signal_message() { return _message; }

    inline std::string get_prompt() const { return _prompt; }
    inline grt::ShellCommand get_result() const { return _result; }

  protected:
    virtual grt::ValueRef execute(grt::GRT *grt);
    virtual void finished_m(const grt::ValueRef &result);

    virtual void process_message_m(const grt::Message &msg);

    FinishedSignal _finished_signal;
    ProcessMessageSignal _message;

    std::string _command;
    
    std::string _prompt;
    grt::ShellCommand _result;
  };


  //----------------------------------------------------------------------

  class WBPUBLICBACKEND_PUBLIC_FUNC GRTDispatcher 
  {
    friend class GRTTaskBase;
    GAsyncQueue *_task_queue;
    void (*_flush_main_thread_and_wait)();
    
    bool _busy;
    bool _threading_disabled;
    bool _worker_running;
    bool _worker_shutdown;
    
    GAsyncQueue *_callback_queue;
    
    GThread *_thread;
    
    static gpointer worker_thread(gpointer data);

    grt::GRT *_grt;
    //std::list<GRTTaskBase*> _current_task;

    void prepare_task(GRTTaskBase *task);
    void execute_task(GRTTaskBase *task);

    void worker_thread_init();
    void worker_thread_release();
    void worker_thread_iteration();

    void restore_callbacks(GRTTaskBase *task);

    void message_callback(const grt::Message &msg, GRTTaskBase *task);
    bool status_query_callback(GRTTaskBase *task);

  public:
    GRTDispatcher(grt::GRT *grt, bool threaded);
    virtual ~GRTDispatcher();

    grt::GRT *grt() { return _grt; };

    void execute_now(GRTTaskBase *task);
    
    void add_task(GRTTaskBase *task);
    grt::ValueRef add_task_and_wait(GRTTaskBase *task) THROW (grt::grt_runtime_error);

    grt::ValueRef execute_simple_function(const std::string &name, 
      const sigc::slot1<grt::ValueRef, grt::GRT*> &function) THROW (grt::grt_runtime_error);

    void execute_async_function(const std::string &name, 
      const sigc::slot1<grt::ValueRef, grt::GRT*> &function) THROW (grt::grt_runtime_error);

    void wait_task(GRTTaskBase *task);
    
    template<class R>
      R call_from_main_thread(const sigc::slot<R> &callback, bool wait)
      {
        DispatcherCallback<R> *cb= new DispatcherCallback<R>(callback);
        R result;
        
        call_from_main_thread(cb, wait);
        
        // result is only valid if wait = true
        result= cb->get_result();
        
        cb->release();
        
        return result;
      }
    
    
    void call_from_main_thread(DispatcherCallbackBase *callback, bool wait);
    
    void set_main_thread_flush_and_wait(void (*callback)());
    
    void start();
    void shutdown();
    
    bool get_busy();

    void cancel_task(GRTTaskBase *task);
    
    void flush_pending_callbacks();

    GThread *get_thread() const { return _thread; }
  };
  

  template<>
    inline void GRTDispatcher::call_from_main_thread<void>(const sigc::slot<void> &callback, bool wait)
    {
      DispatcherCallback<void> *cb= new DispatcherCallback<void>(callback);
      
      call_from_main_thread(cb, wait);
      
      cb->release();
    }

};


#endif /* _GRTDISPATCHER_H_ */
