/* 
 * © 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 _GRT_MANAGER_H_
#define _GRT_MANAGER_H_

#include <grtpp.h>

#include "common.h"
#include "grt_dispatcher.h"
#include "grt_shell.h"
#include "grt_value_tree.h"
#include "grt_value_inspector.h"
#include "grt_structs_tree.h"
#include "grt_modules_tree.h"
#include "grt_message_list.h"

#include "wbpublic_public_interface.h"

#include "plugin_manager.h"


#ifdef _MSC_VER
#pragma make_public(::bec::GRTManager)
#endif

namespace bec {

  class Clipboard;

  /** Manages a GRT context and other assotiated objects useful for a GRT shell and other apps.
   *
   * @ingroup begrt
   */ 
  class WBPUBLICBACKEND_PUBLIC_FUNC GRTManager : public sigc::trackable
  {
    //XXX TODO delete this
    friend class GRTDispatcher;

  public:
    struct Timer
    {
      sigc::slot<bool> slot;
      GTimeVal next_trigger;
      double interval;

      Timer(const sigc::slot<bool> &slot, double interval);

      bool trigger();

      double delay_for_next_trigger(const GTimeVal &now);
    };

  public:
    /** Constructor.
     * 
     * @param threaded enable threading
     * @param verbose enable verbose output
     */ 
    GRTManager(bool threaded = true, bool verbose = false);
    virtual ~GRTManager();

    static GRTManager *get_instance_for(grt::GRT *grt);

    void set_basedir(const std::string &path);
    std::string get_basedir() { return _basedir; }

    void set_datadir(const std::string &path);
    std::string get_data_file_path(const std::string &file);

    void set_user_datadir(const std::string &path);
    std::string get_user_datadir() { return _user_datadir; }

    std::string get_tmp_dir();
    std::string get_unique_tmp_subdir();
    void cleanup_tmp_dir();

    void set_use_log_file(bool value);
    void add_log_file_entry(const char *text, size_t size);

    void set_module_extensions(const std::list<std::string> &extensions);

    void rescan_modules();
    int do_scan_modules(const std::string &path, const std::list<std::string> &exts, bool refresh);
    void scan_modules_grt(grt::GRT *grt, const std::list<std::string> &extensions, bool refresh);

    void set_clipboard(Clipboard *clipb);

    Clipboard *get_clipboard() { return _clipboard; }

    void set_search_paths(const std::string &module_sp,
                          const std::string &struct_sp,
                          const std::string &libraries_sp);
    
    void set_user_extension_paths(const std::string &user_module_path,
                                  const std::string &user_library_path,
                                  const std::string &user_script_path);
    
    std::string get_user_module_path() const { return _user_module_path; }
    std::string get_user_library_path() const { return _user_library_path; }
    std::string get_user_script_path() const { return _user_script_path; }

    // main window statusbar text
    void push_status_text(const std::string &message);
    void replace_status_text(const std::string &message);
    void pop_status_text();
    void set_status_slot(const sigc::slot<void, std::string> &slot);

    void set_message_callback(const sigc::slot<void, grt::Message> &handler);

    void set_error_callback(const sigc::slot<void,std::string,std::string> &cb);
    void set_progress_callback(const sigc::slot<bool,std::string,std::string,float> &cb);

    GRTDispatcher *get_dispatcher() const { return _dispatcher; };
    void dispatch_task(GRTTask *task);

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

    void initialize(const std::string &loader_module_path= "");
    bool initialize_shell(const std::string &shell_type);

    void perform_idle_tasks();

    PluginManager *get_plugin_manager() const { return _plugin_manager; }

    bool is_threaded() { return _threaded; }
    bool in_main_thread();

    // shell
    ShellBE *get_shell();

    void execute_grt_task(const std::string &title,
                          const sigc::slot1<grt::ValueRef, grt::GRT*> &function,
                          const sigc::slot<void,grt::ValueRef> &finished_cb,
                          bool show_progress);


    // message displaying (as dialogs)
    void show_error(const std::string &message, const std::string &detail, bool important= true);
    void show_warning(const std::string &title, const std::string &message, bool important= false);
    void show_message(const std::string &title, const std::string &message, bool important= false);


    // grt internal state inspectors
    ValueInspectorBE *get_new_value_inspector(const grt::ValueRef &value, bool process_editas_flag);
    ValueInspectorBE *get_new_grouped_object_inspector(const grt::ValueRef &object, bool process_editas_flag);

    MessageListBE *get_messages_list();

    // these always return the same instance
    StructsTreeBE *get_shared_structs_tree();
    ModulesTreeBE *get_shared_modules_tree();
    ValueTreeBE *get_shared_value_tree(const std::string &valuespec);

    //
    void set_app_option_slot(const sigc::slot<grt::ValueRef,std::string> &slot);
    grt::ValueRef get_app_option(const std::string &name);
    std::string get_app_option_string(const std::string &name);

    // signals are private so that we can properly protect them with mutexes
    void run_when_idle(const sigc::slot<bool> &slot);
    void block_idle_tasks();
    void unblock_idle_tasks();

    Timer *run_every(const sigc::slot<bool> &slot, double seconds);
    void cancel_timer(Timer *timer);
    double delay_for_next_timeout();

    void set_timeout_request_slot(const sigc::slot<void> &slot);

    void flush_timers();

    void terminate() { _terminated= true; };
    bool terminated() { return _terminated; };
    void reset_termination() {_terminated= false; };

    
    
  protected:
    grt::GRT *_grt;
    GRTDispatcher *_dispatcher;
    GMutex *_idle_mutex;
    GMutex *_timer_mutex;

    PluginManager *_plugin_manager;

    Clipboard *_clipboard;


    ShellBE *_shell;
    ValueTreeBE *_value_tree;
    StructsTreeBE *_structs_tree;
    ModulesTreeBE *_modules_tree;

    MessageListBE *_messages_list;

    sigc::slot<void, std::string> _status_text_slot;
    
    sigc::slot<void,grt::Message> _message_cb;

    sigc::slot<void,std::string,std::string> _error_cb;
    sigc::slot<bool,std::string,std::string,float> _progress_cb;

    std::list<Timer*> _timers;
    std::set<Timer*> _cancelled_timers;
    sigc::slot<void> _timeout_request;

    std::list<sigc::slot<bool> > _idle_slots;
    bool _idle_blocked;

    std::list<std::string> _module_extensions;

    std::string _basedir;
    std::string _datadir;
    std::string _user_datadir;
    std::string _module_pathlist;
    std::string _struct_pathlist;
    std::string _libraries_pathlist;
    
    std::string _user_module_path;
    std::string _user_library_path;
    std::string _user_script_path;

    sigc::slot<grt::ValueRef,std::string> _get_app_option_slot;
    
    bool _threaded;
    bool _verbose;

    virtual bool load_structs();
    virtual bool load_modules();
    virtual bool load_libraries();
    virtual bool init_module_loaders(const std::string &loader_module_path);

    bool init_loaders(const std::string &loader_module_path);
    
    void flush_shell_output();

    FILE *_log_file;

  private:
    bool _terminated; // true if application termination was requested by the BE or a plugin.
    static std::map<grt::GRT*,GRTManager*> _instances;
    
    grt::ValueRef setup_grt(grt::GRT *grt);

    void grt_default_msg_cb(const grt::Message &msg);

    void shell_write(const std::string &text);

    void task_started_cb(const std::string &title);
    void task_finished_cb(const grt::ValueRef &result);
    void task_error_cb(const std::exception &error, const std::string &title);

    void task_msg_cb(const grt::Message &msg, const std::string &title, bool show_progress);
  };

};

#endif /* _GRT_MANAGER_H_ */

