
#ifndef _WB_CONTEXT_H_
#define _WB_CONTEXT_H_

#include "wb_config.h"

#ifndef _WIN32
#include <vector>
#endif

#include "grt/ui_form.h"
#include "grt/grt_manager.h"
#include "grt/plugin_manager.h"
#include "grt/icon_manager.h"

#include "grts/structs.workbench.h"
#include "wb_backend_public_interface.h"

#include "wbcanvas/model_model_impl.h"

#include "wb_context_model.h"


#include <grtpp_undo_manager.h>

#include "mdc.h"


#define APP_MAJOR_NUMBER 5
#define APP_MINOR_NUMBER 1
#define APP_RELEASE_NUMBER 18
#define APP_REVISION_NUMBER 4456



#define WBContext_VERSION 5


namespace bec {
  class Clipboard;
};

namespace wb {
  class WBContextUI;

  class WorkbenchImpl;
  class WBComponent;

  class ModelFile;

  enum RefreshType 
  {
    RefreshNeeded, // frontend should schedule a refresh flush asap (usually called in worker thread)

    //RefreshCatalog,
    //RefreshSchema, //!< refreshes the info about the schema (not contents) arg: schema_id
    //RefreshSchemaContents, //!< refreshes contents of a schema (tables, views etc) arg: schema_id/listname


    RefreshNothing,
    RefreshLayers, // argument: object-id (whole tree if "")
    RefreshSchemaList,
    RefreshSchema,
    RefreshNewView,
    RefreshSelection,
    RefreshToolbar,
    RefreshMessages,
    RefreshCloseEditor, // argument: object-id (close all if "")
    RefreshFindOutput,
    RefreshHistory,
    RefreshUserTypes,

    RefreshOverviewNodeInfo,    // argument: node-id, overview ptr (bec::UIForm*)
    RefreshOverviewNodeChildren,// argument: node-id, overview ptr 

    RefreshCatalogTreeNode,
    RefreshViewName,
    RefreshDocument,
    RefreshCloseDocument,
    RefreshZoom,
    RefreshMenubar,
    RefreshTimer
  };

  
  struct WBShortcut
  {
    std::string shortcut;
    mdc::KeyInfo key;
    mdc::EventState modifiers;

    std::string name;
    std::string command;
  };


  struct MYSQLWBBACKEND_PUBLIC_FUNC WBPaperSize
  {
    std::string name;
    std::string caption;
    double width;
    double height;
    bool margins_set;
    double margin_top;
    double margin_bottom;
    double margin_left;
    double margin_right;
    std::string description;
  };

  // context names
  #define WB_CONTEXT_GLOBAL  "*global"
  #define WB_CONTEXT_MODEL   "model"
  #define WB_CONTEXT_EDITOR "editor"
  #define WB_CONTEXT_MAIN_OVERVIEW "overview.main"
  #define WB_CONTEXT_PHYSICAL_OVERVIEW "overview.physical"
  
  // basic toolbar names
  #define WB_TOOLBAR_MAIN    "main"
  #define WB_TOOLBAR_TOOLS   "tools"
  #define WB_TOOLBAR_OPTIONS "options"

  class ModelDiagramForm;
  class FindDialogBE;

  enum PageOrientation
  {
    Landscape,
    Portrait
  };

  
  struct MYSQLWBBACKEND_PUBLIC_FUNC WBFrontendCallbacks
  {
    // Show a dialog displaying the text as an internal error
    // Args: message, details
    sigc::slot<void,std::string,std::string> show_internal_error;

    // Show dialogs displaying an error/warning/info message, along
    // with its title
    sigc::slot<void,std::string,std::string> show_error;
    sigc::slot<void,std::string,std::string> show_warning;
    sigc::slot<void,std::string,std::string> show_message;

    // Shows a confirmation dialog, which must return true if confirmed.
    // Args: title, message, ok caption, cancel caption, alt caption
    // Returns: 1 ok ok, 0 for cancel, -1 for alt
    sigc::slot<int,std::string,std::string,std::string,std::string,std::string> confirm_action;

    // Args: type, title, file extensions
    sigc::slot<std::string,std::string,std::string,std::string> show_file_dialog;

    // Show some text in the application's status bar: must be thread-safe
    sigc::slot<void,std::string> show_status_text;
    // Displays a progress bar
    // Args: title, status, percentage completed
    // Must return false if the action must be cancelled
    sigc::slot<bool,std::string,std::string,float> show_progress;

    // ok/cancel, title, flags, returnval
    sigc::slot<bool, std::string, int, std::string&> request_input;

    // Open an editor
    // Args: grtmanager, module containing plugin, editor dll, editor class, edited object
    sigc::slot<NativeHandle, bec::GRTManager*, grt::Module*, std::string, std::string, grt::BaseListRef, bec::GUIPluginFlags> open_editor;
    // Show/Hide an editor
    // Args: editor handle (e.g: window handle)
    sigc::slot<void, NativeHandle> show_editor;
    sigc::slot<void, NativeHandle> hide_editor;

    // Execute a built-in command
    sigc::slot<void, std::string> perform_command;

    // Create a canvas view
    sigc::slot<mdc::CanvasView*, std::string, std::string> create_view;
    // Destroy a previously created canvas view
    sigc::slot<void, mdc::CanvasView*> destroy_view;
    // Signals the current view has been changed
    sigc::slot<void, mdc::CanvasView*> switched_view;
    
    // The tool for the view has been changed
    sigc::slot<void, mdc::CanvasView*> tool_changed;

    // Refresh interface
    sigc::slot<void, RefreshType, std::string, NativeHandle> refresh_gui;
    sigc::slot<void,bool> lock_gui;

    // Show a Popup Menu on the canvas
    // Args: items, x, y
    sigc::slot<void, bec::MenuItemList*, int, int> popup_menu;

    sigc::slot<void,std::string> print_output;

    // Displays output from the GRT
    sigc::slot<void,std::string> shell_output;
    // Signals the GRT shell is ready
    sigc::slot<void,std::string> next_shell_command;
    
    // Closes the application
    sigc::slot<void> quit_application;
  };



    
  struct MYSQLWBBACKEND_PUBLIC_FUNC WBOptions
  {
    std::string basedir;
    std::string plugin_search_path;
    std::string struct_search_path;
    std::string module_search_path;
    std::string library_search_path;
    std::string cdbc_driver_search_path;
    std::string user_data_dir;
    std::string open_at_startup;
    bool force_sw_rendering;
    bool force_opengl_rendering;
    bool use_log_file;

    WBOptions() : force_sw_rendering(false), force_opengl_rendering(false), use_log_file(false) {}
  };

  
#define FOREACH_COMPONENT(list, iter) for (std::vector<WBComponent*>::iterator iter= list.begin(); iter != list.end(); ++iter)

  class MYSQLWBBACKEND_PUBLIC_FUNC WBContext : public sigc::trackable, public ModelBridgeDelegate
  {
    friend class WorkbenchImpl;
    friend class WBComponent;

   public:
    WBContext(WBContextUI *ui, bool verbose = false);
    ~WBContext();

    bool use_opengl() { return _use_opengl; }
    int using_opengl() { return _using_opengl; }
    bool is_opengl_available();

    bool init_(WBFrontendCallbacks *callbacks, WBOptions *options);
    void init_finish_(WBOptions *options);
    void finalize();
    
    WBContextUI *get_ui() { return _uicontext; }

    bec::UIForm *get_active_form();
    bec::UIForm *get_active_main_form();

    WBContextModel *get_model_context() { return &_model_context; }
    
    // Document

    // create document
    void new_document();
    void close_document(bool destroying);
    
    void close_document_finish();

    // save document

    bool save_as(const std::string &path);

    std::string get_filename() const;

    void export_png(const std::string &path);
    void export_pdf(const std::string &path);
    void export_ps(const std::string &path);
    void export_svg(const std::string &path);



    // plugins
    void execute_plugin(const std::string &plugin_name, const grt::BaseListRef &args= grt::BaseListRef());



    // Diagrams
    void add_new_diagram(const model_ModelRef &model);
    bool delete_diagram(const model_DiagramRef &view);

    void switch_diagram(const model_DiagramRef &view);

    // GUI Plugin
    void register_builtin_plugins(grt::ListRef<app_Plugin> plugins);

    void close_gui_plugin(NativeHandle handle);

    bool delete_object(model_ObjectRef object, std::map<std::string, bool>& options);


    //
    void request_refresh(RefreshType type, const std::string &str, NativeHandle ptr= (NativeHandle)0);
    void clear_refreshes();

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

    bool open_document(const std::string &file);
    void open_recent_document(int index);
    bool has_unsaved_changes();
    bool save_changes();
    void realize();

    void activate_canvas_object(const model_ObjectRef &object, int flags);

    bec::PluginManager *get_plugin_manager() { return _plugin_manager; }
    template<class C> C *get_component()
    {
      return dynamic_cast<C*>(get_component_named(C::name()));
    }

    WBComponent *get_component_named(const std::string &name);

    WBComponent *get_component_handling(const model_ObjectRef &object);

    bool check_plugin_input_available(const app_PluginInputDefinitionRef &pdef);
    bool check_plugin_runnable(const app_PluginRef &plugin, const std::string &extra_arg);

    void foreach_component(const sigc::slot<void, WBComponent*> &slot);


    bec::GRTManager *get_grt_manager() const { return _manager; }
    grt::GRT *get_grt() const { return _manager->get_grt(); }
    bec::Clipboard *get_clipboard() const { return _clipboard; }

    workbench_WorkbenchRef get_root();
    workbench_DocumentRef get_document();
    grt::DictRef get_wb_options();

    std::string get_datadir() const { return _datadir; }
    
    template<class C> grt::Ref<C> get_parent_for_object(const GrtObjectRef &object)
    {
      GrtObjectRef obj= object;
      while (obj.is_valid() && !obj.is_instance(C::static_class_name()))
        obj= obj->owner();
      return grt::Ref<C>::cast_from(obj);
    }

    void flush_idle_tasks();

 // utilities for error reporting
    void show_exception(const std::string &operation, const std::exception &exc);
    void show_exception(const std::string &operation, const grt::grt_runtime_error &exc);

        
   template<class R>
    R execute_in_main_thread(const std::string &name, 
                             const sigc::slot<R> &function) THROW (grt::grt_runtime_error)
    {
      return _manager->get_dispatcher()->call_from_main_thread/*<R>*/(function, true);
    }
    void execute_in_main_thread(const std::string &name, 
                              const sigc::slot<void> &function, bool wait) THROW (grt::grt_runtime_error);
 
    grt::ValueRef execute_in_grt_thread(const std::string &name, 
                                            const sigc::slot1<grt::ValueRef, grt::GRT*> &function) THROW (grt::grt_runtime_error);

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

    void open_object_editor(const GrtObjectRef &object, bool newwindow= false);

    model_DiagramRef get_view_with_id(const std::string &id);

    int get_object_list_popup_items(bec::UIForm *form, const grt::ListRef<GrtObject> &objects,
      const std::string &label, const std::list<std::string> &groups, bec::MenuItemList &items);
   
    std::string get_object_tooltip(const model_ObjectRef &object, mdc::CanvasItem *item);

    std::string create_attached_file(const std::string &group, const std::string &tmpl);
    void save_attached_file_contents(const std::string &name, const char *data, size_t size);
    std::string get_attached_file_contents(const std::string &name);
    std::string get_attached_file_tmp_path(const std::string &name);
    void delete_attached_file(const std::string &name);
    std::string recreate_attached_file(const std::string &name, const std::string &data);
    int export_attached_file_contents(const std::string &name, const std::string &export_to);

    void block_user_interaction(bool flag);
    bool user_interaction_allowed() { return _user_interaction_blocked == 0; }
    
    // State handling.
    std::string read_state(const std::string &name, const std::string &domain, const std::string &default_value);
    int read_state(const std::string &name, const std::string &domain, const int &default_value);
    double read_state(const std::string &name, const std::string &domain, const double &default_value);
    bool read_state(const std::string &name, const std::string &domain, const bool &default_value);
     
    void save_state(const std::string &name, const std::string &domain, const std::string &value);
    void save_state(const std::string &name, const std::string &domain, const int &value);
    void save_state(const std::string &name, const std::string &domain, const double &value);
    void save_state(const std::string &name, const std::string &domain, const bool &value);

  protected:
    bec::GRTManager *_manager;
    bec::PluginManager *_plugin_manager;

    workbench_WorkbenchRef _wb_root;

    std::string _opengl_version;
    int _user_interaction_blocked;
    int _opengl_available;
    bool _use_opengl;
    int _using_opengl; // -1 dont know, 0 no, 1 yes
    bool _send_messages_to_shell;
    
    std::string _datadir;
    std::string _user_datadir;

    struct RefreshRequest
    {
      RefreshType type;
      std::string str;
      NativeHandle ptr;
      double timestamp;
    };

    std::list<RefreshRequest> _pending_refreshes;
    GMutex *_pending_refresh_mutex;

    WBContextUI *_uicontext;
    WBContextModel _model_context;

    std::vector<WBComponent*> _components;

    WorkbenchImpl *_wb_module;

    bec::Clipboard *_clipboard;


    ModelFile *_file;
    std::string _filename;
    // only used for comparing pointers
    grt::UndoAction *_save_point;


    void delete_canvas_item(mdc::CanvasItem *item);

    grt::ListRef<app_PaperType> get_paper_types(grt::GRT *grt);

    // setup
    void init_grt_tree(grt::GRT *grt, WBOptions *options);
    void run_init_scripts_grt(grt::GRT *grt, WBOptions *options);
    void init_plugins_grt(grt::GRT *grt, WBOptions *options);
    void init_plugin_groups_grt(grt::GRT *grt, WBOptions *options);
    void init_object_listeners_grt(grt::GRT *grt);
    void init_properties_grt(workbench_DocumentRef &doc);


    grt::ValueRef setup_context_grt(grt::GRT *grt, WBOptions *options);

    void set_default_options(grt::DictRef options);

    void init_canvas_grt(grt::GRT *grt, WBOptions *options);

    void load_app_options(bool update);
    void save_app_options();

    void load_app_state();
    void save_app_state();

    void add_recent_file(const std::string &file);

    grt::ValueRef new_document_grt(grt::GRT *grt);
    grt::ValueRef save_grt(grt::GRT *grt);

    grt::ValueRef get_plugin_input_value(const app_PluginInputDefinitionRef &pdef);

    int add_object_plugins_to_popup_menu(const grt::ListRef<GrtObject> &objects,
      const std::list<std::string> &groups, bec::MenuItemList &items);

    grt::ValueRef execute_plugin_grt(grt::GRT *grt, const app_PluginRef &plugin, const grt::BaseListRef &args);
    void plugin_finished(const grt::ValueRef &result, const app_PluginRef &plugin);

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

    void reset_document();
    void reset_listeners();

    void option_dict_changed(grt::internal::OwnedDict*dict=0, bool added=false, const std::string& key="");

    void diagram_object_list_changed(grt::internal::OwnedList *list, bool added, const grt::ValueRef &value, ModelDiagramForm *vform);

  public:
    void update_view_page_settings();

    ModelFile *get_file() { return _file; }

  private:
    // delegate functions from ModelBridgeDelegate
    virtual cairo_surface_t *fetch_image(const std::string &file);
    virtual std::string attach_image(const std::string &file);
    virtual void release_image(const std::string &name);

    virtual mdc::CanvasView *create_canvas_view(const model_DiagramRef &view);
    virtual void free_canvas_view(mdc::CanvasView *view);

    mdc::CanvasView* create_canvas_view_main(const model_DiagramRef &mview);


    bool do_request_input(const std::string &title, int flags, char **text);
    void attempt_options_upgrade(xmlDocPtr xmldoc, const std::string &version);

  public: // frontend callbacks
    sigc::slot<void,std::string,std::string> show_internal_error;
    sigc::slot<void,std::string,std::string> show_error;
    sigc::slot<void,std::string,std::string> show_warning;
    sigc::slot<void,std::string,std::string> show_message;
    sigc::slot<void,std::string> print_output;
    sigc::slot<void,std::string> shell_output;

    // return 1 for ok, 0 for cancel, -1 for alt
    sigc::slot<int,std::string,std::string,std::string,std::string,std::string> confirm_action;
    sigc::slot<std::string,std::string,std::string,std::string> show_file_dialog;

    sigc::slot<void,std::string> show_status_text;

    sigc::slot<bool,std::string,int,std::string&> request_input;

    sigc::slot<void,std::string,void*> show_gui_plugin;

    sigc::slot<mdc::CanvasView*, std::string, std::string> create_view;
    sigc::slot<void, mdc::CanvasView*> destroy_view;
    sigc::slot<void, mdc::CanvasView*> switched_view;

    sigc::slot<void, mdc::CanvasView*> tool_changed;

    sigc::slot<void, RefreshType, std::string, NativeHandle> refresh_gui;
    sigc::slot<void,bool> lock_gui;

    sigc::slot<void, bec::MenuItemList*, int, int> popup_menu;

    sigc::slot<void, std::string> perform_command;
    
    sigc::slot<void> quit_application;
  };

  struct GUILock
  {
    WBContext *_wb;
    
    GUILock(WBContext *wb) : _wb(wb) { _wb->block_user_interaction(true); }
    ~GUILock() { _wb->block_user_interaction(false); }
  };
  
};


#endif /* _WB_CONTEXT_H_ */
