#include "config.h"

#include "main_form.h"
// the rest, backend, etc ...
#include "workbench/wb_context_ui.h"
#include "workbench/wb_model_diagram_form.h"
#include "workbench/wb_overview.h"
#include "workbench/wb_overview_physical.h"
#include "workbench/wb_overview_workspace.h"
#include "linux_utilities/plugin_editor_base.h"

#include "diagram_size_form.h"
#include "info_box.h"

#include "mdc.h"

#include "gtk_helpers.h"

#include "output_box.h"
#include "model_diagram_panel.h"
#include "overview_panel.h"
#include "find_panel.h"

#include "model_sidebar.h"

#include "MGGladeXML.h"

#include "toolbar_manager.h"

#include "shell/shell_view.h"


#ifdef EDITION_OSS
# define SPLASH_IMAGE "wb_splashscreen_oss.png"
#else
# define SPLASH_IMAGE "wb_splashscreen_se.png"
#endif

static void set_window_icons(Gtk::Window *window)
{
  std::vector<Glib::RefPtr<Gdk::Pixbuf> > icons;

  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbenchDoc-16.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbenchDoc-24.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbenchDoc-32.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbenchDoc-48.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbenchDoc-64.png", false));
  icons.push_back(ImageCache::get_instance()->image_from_filename("MySQLWorkbenchDoc-128.png", false));
  
  window->set_icon_list(icons);
}


//------------------------------------------------------------------------------
// @ctx is passed from Program class, see Program::Program
MainForm::MainForm(wb::WBContextUI* ctx)
  : _wbui_context(ctx),
    _output_box(0),
    _model_sidebar(ctx),
    _grt_sidebar(ctx->get_wb()->get_grt_manager())
{  
  bec::GRTManager *grtm= _wbui_context->get_wb()->get_grt_manager();
  _ui= new MGGladeXML(grtm->get_data_file_path("wb.glade").c_str(), "", "");
  _toolbar_manager = new ToolbarManager(ctx->get_command_ui());

  get_mainwindow()->signal_delete_event().connect(sigc::mem_fun(this, &MainForm::close_window));
  get_mainwindow()->signal_set_focus().connect(sigc::mem_fun(this, &MainForm::on_focus_widget));
  
  _accel_group= Gtk::AccelGroup::create();
  _menu_manager= new MenuManager(ctx->get_command_ui(), _accel_group);
  get_mainwindow()->add_accel_group(_accel_group);

  wb::OverviewBE* overview_be = _wbui_context->get_physical_overview();
  // Create the model overview panel
  _overview= new OverviewPanel(_wbui_context, overview_be, _menu_manager);

  _find_panel= 0;
  
  // setup shell view
  _shell_view= new ShellCommandView(grtm);
  
  Gtk::Notebook *note;
  // setup sidebars
  _ui->get("side_note", &note);
  note->append_page(*_model_sidebar.get_base(), _model_sidebar.get_title());

  _ui->get("side_note", &note);
  note->append_page(*_grt_sidebar.get_base(), _grt_sidebar.get_title());

  _model_sidebar.get_info_box()->signal_open_diagram_options().
    connect(sigc::mem_fun(this, &MainForm::show_diagram_options));
  
  note= get_lower_note();
  
  note->remove_page(0); // removes the dummy page the from glade file

  // set shell panel to hidden initially
  append_tab_page(note, _shell_view, _("GRT Shell"), true);
  _shell_view->signal_hide().connect(sigc::bind(sigc::mem_fun(this, &MainForm::toggle_lower_tab_page), _shell_view, false));
  _shell_view->hide();
  
  // set the GRT sidepanel to hidden initially
  toggle_lower_tab_page(_shell_view, false);


  // set output panel to hidden initially
  _output_box= Gtk::manage(new OutputBox(grtm));
  append_tab_page(note, _output_box, _("Output"), true);
  _output_box->hide();
  _output_box->get_be()->signal_show().connect(sigc::mem_fun(*this, &MainForm::show_output));

  get_upper_note()->signal_switch_page().connect(sigc::mem_fun(*this, &MainForm::switch_page));

  Gtk::Statusbar *status = 0;
  _ui->get("statusbar1", &status);
  if (status)
  {
    status->pack_end(_progress_bar, false, false);
    _progress_bar.show();
  }

  _gui_locked= false;

  //  switch_page(0, 0);
}

//------------------------------------------------------------------------------
MainForm::~MainForm()
{
  delete _shell_view;
  delete _ui;

  delete _menu_manager;
  delete _toolbar_manager;
  delete _overview;
}


//------------------------------------------------------------------------------
bool MainForm::close_window(GdkEventAny *ev)
{
  if (_wbui_context->request_quit())
    _wbui_context->perform_quit();

  return true;
}


//------------------------------------------------------------------------------
void MainForm::on_focus_widget(Gtk::Widget *w)
{
  if (_ui)
  {
    void *data = 0;
    bec::UIForm* form = 0;
    
    while (!data && w)
    {
      data = w->get_data("uiform");
      if (data)
      {
        form = reinterpret_cast<bec::UIForm*>(data);
        break;
      }
      w = w->get_parent();
    }
    
    if (form)
      _wbui_context->set_active_form(form);
  }
}

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

void MainForm::setup_ui()
{
  add_form_pane(_overview);

  set_window_icons(get_mainwindow());


  register_commands();
  
  _overview->rebuild_all();
  Gtk::Alignment* menu_holder;
  _menu_manager->rebuild(_ui->get("main_menu_box", &menu_holder));
  
  refresh_toolbar("");
  
  reset_layout();
  
  switch_page(0, 0);
}


//------------------------------------------------------------------------------
void MainForm::show()
{
  get_mainwindow()->show();
}


//------------------------------------------------------------------------------
Gtk::Window* MainForm::get_mainwindow() const
{
  Gtk::Window *win = 0;
  _ui->get("wb_main_window", &win);
  
  return win;
}


Gtk::Notebook *MainForm::get_upper_note() const
{
  Gtk::Notebook *note= 0;
  _ui->get("model_tabs", &note);
  return note;
}


Gtk::Notebook *MainForm::get_lower_note() const
{
  Gtk::Notebook *note= 0;
  _ui->get("plugins_tabs", &note);
  return note;
}


//------------------------------------------------------------------------------
static bool change_status(Gtk::Statusbar *status, const std::string& text)
{
  status->pop();
  status->push(text);
  return false;
}


void MainForm::show_status_text_becb(const std::string& text)
{
  Gtk::Statusbar *status = 0;
  
  _ui->get("statusbar1", &status);
  
  // execute when idle in case we're being called from the worker thread
  Glib::signal_idle().connect(sigc::bind<Gtk::Statusbar*,std::string>(sigc::ptr_fun(change_status), status, text));
}


//------------------------------------------------------------------------------
bool MainForm::show_progress_becb(const std::string& title, const std::string& status, float pct)
{
  if (pct < 0.0)
    pct = 0.0;

  _progress_bar.set_fraction(pct);
  _progress_bar.set_text(status);

  return true;
}


//------------------------------------------------------------------------------
NativeHandle MainForm::open_plugin_becb(bec::GRTManager* mgr, grt::Module *grtmodule,
                                        const std::string& shlib, const std::string& editor_class,
                                        grt::BaseListRef args, bec::GUIPluginFlags flags)
{
  GUIPluginCreateFunction create_function= 0;
  std::string path= grtmodule->path();
  std::string full_path= path.substr(0, path.rfind('/')+1)+shlib;

  // if not forcing a new window creation, check if there's an editor already open for it
  if (!(flags & bec::ForceNewWindowFlag))
  {
    std::vector<NativeHandle> handles= 
      mgr->get_plugin_manager()->get_similar_open_plugins(grtmodule, editor_class, args);

    if (!handles.empty())
    {
      GUIPluginBase* guiplugin = reinterpret_cast<GUIPluginBase*>(handles[0]);
      // try to reuse the plugin
      PluginEditorBase *editor= dynamic_cast<PluginEditorBase*>(guiplugin);
      
      if (editor && editor->switch_edited_object(mgr, args))
      {
        mgr->get_plugin_manager()->forget_gui_plugin_handle(handles[0]);
        return handles[0];
      }
    }
  }
  
  // lookup for the editor_class symbol (create<class>) in the shlib
  GModule *module= g_module_open(full_path.c_str(), (GModuleFlags)G_MODULE_BIND_LOCAL);
  if (!module)
  {
    g_warning("Could not open editor shared object '%s'", full_path.c_str());
    return 0;
  }
  
  if (!g_module_symbol(module, ("create"+editor_class).c_str(), (void**)&create_function)
      || !create_function)
  {
    g_warning("UI creation function '%s' not found in %s", 
              ("create"+editor_class).c_str(), full_path.c_str());
    g_module_close(module);
    return 0;
  }

  // call the initializer with arguments
  GUIPluginBase *object= (*create_function)(grtmodule, mgr, args);
  if (!object)
  {
    g_warning("UI creation function from %s returned 0", full_path.c_str());
    g_module_close(module);
    return 0;
  }

  g_module_close(module);
  
  PluginEditorBase *editor= dynamic_cast<PluginEditorBase*>(object);
  if (editor)
  {
    // add it to the bottom panel
    add_plugin_pane(editor);
  }
  else
  {
    //object->execute();
  }
  
  // return the pointer
  return reinterpret_cast<NativeHandle>(object);
}


//------------------------------------------------------------------------------
void MainForm::show_plugin_becb(NativeHandle handle)
{
  GUIPluginBase *plugin= reinterpret_cast<GUIPluginBase*>(handle);
  
//  plugin->show_plugin();

  if (dynamic_cast<PluginEditorBase*>(plugin))
    show_lower_tab(dynamic_cast<PluginEditorBase*>(plugin));
}


//------------------------------------------------------------------------------
void MainForm::hide_plugin_becb(NativeHandle handle)
{
//  reinterpret_cast<GUIPluginBase*>(handle)->hide_plugin();
}


void MainForm::call_copy()
{
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_copy())
    _wbui_context->get_active_form()->copy();
  else
  {
    Gtk::Widget *focused= get_mainwindow()->get_focus();
    
    if (dynamic_cast<Gtk::Editable*>(focused))
      dynamic_cast<Gtk::Editable*>(focused)->copy_clipboard();
  }
}


void MainForm::call_cut()
{
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_cut())
    _wbui_context->get_active_form()->cut();
  else
  {
    Gtk::Widget *focused= get_mainwindow()->get_focus();
  
    if (dynamic_cast<Gtk::Editable*>(focused))
      dynamic_cast<Gtk::Editable*>(focused)->cut_clipboard();
  }
}


void MainForm::call_paste()
{
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_paste())
    _wbui_context->get_active_form()->paste();
  else
  {
    Gtk::Widget *focused= get_mainwindow()->get_focus();
  
    if (dynamic_cast<Gtk::Editable*>(focused))
      dynamic_cast<Gtk::Editable*>(focused)->paste_clipboard();
  }
}


void MainForm::call_delete()
{
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_delete())
    _wbui_context->get_active_form()->delete_selection();
  else
  {
    Gtk::Widget *focused= get_mainwindow()->get_focus();
    
    if (dynamic_cast<Gtk::Editable*>(focused))
      dynamic_cast<Gtk::Editable*>(focused)->delete_selection();
  }
}

void MainForm::call_select_all()
{
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_select_all())
    _wbui_context->get_active_form()->select_all();
  else
  {
    Gtk::Widget *focused= get_mainwindow()->get_focus();
    
    if (dynamic_cast<Gtk::Editable*>(focused))
      dynamic_cast<Gtk::Editable*>(focused)->select_region(0, -1);
  }
}

void MainForm::call_search()
{
  if (_wbui_context->get_active_context() == WB_CONTEXT_MODEL)
  { 
    wb::ModelDiagramForm* mdf = dynamic_cast<wb::ModelDiagramForm*>(_wbui_context->get_active_form());
    if (mdf)
      mdf->search_and_focus_object(_toolbar_manager->get_search_text());
  }
  else
  {
    wb::OverviewBE* overview_be = _wbui_context->get_physical_overview();
    
    _last_found_node = overview_be->search_child_item_node_matching(bec::NodeId()
                                                                  , _last_found_node
                                                                  ,_toolbar_manager->get_search_text()  
                                                                 );
  
    if (_last_found_node.is_valid())
    {
      _overview->select_node(_last_found_node);
    }
  }
}

bool MainForm::validate_copy()
{
  bool ret = false;
  
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_copy())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    int s, e;
    if (edit && edit->get_selection_bounds(s, e))
      ret = true;
  }

  return ret;
}

bool MainForm::validate_cut()
{
  bool ret = false;
  
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_copy())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    int s, e;
    if (edit && edit->get_selection_bounds(s, e) && edit->get_editable())
      ret = true;
  }
  return ret;
}

bool MainForm::validate_paste()
{
  bool ret = false;
  
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_paste())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    if (edit && edit->get_editable())
      ret = true;
  }
  
  return ret;
}

bool MainForm::validate_delete()
{
  bool ret = false;
  
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_delete())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    int s, e;
    if (edit && edit->get_selection_bounds(s, e) && edit->get_editable())
      ret = true;
  }
  return ret;
}


bool MainForm::validate_select_all()
{
  bool ret = false;
  
  if (_wbui_context->get_active_form() && _wbui_context->get_active_form()->can_select_all())
    ret = true;
  else
  {
    Gtk::Editable *edit= dynamic_cast<Gtk::Editable*>(get_mainwindow()->get_focus());
    if (edit)
      ret = true;
  }
  
  return ret;
}

bool MainForm::validate_search()
{
  return true;
}

void MainForm::register_commands()
{
  std::list<std::string> commands;
  
  //  commands.push_back("overview.mysql_model");
  commands.push_back("show_grt_shell");
  commands.push_back("show_about");
  commands.push_back("diagram_size");
  commands.push_back("show_output_form");
  //  commands.push_back("view_model_navigator");
  //  commands.push_back("view_catalog");
  //  commands.push_back("view_layers");
  //  commands.push_back("view_user_datatypes");
  //  commands.push_back("view_object_properties");
  //  commands.push_back("view_object_description");
  //  commands.push_back("view_undo_history");
  commands.push_back("reset_layout");
  commands.push_back("show_find_dialog");
  commands.push_back("wb.page_setup");
  commands.push_back("closetab");
  //  commands.push_back("help_index");
  //  commands.push_back("help_version_check");
  commands.push_back("wb.sidebarHide");
  
  _wbui_context->get_command_ui()->add_frontend_commands(commands);
  
  _wbui_context->get_command_ui()->add_builtin_command("copy", 
                                               sigc::mem_fun(this, &MainForm::call_copy), 
                                               sigc::mem_fun(this, &MainForm::validate_copy));
  _wbui_context->get_command_ui()->add_builtin_command("cut", 
                                               sigc::mem_fun(this, &MainForm::call_cut), 
                                               sigc::mem_fun(this, &MainForm::validate_cut));
  _wbui_context->get_command_ui()->add_builtin_command("paste",
                                               sigc::mem_fun(this, &MainForm::call_paste), 
                                               sigc::mem_fun(this, &MainForm::validate_paste));
  _wbui_context->get_command_ui()->add_builtin_command("delete",
                                               sigc::mem_fun(this, &MainForm::call_delete), 
                                               sigc::mem_fun(this, &MainForm::validate_delete));
  _wbui_context->get_command_ui()->add_builtin_command("selectAll",
                                               sigc::mem_fun(this, &MainForm::call_select_all), 
                                               sigc::mem_fun(this, &MainForm::validate_select_all));
  _wbui_context->get_command_ui()->add_builtin_command("searchbox",
                                               sigc::mem_fun(this, &MainForm::call_search), 
                                               sigc::mem_fun(this, &MainForm::validate_search));

}

//------------------------------------------------------------------------------
void MainForm::perform_command_becb(const std::string& command)
{
  if (command == "reset_layout")
    reset_layout();
  else if (command == "show_grt_shell")
    toggle_lower_tab_page(_shell_view, true);
  else if (command == "show_about")
    show_about();
  else if (command == "diagram_size")
    show_diagram_options();
  else if (command == "show_output_form")
    show_output();
  else if (command == "show_find_dialog")
    show_find();
  else if (command == "wb.page_setup")
    show_page_setup();
  else if (command == "closetab")
    close_active_tab();
  /*
   // Help
   else if (command == "help_index")
   Help.ShowHelp(null, System.IO.Path.Combine(Application.StartupPath, "MySQLWorkbench.chm"));
   else if (command == "help_version_check")
   Program.CheckForNewVersion();
   */
  else if (command == "wb.sidebarHide")
  {
    Gtk::Box *box;
    _ui->get("main_toolbar", &box);

    std::list<Gtk::Widget*> children= box->get_children();

    for (std::list<Gtk::Widget*>::iterator iter= children.begin(); iter != children.end(); ++iter)
    {
      if ((*iter)->get_name() == "builtin:wb.sidebarHide")
      {
        Gtk::Notebook *note;
        _ui->get("side_note", &note);
        
        if (((Gtk::ToggleButton*)*iter)->get_active())
          note->hide();
        else
          note->show();
        break;
      }
    }
  }
  else
  {
    g_message("Command '%s' not handled!\n", command.c_str());
  }
}


//------------------------------------------------------------------------------
mdc::CanvasView* MainForm::create_view_becb(const std::string& id, const std::string& title)
{  
  ModelDiagramPanel *model_panel= Gtk::manage(new ModelDiagramPanel(_wbui_context, _toolbar_manager));
  
  model_panel->init(id);

  model_panel->set_data("uiform", model_panel->get_form());
  model_panel->set_data("canvas", model_panel->get_canvas());
  model_panel->set_data("model_panel", model_panel);
  add_model_pane(model_panel);

  return model_panel->get_canvas();
}


//------------------------------------------------------------------------------
void MainForm::destroy_view_becb(mdc::CanvasView* view)
{
  Gtk::Notebook *note= get_upper_note();
  for (int c= note->get_n_pages(), i= 1; i < c; i++)
  {    
    ModelDiagramPanel *model_panel = reinterpret_cast<ModelDiagramPanel*>(note->get_nth_page(i)->get_data("model_panel"));
    
    if (model_panel->get_canvas() == view)
    {
      note->remove_page(i);
      break;
    }
  }
}

//------------------------------------------------------------------------------
void MainForm::switched_view_becb(mdc::CanvasView* view)
{
  Gtk::Notebook *note= get_upper_note();
  
  bec::UIForm *view_form= _wbui_context->get_diagram_form(view);
  
  for (int c= note->get_n_pages(), i= 1; i < c; i++)
  {    
    if (note->get_nth_page(i)->get_data("uiform") == view_form)
    {
      note->set_current_page(i);
      break;
    }
  }
}

//------------------------------------------------------------------------------
void MainForm::tool_changed_becb(mdc::CanvasView* view)
{
  ModelDiagramPanel *panel= get_panel_for_view(view);
  if (panel)
  {
    // update all toolbars
    refresh_toolbar("");
    
    // update cursor in canvas
    panel->update_tool_cursor();
  }
}

const char* RefreshTypeStr[] = {
  "RefreshNeeded",
    "RefreshNothing",
    "RefreshLayers",
    "RefreshSchemaList",
    "RefreshSchema",
    "RefreshNewView",
    "RefreshSelection",
    "RefreshToolbar",
    "RefreshMessages",
    "RefreshCloseEditor",
    "RefreshHistory",
    "RefreshUserTypes",
    "RefreshOverviewNodeInfo",
    "RefreshOverviewNodeChildren",
    "RefreshCatalogTreeNode",
    "RefreshViewName",
    "RefreshDocument",
    "RefreshCloseDocument",
    "RefreshZoom",
    "RefreshMenubar",
    "RefreshTimer"
};

//------------------------------------------------------------------------------
static bool call_idle_flush(wb::WBContext *wb)
{
  wb->flush_idle_tasks();
  return false;
}


void MainForm::refresh_gui_becb(wb::RefreshType type, const std::string& arg_id, NativeHandle arg_ptr)
{
  //g_message("%s: %i %s: arg=%s, arg_ptr=%li", __FUNCTION__, type, RefreshTypeStr[type], arg_id.c_str(), arg_ptr);
  // _overview is OverviewPanel
  switch (type)
  {
  case wb::RefreshNeeded:
    {
      Glib::signal_idle().connect(sigc::bind<wb::WBContext*>(sigc::ptr_fun(call_idle_flush), _wbui_context->get_wb()));
      break;
    }

  case wb::RefreshCloseDocument:
    prepare_close_document();
    
    _wbui_context->get_wb()->flush_idle_tasks();

    _wbui_context->get_wb()->close_document_finish();
    break;

  case wb::RefreshNothing:
    break;
    
  case wb::RefreshLayers:
    {
      _model_sidebar.refresh_layers();
      break;
    }
  case wb::RefreshSchemaList:
    {
      _model_sidebar.refresh_catalog();
      break;
    } 
  case wb::RefreshSchema:
    {
      _model_sidebar.refresh_catalog();
      break;
    } 
  case wb::RefreshNewView:
    {
      break;
    }

  case wb::RefreshOverviewNodeInfo:
    _overview->refresh_node(bec::NodeId(arg_id));
    break;

  case wb::RefreshOverviewNodeChildren:
    if (arg_id.empty())
      _overview->show();
    _overview->refresh_children(bec::NodeId(arg_id));
    break;

  case wb::RefreshToolbar:
    refresh_toolbar(arg_id);
    break;

  case wb::RefreshViewName:
    {
      // Find tab with view and rename it
      FormViewBase *form= reinterpret_cast<FormViewBase*>(arg_ptr);
      Gtk::Notebook *note= get_upper_note();
      for (int c= note->get_n_pages(), i= 1; i < c; i++)
      {
        ModelDiagramPanel *model_panel = reinterpret_cast<ModelDiagramPanel*>(note->get_nth_page(i)->get_data("model_panel"));

        if (dynamic_cast<FormViewBase*>(model_panel) == form)
          model_panel->set_title(arg_id);
      }
      break;
    }
  case wb::RefreshMenubar:
    {
      Gtk::Alignment* menu_holder;
      _menu_manager->rebuild(_ui->get("main_menu_box", &menu_holder));
      break;
    }
  case wb::RefreshDocument:
    {
      get_mainwindow()->set_title(_wbui_context->get_title());
      
      _model_sidebar.refresh_usertypes();
      _grt_sidebar.refresh();
      break;
    }
  case wb::RefreshSelection:
    _model_sidebar.refresh_properties();
    _model_sidebar.refresh_documentation(arg_ptr ? reinterpret_cast<FormViewBase*>(arg_ptr)->get_form() : 0);
    break;
    
  case wb::RefreshHistory:
    _model_sidebar.refresh_history();
    break;
    
  case wb::RefreshUserTypes:
    _model_sidebar.refresh_usertypes();
    break;
    
  case wb::RefreshZoom:
    _model_sidebar.refresh_navigator();
    break;

  case wb::RefreshCloseEditor:
    close_editor_for_object(arg_id);
    break;
  case wb::RefreshMessages:
    _output_box->refresh();
    break;
  case wb::RefreshTimer:
    update_timer();
    break;
  case wb::RefreshFindOutput:
    break;
  }
}

//------------------------------------------------------------------------------
void MainForm::lock_gui_becb(bool lock)
{
  _gui_locked= lock;
}

//------------------------------------------------------------------------------
void MainForm::popup_menu_becb(bec::MenuItemList* items, int x, int y)
{
  _menu_manager->run_popup(*items, x, y, GDK_CURRENT_TIME);
}

//------------------------------------------------------------------------------
void MainForm::print_output_becb(const std::string& msg)
{
  _output_box->add_output(msg);
}

//------------------------------------------------------------------------------
void MainForm::shell_output_becb(const std::string& msg)
{
  _shell_view->handle_output(msg);
}


//------------------------------------------------------------------------------
void MainForm::next_shell_command_becb(const std::string& prompt)
{
  _shell_view->handle_prompt(prompt);
}

//------------------------------------------------------------------------------
void MainForm::refresh_toolbar(const std::string &toolbar)
{
  Gtk::Box *box= 0;
  if (toolbar.empty())
  {
    refresh_toolbar("main");
    refresh_toolbar("tools");
    refresh_toolbar("options");
    return;
  }

  if (toolbar == "options")
  {
    bec::UIForm *form= _wbui_context->get_active_main_form();
    if (form)
    {
      ModelDiagramPanel *model= dynamic_cast<ModelDiagramPanel*>(reinterpret_cast<FormViewBase*>(form->get_frontend_data()));
      if (model)
        model->update_toolbar(toolbar);
    }
  }
  else
    _toolbar_manager->rebuild_toolbar(_ui->get(toolbar+"_toolbar", &box), toolbar);
}

//------------------------------------------------------------------------------
void MainForm::switch_page(GtkNotebookPage*, guint pagenum)
{  
  Gtk::Notebook *note= get_upper_note();
  
  Gtk::Widget *page = note->get_nth_page(pagenum);
  page->show();
  note->set_current_page(pagenum);
  
  bec::UIForm* uiform = static_cast<bec::UIForm*>(page->get_data("uiform"));
  _wbui_context->set_active_form(uiform);

  _model_sidebar.refresh_navigator();
  
  // force an immediate refresh so that UI updates quicker
  refresh_toolbar("");

  Gtk::Alignment* menu_holder;
  _menu_manager->rebuild(_ui->get("main_menu_box", &menu_holder));
}


//--------------------------------------------------------------------------------
void MainForm::toggle_lower_tab_page(Gtk::Widget *page, bool show)
{
  Gtk::Notebook *note= 0;
  _ui->get("side_note", &note);
  
  note->set_show_tabs(show);
  if (!show)
    note->set_current_page(0);
  
  if (show)
  {
    show_lower_tab(page);
    note->set_current_page(note->page_num(*page));
  }
  else
  {
    Gtk::Notebook *note= get_lower_note();
    
    close_tab(note, page, sigc::slot<bool>());
  }
}


//==============================================================================
ModelDiagramPanel *MainForm::get_panel_for_view(mdc::CanvasView *view)
{
  if (_wbui_context->get_diagram_form(view))
    return dynamic_cast<ModelDiagramPanel*>(static_cast<FormViewBase*>(_wbui_context->get_diagram_form(view)->get_frontend_data()));
  
  return 0;
}


static bool close_plugin(wb::WBContext *wb, PluginEditorBase *editor)
{
  wb->close_gui_plugin(dynamic_cast<GUIPluginBase*>(editor));
  
  return true;
}

void MainForm::add_plugin_pane(PluginEditorBase *pane)
{
  Gtk::Notebook *note= get_lower_note();
  Gtk::Label *label= 0;
  
  append_tab_page(note, pane, pane->get_title(), true, sigc::bind(sigc::ptr_fun(close_plugin), _wbui_context->get_wb(), pane),
                  &label);

  pane->signal_title_changed().connect(sigc::mem_fun(label, &Gtk::Label::set_text));
}


void MainForm::show_lower_tab(Gtk::Widget *page)
{
  Gtk::Notebook *note= get_lower_note();
  
  page->show();
  
  if (!note->is_visible())
    note->show();
}


void MainForm::close_active_tab()
{
  Gtk::Widget *focused= get_mainwindow()->get_focus();
  Gtk::Notebook *upper= get_upper_note();
  Gtk::Notebook *lower= get_lower_note(); 

  // go up the hierarchy to see if one of the upper or lower notebooks are focused
  while (focused && focused != upper && focused != lower)
    focused= focused->get_parent();

  if (focused)
  {
    Gtk::Notebook *note= dynamic_cast<Gtk::Notebook*>(focused);

    int page= note->get_current_page();

    if (note == upper && page == 0) // dont close Overview page
      return; 

    if (page >= 0)
    {
      Gtk::Widget *tab= note->get_nth_page(page);

      close_tab(note, tab, sigc::slot<bool>());
    }
  }
}


void MainForm::close_tab(Gtk::Notebook *note, Gtk::Widget *page, const sigc::slot<bool> &on_close)
{
  page->hide();

  // on_close should return true if the form should be closed/removed
  if (on_close && on_close())
    note->remove_page(*page);
  
  bool visible= false;
  for (int c= note->get_n_pages(), i= 0; i < c; i++)
  {
    if (note->get_nth_page(i)->is_visible())
    {
      visible= true;
      break;
    }
  }
  
  if (!visible)
    note->hide();
}


void MainForm::append_tab_page(Gtk::Notebook *note, Gtk::Widget *widget, const std::string &title,
                               bool auto_switch,
                               const sigc::slot<bool> &on_close,
                               Gtk::Label **title_label_ret)
{
  Gtk::HBox *hbox= Gtk::manage(new Gtk::HBox(false, 1));
  Gtk::Label *text_label= Gtk::manage(new Gtk::Label(title));
  
  // temp hack to remove close button for overview, since it cant be brought back now
  if (widget != _overview->get_panel())
  {
    Gtk::Label *label= Gtk::manage(new Gtk::Label("\342\234\225"));
    Gtk::EventBox *evbox= Gtk::manage(new Gtk::EventBox());

    evbox->add(*label);
    evbox->signal_button_release_event().
      connect(sigc::bind_return(sigc::bind(sigc::hide<0>(sigc::mem_fun(this, &MainForm::close_tab)), note, widget, on_close), false));

    hbox->pack_end(*evbox);
  }
  hbox->pack_end(*text_label);

  hbox->show_all();

  note->append_page(*widget, *hbox);
  
  if (title_label_ret)
    *title_label_ret= text_label;

  if (auto_switch)
  {
    // switch the current page when we're in idle
    Glib::signal_idle().connect(sigc::bind_return(sigc::bind(sigc::mem_fun(note, &Gtk::Notebook::set_current_page),
                                                             note->get_n_pages()-1), false));
  }
}


void MainForm::add_model_pane(ModelDiagramPanel *panel)
{
  bool is_closed= panel->get_diagram_form()->is_closed();

  add_form_pane(panel, !is_closed);

  if (is_closed)
    panel->hide();
  
  Gtk::Label *label= (Gtk::Label*)*((Gtk::HBox*)get_upper_note()->get_tab_label(*panel))->get_children().begin();
  if (label)
    panel->signal_title_changed().connect(sigc::mem_fun(label, &Gtk::Label::set_text));
}


void MainForm::add_form_pane(FormViewBase *panel, bool opened)
{
  panel->get_panel()->set_data("FormViewBase", panel);
  panel->get_panel()->set_data("uiform", panel->get_form());

  append_tab_page(get_upper_note(), panel->get_panel(), panel->get_title(), opened);
//  Gtk::Label *label= (Gtk::Label*)*((Gtk::HBox*)get_upper_note()->get_tab_label(*panel))->get_children().begin();
//  if (label)
//    panel->signal_title_changed().connect(sigc::mem_fun(label, &Gtk::Label::set_text));
}


void MainForm::prepare_close_document()
{
  // close all diagram tabs so they stop receiving events (which can lead to a crash)
  Gtk::Notebook *note= get_upper_note();

  for (int i= note->get_n_pages()-1; i >= 0; --i)
  {
    Gtk::Widget *panel= note->get_nth_page(i);
    if (dynamic_cast<ModelDiagramPanel*>(panel))
      panel->hide();
  }

  // reset overview
  _overview->reset();
}


static bool return_true() { return true; }

void MainForm::close_editor_for_object(const std::string &id)
{
  Gtk::Notebook *note= get_lower_note();

  for (int i= note->get_n_pages()-1; i >= 0; --i)
  {
    Gtk::Widget *panel= note->get_nth_page(i);
    PluginEditorBase* editor;
    if ((editor= dynamic_cast<PluginEditorBase*>(panel))
        && (id.empty() || editor->should_close_on_delete_of(id)))
    {
      // removing the page will automatically unreference and destroy it
      close_tab(note, editor, sigc::ptr_fun(return_true));
    }
  }
}


bool MainForm::fire_timer()
{
  _wbui_context->get_wb()->get_grt_manager()->flush_timers();

  update_timer();
  return false;
}


void MainForm::update_timer()
{
  int delay_ms= (int)(1000 * _wbui_context->get_wb()->get_grt_manager()->delay_for_next_timeout());
  
  if (delay_ms >= 0)
    Glib::signal_timeout().connect(sigc::mem_fun(this, &MainForm::fire_timer), delay_ms);
}



//==============================================================================
// Here go command handlers
// 
//------------------------------------------------------------------------------
void MainForm::reset_layout()
{
  Gtk::Paned *paned;
  
  _ui->get("model_eer_panes", &paned);
  paned->set_position(800);

  _ui->get("top_panes", &paned);
  paned->set_position(paned->get_width()-200);

  _model_sidebar.reset_layout();
  _grt_sidebar.reset_layout();
}



void MainForm::show_about()
{
  Gtk::Window dialog(Gtk::WINDOW_TOPLEVEL);
  
  dialog.set_default_size(560, 322);
  dialog.modify_bg_pixmap(Gtk::STATE_NORMAL, bec::IconManager::get_instance()->get_icon_path(SPLASH_IMAGE));
  dialog.set_position(Gtk::WIN_POS_CENTER);
  dialog.set_decorated(false);
  
  Gtk::Fixed fixed;
  dialog.add(fixed);
  
  Gtk::Label label("Version " PACKAGE_VERSION);
  label.set_alignment(1.0, 0.0);
  label.set_justify(Gtk::JUSTIFY_RIGHT);
  fixed.put(label, 0, 150);
  label.set_size_request(525, 50);
  label.modify_fg(Gtk::STATE_NORMAL, label.get_style()->get_white());
  
  dialog.set_modal(true);
  dialog.show_all();
  
  dialog.add_events(Gdk::BUTTON_PRESS_MASK);
  dialog.signal_button_press_event().connect_notify(sigc::hide(sigc::ptr_fun(&Gtk::Main::quit)));
  
  Gtk::Main::instance()->run();
  
  dialog.hide();
}


void MainForm::show_output()
{
  toggle_lower_tab_page(_output_box, true);
}


void MainForm::show_diagram_options()
{
  DiagramSizeForm *form= DiagramSizeForm::create(_wbui_context);

  form->run();
  
  form->hide();
  delete form;
}


void MainForm::show_find()
{
  if (!_find_panel)
  {
    _find_panel= FindPanel::create(_wbui_context);

    Gtk::VBox *vbox;
    _ui->get("inner_vbox", &vbox);

    vbox->pack_start(*_find_panel, false, false);
    vbox->reorder_child(*_find_panel, 0);
    _find_panel->unreference();

  }

  _find_panel->show();
}



void MainForm::show_page_setup()
{
  _wbui_context->get_wb()->execute_plugin("wb.print.setup");
}
