/* 
 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */
#include "stdafx.h"

#include "wb_sql_editor_form.h"
#include "wb_sql_editor_form_ui.h"
#include "query_side_palette.h"
#include "wb_live_schema_tree.h"
#include "grtui/inserts_export_form.h"

#include "workbench/wb_context_ui.h"
#include "base/boost_smart_ptr_helpers.h"


#include "mforms/app.h"
#include "mforms/splitter.h"
#include "mforms/tabview.h"
#include "mforms/hypertext.h"
#include "mforms/panel.h"
#include "mforms/menubar.h"
#include "mforms/toolbar.h"
#include "mforms/grttreeview.h"

using namespace bec;
using namespace grt;
using namespace wb;
using namespace base;


void SqlEditorForm::activate_command(const std::string &command)
{
  wbsql()->get_wbui()->get_command_ui()->activate_command(command);
}


mforms::ToolBar *SqlEditorForm::get_toolbar()
{
  if (!_toolbar)
  {
    _toolbar = wbsql()->get_wbui()->get_command_ui()->create_toolbar("data/dbquery_toolbar.xml", boost::bind(&SqlEditorForm::activate_command, this, _1));
    
    update_menu_and_toolbar();
  }
  return _toolbar;
}

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

mforms::MenuBar *SqlEditorForm::get_menubar()
{
  if (!_menu)
  {
    _menu = _context_ui->get_command_ui()->create_menubar_for_context(WB_CONTEXT_QUERY);
    
    update_menu_and_toolbar();
    
    _menu->set_item_enabled("query.save_edits", false);
    _menu->set_item_enabled("query.discard_edits", false);
    _menu->set_item_enabled("query.export", false);
    
    _menu->set_item_checked("query.stopOnError", !continue_on_error());
  }
  return _menu;
}

void SqlEditorForm::update_menu_and_toolbar()
{
  if (!_grtm->in_main_thread())
  {
    exec_sql_task->execute_in_main_thread(boost::bind(&SqlEditorForm::update_menu_and_toolbar, this),
                                          false,
                                          false);
    return;
  }
  
  bool running = is_running_query();
  
  if (_menu)
  {
    _menu->set_item_enabled("query.cancel", running);
    _menu->set_item_enabled("query.execute", !running);
    _menu->set_item_enabled("query.reconnect", !running);
    _menu->set_item_enabled("wb.sqlide.executeToTextOutput", !running);
    _menu->set_item_enabled("query.execute_current_statement", !running);
    _menu->set_item_enabled("query.explain", !running);
    _menu->set_item_enabled("query.explain_current_statement", !running);
    
    _menu->set_item_enabled("query.commit", !running && !auto_commit());
    _menu->set_item_enabled("query.rollback", !running && !auto_commit());    
    mforms::MenuItem *item = _menu->find_item("query.autocommit");
    if (item)
    {
      item->set_enabled(!running);
      item->set_checked(auto_commit());    
    }
  }
  
  if (_toolbar)
  {
    /* deprecated
     _toolbar->set_item_enabled("query.cancel", running);
     _toolbar->set_item_enabled("query.execute", !running);
     _toolbar->set_item_enabled("query.reconnect", !running);
     _toolbar->set_item_enabled("wb.sqlide.executeToTextOutput", !running);
     _toolbar->set_item_enabled("query.execute_current_statement", !running);
     _toolbar->set_item_enabled("query.explain", !running);
     _toolbar->set_item_enabled("query.explain_current_statement", !running);
     
     _toolbar->set_item_enabled("query.commit", !running && !auto_commit());
     _toolbar->set_item_enabled("query.rollback", !running && !auto_commit());
     mforms::ToolBarItem *item = _toolbar->find_item("query.autocommit");
     if (item)
     {
     item->set_enabled(!running);
     item->set_checked(auto_commit());
     }
     }*/
  }
  
  /*
   if (running)
   {
   set_editor_tool_items_enbled("query.cancel", false);
   for (Sql_editors::iterator iter = _sql_editors.begin(); iter != _sql_editors.end(); ++iter)
   if (iter->busy)
   iter->toolbar->set_item_checked("query.cancel", true);
   }
   else*/
  set_editor_tool_items_enbled("query.cancel", running);
  
  set_editor_tool_items_enbled("query.execute", !running);
  set_editor_tool_items_enbled("query.reconnect", !running);
  set_editor_tool_items_enbled("wb.sqlide.executeToTextOutput", !running);
  set_editor_tool_items_enbled("query.execute_current_statement", !running);
  set_editor_tool_items_enbled("query.explain", !running);
  set_editor_tool_items_enbled("query.explain_current_statement", !running);
  
  set_editor_tool_items_enbled("query.commit", !running && !auto_commit());
  set_editor_tool_items_enbled("query.rollback", !running && !auto_commit());    
  set_editor_tool_items_enbled("query.autocommit", !running);
  set_editor_tool_items_checked("query.autocommit", auto_commit());
  set_editor_tool_items_checked("query.stopOnError", !_continue_on_error);
}


void SqlEditorForm::validate_menubar()
{
  if (get_menubar())
    get_menubar()->validate();
}

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

/**
 * Setup of schema browser/object info/quick help sidebars (puzzling: sometimes called side palette).
 */
void SqlEditorForm::setup_sidebars()
{
  // Left hand side bar (schema browser, object info).
  _side_bar = mforms::TaskSidebar::create();
#if !defined(_WIN32) && !defined(__APPLE__)
  _side_bar->get_schema_tree()->set_name("LiveSchemaTree");
#endif
  _side_bar->set_schema_model(&_base_schema_tree);
  _side_bar->set_filtered_schema_model(&_filtered_schema_tree);
  //_side_bar->set_selection_color(mforms::App::get()->get_system_color(mforms::SystemHighlight).to_html());
  _side_bar->set_selection_color(mforms::SystemHighlight);
  
  scoped_connect(_side_bar->signal_filter_changed(),boost::bind(&SqlEditorForm::side_bar_filter_changed, this, _1));
  scoped_connect(_side_bar->get_schema_tree()->signal_changed(),boost::bind(&SqlEditorForm::schema_row_selected, this));
  
  //  _side_bar->set_collapse_states(_options.get_string("DbSqlEditor:SidebarCollapseState"));
  
  _side_splitter = mforms::manage(new mforms::Splitter(false, true));
#ifdef _WIN32
  mforms::Panel* panel;
  _side_splitter->set_back_color("#283752");
  panel = mforms::manage(new mforms::Panel(mforms::StyledHeaderPanel));
  panel->set_title("Object Browser");
  panel->add(_side_bar);
  _side_splitter->add(panel, 200);
#else
  _side_splitter->add(_side_bar, 200);
#endif
  
  _info_tabview = new mforms::TabView(mforms::TabViewSelectorSecondary);
  
#ifdef _WIN32
  panel = mforms::manage(new mforms::Panel(mforms::StyledHeaderPanel));
  panel->set_title(_("Information"));
  panel->add(_info_tabview);
  _side_splitter->add(panel, 30);
#else
  _side_splitter->add(_info_tabview, 30);
#endif
  
  _object_info = new mforms::HyperText();
  _session_info = new mforms::HyperText();
#ifdef _WIN32
  _object_info->set_back_color("#ffffff");
  _session_info->set_back_color("#ffffff");
#else
  _object_info->set_back_color("#ebebeb");
  _session_info->set_back_color("#ebebeb");
#endif
  
  _info_tabview->add_page(_object_info, _("Object Info"));
  _info_tabview->add_page(_session_info, _("Session"));
  
  _session_info->set_markup_text(_connection_info);
  
  _side_splitter->set_position(500);
  
  // Right hand side bar (quick help, variables).
  _side_palette = mforms::manage(new QuerySidePalette(shared_from_this()));
  
#ifdef _WIN32
  panel = mforms::manage(new mforms::Panel(mforms::StyledHeaderPanel));
  panel->set_title(_("SQL Additions"));
  panel->add(_side_palette);
  _side_palette_host = panel; // Once we have the variables view this becomes a splitter too.

#else
  _side_palette_host = _side_palette;
#endif
}

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

void SqlEditorForm::schema_row_selected()
{
  std::vector<bec::NodeId> nodes;
  std::string details;
  
  if (_side_bar)
  {
    _side_bar->get_schema_tree()->get_selection(nodes);
    if (nodes.empty())
      details= "<b><font color=\"#aaa\">No object selected</font></b>";
    else if (nodes.size() > 1)
      details= "<b><font color=\"#aaa\">Multiple objects selected</font></b>";
    else
      details= get_schema_tree()->get_field_description(nodes[0], 0);
    _object_info->set_markup_text(details);
   
  }
}

void SqlEditorForm::side_bar_filter_changed(const std::string& filter)
{
  if (filter.length() > 0)
    _schema_tree = &_filtered_schema_tree;
  else
    _schema_tree = &_base_schema_tree;

  refresh_schema_tree(false);
}
//--------------------------------------------------------------------------------------------------

void SqlEditorForm::toolbar_command(const std::string& command)
{
  if (command == "query.new_schema")
    tree_create_object(LiveSchemaTree::Schema, "", "");
  else if (command == "query.new_table")
    tree_create_object(LiveSchemaTree::Table, "", "");
  else if (command == "query.new_view")
    tree_create_object(LiveSchemaTree::View, "", "");
  else if (command == "query.new_routine")
    tree_create_object(LiveSchemaTree::Routine, "", "");
}


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


mforms::View* SqlEditorForm::get_sidebar()
{
  return _side_splitter;
}

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

mforms::View *SqlEditorForm::get_side_palette()
{
  return _side_palette_host;
}

//--------------------------------------------------------------------------------------------------
// Editor Toolbar

static void toggle_continue_on_error(SqlEditorForm *sql_editor_form)
{
  sql_editor_form->continue_on_error(!sql_editor_form->continue_on_error());
}

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

static void toggle_show_special_chars(mforms::ToolBarItem *item, SqlEditorForm *sql_editor_form)
{
  if (item->get_checked())
    sql_editor_form->do_partial_ui_refresh(SqlEditorForm::ShowSpecialCharacters);
  else
    sql_editor_form->do_partial_ui_refresh(SqlEditorForm::HideSpecialCharacters);
}

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

boost::shared_ptr<mforms::ToolBar> SqlEditorForm::setup_editor_toolbar()
{
  boost::shared_ptr<mforms::ToolBar> tbar(new mforms::ToolBar(mforms::SecondaryToolBar));
  mforms::ToolBarItem *item;
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.openFile");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_open.png"));
  item->set_tooltip(_("Open a script file in this editor"));
  scoped_connect(item->signal_activated(),boost::bind((void (SqlEditorForm::*)(const std::string&,bool))&SqlEditorForm::open_file, this, "", false));
  tbar->add_item(item);
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.saveFile");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_save.png"));
  item->set_tooltip(_("Save the script to a file."));
  scoped_connect(item->signal_activated(),boost::bind(&SqlEditorForm::save_file, this));
  tbar->add_item(item);
  
  tbar->add_item(mforms::manage(new mforms::ToolBarItem(mforms::SeparatorItem)));
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.execute");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_execute.png"));
  item->set_tooltip(_("Execute the selected portion of the script or everything, if there is no selection"));
  scoped_connect(item->signal_activated(),boost::bind(&bec::RefreshUI::do_partial_ui_refresh, this, (int)RunCurrentScript));
  tbar->add_item(item);
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.execute_current_statement");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_execute-current.png"));
  item->set_tooltip(_("Execute the statement under the keyboard cursor"));
  scoped_connect(item->signal_activated(),boost::bind(&bec::RefreshUI::do_partial_ui_refresh, this, (int)RunCurrentStatement));
  tbar->add_item(item);  
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.explain_current_statement");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_explain.png"));
  item->set_tooltip(_("Execute the EXPLAIN command on the statement under the cursor"));
  scoped_connect(item->signal_activated(),boost::bind(&SqlEditorForm::explain_current_statement, this));
  tbar->add_item(item);  
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.cancel");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_stop.png"));
  item->set_tooltip(_("Stop the query being executed (the connection to the DB server will not be restarted and any open transactions will remain open)"));
  scoped_connect(item->signal_activated(),boost::bind(&SqlEditorForm::cancel_query, this));
  tbar->add_item(item);  
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ToggleItem));
  item->set_name("query.stopOnError");
  item->set_alt_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_stop-on-error-on.png"));
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_stop-on-error-off.png"));
  item->set_tooltip(_("Toggle whether execution of SQL script should continue after failed statements"));
  scoped_connect(item->signal_activated(),boost::bind(toggle_continue_on_error, this));
  
  tbar->add_item(item);  
  
  tbar->add_item(mforms::manage(new mforms::ToolBarItem(mforms::SeparatorItem)));
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.commit");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_commit.png"));
  item->set_tooltip(_("Commit the current transaction.\nNOTE: all query tabs in the same connection share the same transaction. To have independent transactions, you must open a new connection."));
  scoped_connect(item->signal_activated(),boost::bind(&SqlEditorForm::commit, this));
  tbar->add_item(item);  
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.rollback");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_rollback.png"));
  item->set_tooltip(_("Rollback the current transaction.\nNOTE: all query tabs in the same connection share the same transaction. To have independent transactions, you must open a new connection."));
  scoped_connect(item->signal_activated(),boost::bind(&SqlEditorForm::rollback, this));
  tbar->add_item(item);  
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ToggleItem));
  item->set_name("query.autocommit");
  item->set_alt_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_autocommit-on.png"));
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_autocommit-off.png"));
  item->set_tooltip(_("Toggle autocommit mode. When enabled, each statement will be commited immediately.\nNOTE: all query tabs in the same connection share the same transaction. To have independent transactions, you must open a new connection."));
  scoped_connect(item->signal_activated(),boost::bind(&SqlEditorForm::toggle_autocommit, this));
  tbar->add_item(item);  
  
  tbar->add_item(mforms::manage(new mforms::ToolBarItem(mforms::SeparatorItem)));
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.beautify");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_beautifier.png"));
  item->set_tooltip(_("Beautify/reformat the SQL script"));
  _wbsql->get_wbui()->get_command_ui()->scoped_connect(item->signal_activated(),boost::bind((void (wb::CommandUI::*)(const std::string&))&wb::CommandUI::activate_command, 
                                                                                            _wbsql->get_wbui()->get_command_ui(), "plugin:wb.sqlide.enbeautificate"));
  tbar->add_item(item);  
  
  /*
   item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
   item->set_name("query.beautify_current_statement");
   item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_beautifier_current.png"));
   item->set_tooltip(_("Beautify/reformat the current SQL statement"));
   item->signal_activated().connect(sigc::bind(sigc::mem_fun(this, &bec::RefreshUI::do_partial_ui_refresh), (int)ShowFindPanel));
   tbar->add_item(item);  
   */
  
  tbar->add_item(mforms::manage(new mforms::ToolBarItem(mforms::SeparatorItem)));
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ActionItem));
  item->set_name("query.search");
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_find.png"));
  item->set_tooltip(_("Show the Find panel for the editor"));
  scoped_connect(item->signal_activated(),boost::bind(&bec::RefreshUI::do_partial_ui_refresh, this, (int)ShowFindPanel));
  tbar->add_item(item);  
  
  item = mforms::manage(new mforms::ToolBarItem(mforms::ToggleItem));
  item->set_name("query.toggleInvisible");
  item->set_alt_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_special-chars-on.png"));
  item->set_icon(IconManager::get_instance()->get_icon_path("qe_sql-editor-tb-icon_special-chars-off.png"));
  item->set_tooltip(_("Toggle display of invisible characters (spaces, tabs, newlines)"));
  scoped_connect(item->signal_activated(),boost::bind(toggle_show_special_chars, item, this));
  tbar->add_item(item);
  
  return tbar;
}


void SqlEditorForm::set_editor_tool_items_enbled(const std::string &name, bool flag)
{
  for (Sql_editors::iterator editor = _sql_editors.begin(); editor != _sql_editors.end(); ++editor)
    (*editor)->toolbar->set_item_enabled(name, flag);
}


void SqlEditorForm::set_editor_tool_items_checked(const std::string &name, bool flag)
{
  for (Sql_editors::iterator editor = _sql_editors.begin(); editor != _sql_editors.end(); ++editor)
    (*editor)->toolbar->set_item_checked(name, flag);
}

void SqlEditorForm::set_tool_item_checked(const std::string& name, bool flag)
{
  _toolbar->set_item_checked(name, flag);
}



void SqlEditorForm::show_export_recordset(int editor_index, Recordset::Ptr rs_ptr)
{
  try
  {
    RETURN_IF_FAIL_TO_RETAIN_WEAK_PTR (Recordset, rs_ptr, rs)
    {
      grt::ValueRef option(_grtm->get_app_option("Recordset:LastExportPath"));
      std::string path = option.is_valid() ? grt::StringRef::cast_from(option) : "";
      option = _grtm->get_app_option("Recordset:LastExportExtension");
      std::string extension = option.is_valid() ? grt::StringRef::cast_from(option) : "";
      InsertsExportForm exporter(0/*mforms::Form::main_form()*/, rs_ref, extension);
      exporter.set_title(_("Export Resultset"));
      if (!path.empty())
        exporter.set_path(path);
      path = exporter.run();
      if (path.empty())
        _grtm->replace_status_text(_("Export resultset canceled"));
      else
      {
        _grtm->replace_status_text(strfmt(_("Exported resultset to %s"), path.c_str()));
        _grtm->set_app_option("Recordset:LastExportPath", grt::StringRef(path));
        extension = base::extension(path);
        if (!extension.empty() && extension[0] == '.')
          extension = extension.substr(1);
        if (!extension.empty())
          _grtm->set_app_option("Recordset:LastExportExtension", grt::StringRef(extension));    
      }
    }
  }
  catch (const std::exception &exc)
  {
    mforms::Utilities::show_error("Error exporting recordset", exc.what(), "OK");
  }
}
