/* 
 * Copyright (c) 2009, 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 "server_instance_editor.h"
#include "grtui/grtdb_connection_editor.h"

#include "grtpp_util.h"
#include <grt/common.h>
#include "base/string_utilities.h"

#include "mforms/uistyle.h"
#include "mforms/panel.h"
#include "mforms/filechooser.h"

#define SYSTEM_STAT_HEADER "# Customize this script to suit your system if necessary\n"\
"# Shell function names and output format must be maintained\n\n"

static struct SystemStatScript {
  const char *name;
  const char *script;
} system_stat_scripts[] = {
{"Windows", // Obsolete since introduction of wmi API based management.
"get_cpu_info = wmic cpu get LoadPercentage |  wba_line(1) | wba_arithm(/, 100.0)"
"\n"
"get_mem_info = wmic os get FreePhysicalMemory |  wba_line(1)"
"\n"
"get_mem_total = wmic os get TotalVisibleMemorySize |  wba_line(1)"
"\n"
},
{"Linux", 
"get_cpu_info = /usr/bin/uptime | wba_token(' ', -3)"
"\n"
"get_mem_info = free | wba_filter(-/+ buffers/cache:) | wba_token(' ', 3)"
"\n"
"get_mem_total = free | wba_filter(Mem:) | wba_token(' ', 1)"
"\n"
},
{"MacOS X",
"get_cpu_info = /usr/bin/uptime | wba_token(' ', -3)"
"\n"
"get_mem_info = vm_stat | wba_filter(free) | wba_token(':', 1)"
"\n"
"get_mem_total = sysctl hw.memsize | wba_token(' ', 1) | wba_arithm(/, 4096)"
"\n"
},
{"OpenSolaris",
"get_cpu_info = /usr/bin/uptime | wba_token(' ', -3)"
"\n"
"get_mem_info = free | wba_filter(Mem:) | wba_token(' ', 1, 2)"
"\n"
"get_mem_total = "
"\n"
},
{"FreeBSD",
"get_cpu_info = /usr/bin/uptime | wba_token(' ', -3)"
"\n"
"get_mem_info = free | wba_filter(Mem:) | wba_token(' ', 1, 2)"
"\n"
"get_mem_total = sysctl hw.memsize | wba_token(' ', 1) | wba_arithm(/, 4096)"
"\n"
},
{NULL, NULL}
};


std::string get_admin_script_for_os(const std::string &os)
{
  for (int i= 0; system_stat_scripts[i].name; i++)
    if (strcmp(system_stat_scripts[i].name, os.c_str()) == 0)
      return system_stat_scripts[i].script;
  return "";
}


#define WINDOWS_CONFIG_HINT_TEXT "\nWindows Hint:\n\n"\
"When Server Instance is configured for\n"\
"\"Windows Remote Management\", the \"Configuration File\"\n"\
"must be specified using a mapped drive,\n"\
"network share or administrative share.\n\n"\
"Examples of these are\n"\
"M:\\<path to file>\\my.ini\n"\
"\\\\<server>\\<share>\\<path to file>\\my.ini\n"\
"\\\\<server>\\C$\\Program Fiels\\MySQL\\MySQL Server 5.5\\my.ini"



static const char *filter_help = 
"Workbench expects status scripts to return 0 on success (running) and 1 otherwise.\n"
"In case of OS where execution status is not always correctly detected\n"
"(as it is in Windows) wba_ internal filters can be used.\n"
"The wba_filter() filter generates a status code internally,\n"
"0 if the pattern is found in the output from the command and 1 if not.\n"
"Filters chain can be debugged using Plugins/Utilities/Test Filters from main menu.\n"
"You may use the following filters provided by Workbench to manipulate\n"
"text output by system commands.\n"
"Filters can be combined (piped) using the | symbol\n"
"\n"
"wba_line(<line number>)\n"
"  Skips all lines in the output except the given one\n"
"wba_token(<separator>, <token number>)\n"
"  Splits line into pieces using separator and returns given numbered token.\n"
"  token number can have negative values to refer to a token counting in reverse order\n"
"wba_filter(<pattern>)\n"
"  Removes all lines which do not contain <pattern>\n"
"wba_arithm(<op>, <arg>)\n"
"  Applies arithmetic operations to the input value";


using namespace mforms;
using namespace base;

inline Label *RLabel(const std::string &text)
{
  Label *l= new Label(text);
  l->set_text_align(MiddleRight);
  return l;
}

inline Table *NewTable(int rows, int cols)
{
  Table *table = new Table();
  table->set_row_count(rows);
  table->set_column_count(cols);
  table->set_row_spacing(8);
  table->set_column_spacing(8);
  table->set_padding(8);
  return table;
}

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

/**
 * Determines if the given connection is an SSH connection and returns true if so.
 */
static bool is_ssh_connection(const db_mgmt_ConnectionRef &connection)
{
  if (connection.is_valid())
  {
    std::string driver= connection->driver().is_valid() ? connection->driver()->name() : "";
    return (driver == "MysqlNativeSSH");
  }
  return false;
}

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

/**
 * Determines if the given connection is a local connection (i.e. to the current box).
 */
static bool is_local_connection(const db_mgmt_ConnectionRef &connection)
{
  if (connection.is_valid())
  {
    std::string hostname= connection->parameterValues().get_string("hostName");

    if (!is_ssh_connection(connection) && (hostname == "localhost" || hostname.empty() || hostname == "127.0.0.1"))
      return true;
  }
  return false;
}

//----------------- ServerInstanceEditor -----------------------------------------------------------

ServerInstanceEditor::ServerInstanceEditor(bec::GRTManager *grtm, const db_mgmt_ManagementRef &mgmt)
: Form(0, FormResizable)
, _grtm(grtm)
, _top_vbox(false)
, _top_hbox(true)
, _content_box(false)
, _inst_list_buttons_hbox(true)
, _stored_instance_list(TreeDefault|TreeFlatList)
, _tabview(mforms::TabViewSystemStandard)
, _conn_box(false)
, _no_remote_admin(RadioButton::new_id())
, _win_remote_admin(_no_remote_admin.group_id())
, _ssh_remote_admin(_no_remote_admin.group_id())
, _remote_param_box(false)
, _password_box(true)
, _sys_box(false)
, _details_tabview(mforms::TabViewSystemStandard)
, _sudo_cmd(mforms::SelectorCombobox)
, _script_text(VerticalScrollBar)
, _bottom_hbox(true)
{
  _mgmt= mgmt;
  _instances= mgmt->storedInstances();
  
  set_title(_("Manage Server Instances"));
  
  _top_vbox.set_padding(MF_WINDOW_PADDING);
  _top_vbox.set_spacing(12);
  _top_hbox.set_spacing(8);
  _top_vbox.add(&_top_hbox, true, true);
  _top_vbox.add(&_bottom_hbox, false, true);
  
  _bottom_hbox.set_spacing(12);
      
  _top_hbox.add(&_stored_instance_list, false, true);
  
  {
    Box *hbox = manage(new Box(true));
    hbox->add(manage(RLabel(_("Instance Profile Name:"))), false, true);
    hbox->add(&_name_entry, true, true);
    hbox->set_padding(12);
    hbox->set_spacing(MF_TABLE_COLUMN_SPACING);
    scoped_connect(_name_entry.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this, 
                                                    &_name_entry));
    
    _content_box.add(hbox, false, true);
    _content_box.add(&_tabview, true, true);
    _top_hbox.add(&_content_box, true, true);
  }
  
  scoped_connect(_stored_instance_list.signal_changed(),boost::bind(&ServerInstanceEditor::instance_changed, this));  
  
  _conn_box.set_padding(MF_PANEL_PADDING);
  _remote_param_box.set_padding(MF_PANEL_PADDING);
  _sys_box.set_padding(MF_PANEL_PADDING);
  _sys_box.set_spacing(10);
  
  Box *vbox = manage(new Box(false));
  vbox->set_spacing(15);
  vbox->set_padding(MF_PANEL_PADDING);
  
  _tabview.add_page(vbox, _("Connection"));
  
  // Connection
  {
    Panel *panel = manage(new Panel(TitledGroupPanel));
    panel->set_title(_("MySQL Connection"));
    
    Label *label= manage(new Label(_("Pick a preset connection to the MySQL server instance. "
                                     "The connection will be used for\nbasic administration tasks "
                                     "such as managing users and viewing schema objects.")));
    label->set_style(SmallStyle);
    label->set_wrap_text(true);

    _conn_box.set_padding(MF_PANEL_PADDING);
    _conn_box.set_spacing(MF_TABLE_ROW_SPACING);
    _conn_box.add(label, false, false);
    
    Box *box = manage(new Box(true));
    box->set_spacing(MF_TABLE_COLUMN_SPACING);
    box->add(manage(new Label(_("Connection:"))), false, false);
    box->add(&_connection_selector, true, true);
    _conn_box.add(box, false, false);
    
    Box *bbox = manage(new Box(true));
    bbox->set_spacing(MF_TABLE_COLUMN_SPACING);
    _conn_box.add(bbox, false, true);
    Button *b = manage(new Button());
    b->set_text("Manage Connections...");
    scoped_connect(b->signal_clicked(),boost::bind(&ServerInstanceEditor::open_editor, this, false));
    bbox->add_end(b, false, true);
    b = manage(new Button());
    b->set_text("Edit Selected...");
    scoped_connect(b->signal_clicked(),boost::bind(&ServerInstanceEditor::open_editor, this, true));
    bbox->add_end(b, false, true);
    
    Box *verbox = manage(new Box(true));
    verbox->set_spacing(MF_TABLE_COLUMN_SPACING);
    _conn_box.add(verbox, false, true);
    
    verbox->add(manage(new Label(_("MySQL Server Version:"))), false, true);
    verbox->add(&_server_version, true, true);
    scoped_connect(_server_version.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this, &_server_version));
    b = manage(new Button());
    b->set_text("Connect and Check...");
    scoped_connect(b->signal_clicked(),boost::bind(&ServerInstanceEditor::check_version, this));
    verbox->add(b, false, true);
    
    scoped_connect(_connection_selector.signal_changed(),boost::bind(&ServerInstanceEditor::connection_changed, this));
    panel->add(&_conn_box);
    vbox->add(panel, false, true);
  }
    
  // Remote management
  {
    Panel* remote_admin_panel = manage(new Panel(TitledGroupPanel)); // For border and title.
    remote_admin_panel->set_title(_("Remote Management"));

    Box* remote_admin_box = manage(new Box(false));                 // For the content.
    remote_admin_box->set_padding(MF_PANEL_PADDING);
    remote_admin_box->set_spacing(4);
    mforms::Table* remote_param_table = NewTable(6, 2);
    
    _no_remote_admin.set_text(_("Do not use remote management"));
    scoped_connect(_no_remote_admin.signal_toggled(),boost::bind(&ServerInstanceEditor::toggle_administration, this));
    _win_remote_admin.set_text(_("Native Windows remote management (only available on Windows)"));
    scoped_connect(_win_remote_admin.signal_toggled(),boost::bind(&ServerInstanceEditor::toggle_administration, this));
#ifndef _WIN32
    _win_remote_admin.set_enabled(false);
#endif
    _ssh_remote_admin.set_text(_("SSH login based management"));
    scoped_connect(_ssh_remote_admin.signal_toggled(),boost::bind(&ServerInstanceEditor::toggle_administration, this));

    remote_admin_box->add(&_no_remote_admin, false, true);
    remote_admin_box->add(&_win_remote_admin, false, true);
    remote_admin_box->add(&_ssh_remote_admin, false, true);

    _remote_param_box.set_spacing(12);
    _remote_param_box.add(manage(remote_param_table), true, true);

    remote_param_table->add(RLabel(_("Hostname:")), 0, 1, 0, 1, HFillFlag);
    Box *box= manage(new Box(true));
    box->set_spacing(MF_TABLE_COLUMN_SPACING);

    _remote_host.set_size(200, -1);
    box->add(&_remote_host, true, true);
    scoped_connect(_remote_host.signal_changed(),
      boost::bind(&ServerInstanceEditor::entry_changed, this, &_remote_host));
    
    box->add(manage(RLabel(_("Port:"))), false, true);
    _ssh_port.set_size(50, -1);
    box->add(&_ssh_port, true, true);
    scoped_connect(_ssh_port.signal_changed(),
      boost::bind(&ServerInstanceEditor::entry_changed, this, &_ssh_port));
    remote_param_table->add(box, 1, 2, 0, 1, HFillFlag | HExpandFlag);
    
    remote_param_table->add(manage(RLabel(_("Username:"))), 0, 1, 1, 2, HFillFlag);
    remote_param_table->add(&_remote_user, 1, 2, 1, 2, HExpandFlag|HFillFlag);
    scoped_connect(_remote_user.signal_changed(),
      boost::bind(&ServerInstanceEditor::entry_changed, this,  &_remote_user));
    remote_param_table->add(manage(RLabel(_("Password:"))), 0, 1, 2, 3, HFillFlag);
    remote_param_table->add(&_password_box, 1, 2, 2, 3, HExpandFlag|HFillFlag);

#ifdef _WIN32
    _password_set.set_text(_("Store in Vault ..."));
    _password_set.set_tooltip(_("Store the password for this connection in the secured vault."));
    _password_clear.set_text(_("Remove from Vault"));
    _password_clear.set_tooltip(_("Remove previously stored password from the secured vault."));
#else
    _password_set.set_text(_("Store in Keychain ..."));
    _password_set.set_tooltip(_("Store the password for this connection in the system's keychain."));
    _password_clear.set_text(_("Remove from Keychain"));
    _password_clear.set_tooltip(_("Remove previously stored password from the system's keychain"));
#endif

    scoped_connect(_password_set.signal_clicked(),boost::bind(&ServerInstanceEditor::set_password, this, false));
    scoped_connect(_password_clear.signal_clicked(),boost::bind(&ServerInstanceEditor::set_password, this, true));
    
    _password_box.set_spacing(8);
    _password_box.add(&_password_set, true, true);
    _password_box.add(&_password_clear, true, true);
    
    _ssh_usekey.set_text(_("Authenticate Using SSH Key"));
    scoped_connect(_ssh_usekey.signal_clicked(),
      boost::bind(&ServerInstanceEditor::check_changed, this, &_ssh_usekey));
    remote_param_table->add(&_ssh_usekey, 1, 2, 3, 4, HFillFlag);

    mforms::Label *l;
    remote_param_table->add(l = RLabel(_("SSH Key Path:")), 0, 1, 4, 5, HFillFlag);
    box= manage(new Box(true));
    box->set_spacing(MF_TABLE_COLUMN_SPACING);
    box->add(&_ssh_keypath, true, true);
    l->set_tooltip(_("Path to the SSH private key to use for connecting to the remote SSH server.\n"
                     "The key must be in the format used by OpenSSH. If you do not use OpenSSH to\n"
                     "generate the key (ssh-keygen), you may need to convert your key to that format."));
    _ssh_keypath.set_tooltip(_("Path to the SSH private key to use for connecting to the remote SSH server.\n"
                               "The key must be in the format used by OpenSSH. If you do not use OpenSSH to\n"
                               "generate the key (ssh-keygen), you may need to convert your key to that format."));
    scoped_connect(_ssh_keypath.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this,  
                                                  &_ssh_keypath));
    Button *b = manage(new Button());
    b->set_text(_("Browse"));
    box->add(b, false, true);
    scoped_connect(b->signal_clicked(),boost::bind(&ServerInstanceEditor::browse_file, this));
    remote_param_table->add(box, 1, 2, 4, 5, HFillFlag);

    remote_admin_box->add(&_remote_param_box, true, true);
    remote_admin_panel->add(remote_admin_box);
    vbox->add(remote_admin_panel, false, true);
  }
  
  {
    Box *box = manage(new Box(true));
    box->set_spacing(8);
    
    _autodetect_button.set_enabled(_grtm->get_grt()->get_module("WbAdmin")!=0);
    _autodetect_button.set_text(_("Detect Server Configuration..."));
    _autodetect_button.set_tooltip(_("Attempt to automatically detect server configuration parameters for the system,\n"
                                     "such as operating system type, path to configuration file, how to start/stop MySQL etc"));
    
    box->add(&_autodetect_button, true, false);
    scoped_connect(_autodetect_button.signal_clicked(),boost::bind(&ServerInstanceEditor::autodetect_system, this));
  }
  
  
  _tabview.add_page(&_sys_box, _("System Profile"));

  // Sys
  {
    Label *label = manage(new Label(_("Information about the server and MySQL configuration, such as path to the configuration file, "
                                      "command to start or stop it etc. You may pick a preset configuration profile or customize one for your needs.")));
    label->set_wrap_text(true);
    label->set_style(SmallStyle);
    
    _sys_box.add(label, false, true);

    Table *table = manage(NewTable(6, 2));
    _sys_box.add(table, false, true);

    {
      table->add(manage(RLabel(_("System Type:"))), 0, 1, 0, 1, HFillFlag);
      table->add(&_os_type, 1, 2, 0, 1, HFillFlag | HExpandFlag);
      scoped_connect(_os_type.signal_changed(),boost::bind(&ServerInstanceEditor::system_type_changed, this));
    }
    {
      label= manage(RLabel(_("Installation Type:")));
      table->add(label, 0, 1, 1, 2, HFillFlag);

      scoped_connect(_sys_profile_type.signal_changed(),boost::bind(&ServerInstanceEditor::profile_changed, this));
      table->add(&_sys_profile_type, 1, 2, 1, 2, HFillFlag | HExpandFlag);
    }

    { // use a custom file browsing field because the files can be local or remote
      Box *box = manage(new Box(true));
      box->set_spacing(8);
      table->add(manage(RLabel(_("Configuration File:"))), 0, 1, 2, 3, HFillFlag);

      box->add(&_sys_config_path, true, true);
      _sys_config_path_browse.enable_internal_padding(false);
      _sys_config_path_browse.set_text("...");
      box->add(&_sys_config_path_browse, false, true);
      scoped_connect(_sys_config_path_browse.signal_clicked(),boost::bind(&ServerInstanceEditor::run_filechooser_wrapper, this, &_sys_config_path));
      table->add(box, 1, 2, 2, 3, HFillFlag | HExpandFlag);
    }

    scoped_connect(_sys_config_path.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this,  
                                                  &_sys_config_path));
    table->add(manage(RLabel(_("Configuration File Section:"))), 0, 1, 3, 4, HFillFlag);
    table->add(&_sys_myini_section, 1, 2, 3, 4, HFillFlag | HExpandFlag);
    scoped_connect(_sys_myini_section.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this,  
                                                  &_sys_myini_section));

    table->add(_sys_win_service_name_label = manage(RLabel(_("Windows Service Name:"))), 0, 1, 4, 5, HFillFlag);
    table->add(&_sys_win_service_name, 1, 2, 4, 5, HFillFlag | HExpandFlag);
    scoped_connect(_sys_win_service_name.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this,  
                                                  &_sys_win_service_name));

    _sys_win_hint_label.set_text(WINDOWS_CONFIG_HINT_TEXT);
    table->add(&_sys_win_hint_label, 1, 2, 5, 6, HFillFlag|HExpandFlag);

    _details_description.set_text(_("\nThe following options specify how Workbench should perform certain management actions. "
      "Commands given here are the same as if they would be run in a system shell. Commands which return "
      "a value might be combined with some predefined filters. See \"Filters Help\" for more details."));
    _details_description.set_wrap_text(true);
    _details_description.set_style(SmallHelpTextStyle);
    _sys_box.add(&_details_description, false, true);
    
    _sys_box.add(&_details_tabview, true, true);
    
    table = NewTable(6, 2);
#if defined(_WIN32) || defined(__APPLE__)
    _details_tabview.add_page(manage(table), _("MySQL Management"));
#else
    // XXX tmp workaround for crash caused by changed destruction routine
    _details_tabview.add_page(table, _("MySQL Management"));
#endif
    
    table->add(manage(RLabel(_("Start MySQL:"))), 0, 1, 0, 1, HFillFlag);
    table->add(&_start_cmd, 1, 2, 0, 1, HFillFlag | HExpandFlag);
    scoped_connect(_start_cmd.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this,  
                                                  &_start_cmd));
    table->add(manage(RLabel(_("Stop MySQL:"))), 0, 1, 1, 2, HFillFlag);
    table->add(&_stop_cmd, 1, 2, 1, 2, HFillFlag | HExpandFlag);    
    scoped_connect(_stop_cmd.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this,  
                                                  &_stop_cmd));

    table->add(&_sudo_check, 1, 2, 2, 3, HFillFlag | HExpandFlag);
#ifndef _WIN32
    _sudo_check.set_text(_("Elevate privileges to execute start/stop commands\nand write configuration data"));
#else
    _sudo_check.set_text(_("Acquire administrator rights to execute start/stop commands\nand write configuration data"));
#endif
    scoped_connect(_sudo_check.signal_clicked(),boost::bind(&ServerInstanceEditor::check_changed, this, 
                                                  &_sudo_check));


    table->add(manage(RLabel(_("Check MySQL Status:"))), 0, 1, 3, 4, HFillFlag);
    table->add(&_status_cmd, 1, 2, 3, 4, HFillFlag | HExpandFlag);
    scoped_connect(_status_cmd.signal_changed(),boost::bind(&ServerInstanceEditor::entry_changed, this,  
                                                  &_status_cmd));


    table->add(&_sudo_status_check, 1, 2, 4, 5, HFillFlag | HExpandFlag);
#ifndef _WIN32
    _sudo_status_check.set_text(_("Elevate privileges to execute status commands"));
#else
    _sudo_status_check.set_text(_("Acquire administrator rights to execute status commands"));
#endif
    scoped_connect(_sudo_status_check.signal_clicked(),boost::bind(&ServerInstanceEditor::check_changed, this, 
                                                  &_sudo_status_check));

    table->add(manage(RLabel(_("Sudo command:"))), 0, 1, 5, 6, HFillFlag|VExpandFlag);
    {
      mforms::Box *box = manage(new Box(true));
      box->add(&_sudo_cmd, true, true);

      table->add(box, 1, 2, 5, 6, HFillFlag | HExpandFlag);    
      scoped_connect(_sudo_cmd.signal_changed(),boost::bind(&ServerInstanceEditor::selector_changed, this,
                                                    &_sudo_cmd));
    }

    {
      Box *stats_box = manage(new Box(false));
      _details_tabview.add_page(stats_box, _("Server Stats"));    
      scoped_connect(_script_text.signal_changed(),boost::bind(&ServerInstanceEditor::text_changed, this, 
                                                  &_script_text));

      stats_box->add(&_script_text, true, true);

      Box *box = manage(new Box(true));
      box->set_spacing(12);
    
      Button* help_filters_button  = manage(new Button());
      help_filters_button->set_text(_("Filters Help"));
      box->add_end(help_filters_button, false, true);
      scoped_connect(help_filters_button->signal_clicked(),boost::bind(&ServerInstanceEditor::show_filter_help, this));
      
      Button* test_filters_button  = manage(new Button());
      test_filters_button->set_text(_("Filters Debugger"));
      box->add_end(test_filters_button, false, true);
      scoped_connect(test_filters_button->signal_clicked(),boost::bind(&ServerInstanceEditor::run_filters_debugger, this));

      stats_box->add(box, false, false);
    }
  }
  

  _dup_inst_button.set_text(_("Duplicate"));
  scoped_connect(_dup_inst_button.signal_clicked(),boost::bind(&ServerInstanceEditor::duplicate_instance, this));

  _del_inst_button.set_text(_("Delete"));
  scoped_connect(_del_inst_button.signal_clicked(),boost::bind(&ServerInstanceEditor::delete_instance, this));
  _add_inst_button.set_text(_("New"));
  scoped_connect(_add_inst_button.signal_clicked(),boost::bind(&ServerInstanceEditor::add_instance, this));
  
  _move_up_button.set_text(_("Move Up"));
  scoped_connect(_move_up_button.signal_clicked(),boost::bind(&ServerInstanceEditor::reorder_instance, this, true));
  _move_down_button.set_text(_("Move Down"));
  scoped_connect(_move_down_button.signal_clicked(),boost::bind(&ServerInstanceEditor::reorder_instance, this, false));
  
  _bottom_hbox.add(&_add_inst_button, false, true);
  _bottom_hbox.add(&_del_inst_button, false, true);
  _bottom_hbox.add(&_dup_inst_button, false, true);
  _bottom_hbox.add(&_move_up_button, false, true);
  _bottom_hbox.add(&_move_down_button, false, true);
  
  _bottom_hbox.add_end(&_ok_button, false, true);
  //_bottom_hbox.add_end(&_test_button, false, true);  
  //  _bottom_hbox.add_end(&_cancel_button, false, true);
  
  _ok_button.set_text(_("Close"));

  _test_button.set_text(_("Test Settings"));
  _test_button.set_enabled(_grtm->get_grt()->get_module("WbAdmin")!=0);
  scoped_connect(_test_button.signal_clicked(),boost::bind(&ServerInstanceEditor::test_settings, this));
  
  _add_inst_button.enable_internal_padding(true);
  _del_inst_button.enable_internal_padding(true);
  _ok_button.enable_internal_padding(true);
  _test_button.enable_internal_padding(true);

  _stored_instance_list.set_size(180, -1);
  
  set_content(&_top_vbox);
  
  _stored_instance_list.add_column(::mforms::StringColumnType, _("Server Instances"), 150, false);
  _stored_instance_list.end_columns();

  set_size(820, 700);
  center();
  
  ///
  
  std::string path= _grtm->get_data_file_path("mysql.profiles");
  GDir *dir = g_dir_open(path.c_str(), 0, NULL);
  std::set<std::string> sudo_commands;
  if (dir)
  {
    const gchar *file;
    while ((file = g_dir_read_name(dir)))
    {
      if (g_str_has_suffix(file, ".xml"))
      {
        std::string fname= std::string(file, strlen(file)-4);
        std::string label= bec::replace_string(fname, "_", " ");
        grt::DictRef dict;
        try
        {
          dict= grt::DictRef::cast_from(_grtm->get_grt()->unserialize(path+"/"+file));
        }
        catch (std::exception &exc)
        {
          g_warning("Profile %s contains invalid data: %s", path.c_str(), exc.what());
          continue;
        }
        if (dict.has_key("sys.sudo"))
        {
          sudo_commands.insert(dict.get_string("sys.sudo"));
        }
        _presets[dict.get_string("sys.system")].push_back(std::make_pair(label, dict));
      }
    }
    g_dir_close(dir);
  }  

  // Fill in items in sudo commands selector
  std::set<std::string>::const_iterator it = sudo_commands.begin();
  const std::set<std::string>::const_iterator end = sudo_commands.end();
  for (; it != end; ++it)
    _sudo_cmd.add_item(*it);
}


void ServerInstanceEditor::set_password(bool clear)
{
  std::string port = _ssh_port.get_string_value();
  
  std::string storage_key;
  if (_ssh_remote_admin.get_active())
  {
    // WBA stores password with key ssh@host, without port
    //storage_key = strfmt("ssh@%s:%s", _remote_host.get_string_value().c_str(), port.empty() ? "22" : port.c_str());
    storage_key = strfmt("ssh@%s", _remote_host.get_string_value().c_str());
  }
  else
    storage_key = "wmi@" + _remote_host.get_string_value();
  std::string username = _remote_user.get_string_value();
  
  if (username.empty())
  {
    mforms::Utilities::show_warning("Cannot Set Password", "Please fill the username to be used.", "OK");
    return;
  }

  if (clear)
  {
    try
    {
      mforms::Utilities::forget_password(storage_key, username);
    }
    catch (std::exception &exc)
    {
      mforms::Utilities::show_error("Clear Password", 
                                    base::strfmt("Could not clear password: %s", exc.what()),
                                    "OK");
    }
  }
  else
  {
    std::string password;
    
    try
    {
      if (mforms::Utilities::ask_for_password(_("Store Password For Server"), 
                                              storage_key, username, password))
        mforms::Utilities::store_password(storage_key, username, password);
    }
    catch (std::exception &exc)
    {
      mforms::Utilities::show_error("Store Password", 
                                    base::strfmt("Could not store password: %s", exc.what()),
                                    "OK");
    }
  }
  show_instance();
}


void ServerInstanceEditor::check_version()
{
  db_mgmt_ServerInstanceRef instance(selected_instance());
  
  if (instance.is_valid() && instance->connection().is_valid())
  {
    try
    {
      sql::ConnectionWrapper dbc_conn= sql::DriverManager::getDriverManager()->getConnection(instance->connection());
      
      if (dbc_conn.get() != NULL)
      {
        boost::shared_ptr<sql::Statement> stmt(dbc_conn->createStatement());
        boost::shared_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT VERSION()"));
        if (res.get() && res->next())
        {
          std::string version = res->getString(1);
          _server_version.set_value(version);
          entry_changed(&_server_version);
          
          int major, minor;
          if (sscanf(version.c_str(), "%i.%i", &major, &minor) == 2 && major < 5)
            mforms::Utilities::show_warning("Unsupported Server Version", 
                                            strfmt("MySQL Server version is %s, which is not supported by Workbench.", version.c_str()),
                                            "OK");
          return;
        }
      }
      mforms::Utilities::show_warning("Error Verifying Server Version", "Could not fetch server version.", "OK");
    }
    catch (std::exception &exc)
    {
      mforms::Utilities::show_warning("Error Verifying Server Version", exc.what(), "OK");
    }
  }
}


void ServerInstanceEditor::show_filter_help()
{
  mforms::Form window(0);
  mforms::Box vbox(false);
  mforms::Box box(true);
  mforms::Button ok;
  mforms::Label label;
  
  window.set_title("MySQL Management Commands Help");

//  window.set_size(400, 200);
  window.set_content(&vbox);

  vbox.set_padding(12);
  vbox.set_spacing(12);
  
  label.set_text(filter_help);
  
  vbox.add(&label, true, true);
  vbox.add(&box, false, true);
  
  ok.set_text("Close");
  box.add_end(&ok, false, true);

  window.center();
  window.run_modal(&ok, 0);
}


db_mgmt_ServerInstanceRef ServerInstanceEditor::run()
{
  refresh_options();
  refresh_instance_list();
    
  _stored_instance_list.select_node(_stored_instance_list.node_at_row(0));
  instance_changed();
  run_modal(&_ok_button, NULL);

  return selected_instance();
}

void ServerInstanceEditor::run_filters_debugger()
{
  grt::Module *module= _grtm->get_grt()->get_module("WbAdmin");
  if (module)
  {
    db_mgmt_ServerInstanceRef instance(selected_instance());
    grt::BaseListRef args(_grtm->get_grt());
    args.ginsert(instance->serverInfo());
    args.ginsert(instance->loginInfo());

    grt::IntegerRef selection = grt::IntegerRef::cast_from(module->call_function("openFilterDebugger", args));
  }
}

void ServerInstanceEditor::run_filechooser_wrapper(mforms::TextEntry* entry) // Allows to run local or remote file selector
{
  db_mgmt_ServerInstanceRef instance(selected_instance());
  bool is_local = false;

  if (instance.is_valid())
    is_local = is_local_connection(instance->connection());

  if (is_local)
    run_filechooser(entry);
  else
  {
    grt::Module *module= _grtm->get_grt()->get_module("WbAdmin");
    if (module)
    {
      grt::BaseListRef args(_grtm->get_grt());
      args.ginsert(instance);

      try
      {
        grt::StringRef selection = grt::StringRef::cast_from(module->call_function("openRemoteFileSelector", args));
        if (!selection.empty())
        {
          entry->set_value(selection.c_str());
          entry_changed(entry);
        }
      }
      catch (const std::exception &exc)
      {
        _grtm->get_grt()->send_error("Error in remote file browser", exc.what());
      }
    }
  }
}

void ServerInstanceEditor::run_filechooser(mforms::TextEntry* entry)
{
  mforms::FileChooser fc(mforms::OpenFile, true);
  //TODO: Add set directory
  if (fc.run_modal())
  {
    const std::string path = fc.get_path();
    if (!path.empty() || path != "")
      entry->set_value(path);
    (*entry->signal_changed())();
  }
}

void ServerInstanceEditor::system_type_changed()
{
  db_mgmt_ServerInstanceRef instance(selected_instance());
  
  if (instance.is_valid())
  {
    const int i= _os_type.get_selected_index();
    if (i>=0)
    {
      const std::string &system = system_stat_scripts[i].name;
      instance->serverInfo().gset("sys.system", system);

      const bool is_not_windows = system.find("indows") == std::string::npos;
      _sudo_status_check.set_enabled(is_not_windows);
      _sudo_cmd.set_enabled(is_not_windows);

      if (!is_not_windows)
        _sudo_status_check.set_active(false);

      if (system_stat_scripts[i].script)
      {
        std::string script= SYSTEM_STAT_HEADER;
        script.append(get_admin_script_for_os(system));
        _script_text.set_value(script);
        instance->serverInfo().gset("sys.script", script);
      }

      refresh_profile_list();
    
      profile_changed();
    }
  }
}


void ServerInstanceEditor::refresh_profile_list()
{
  const int i= _os_type.get_selected_index();
  if (i >= 0)
  {
    const std::string system = system_stat_scripts[i].name;

    _sys_profile_type.clear();
    std::vector<std::pair<std::string,grt::DictRef> >::const_iterator iter = _presets[system].begin();
    for (;iter != _presets[system].end(); ++iter)
    {
      _sys_profile_type.add_item(iter->first);
    }
    _sys_profile_type.add_item(_("Custom"));  
  }
}


void ServerInstanceEditor::refresh_instance_list()
{
  _stored_instance_list.clear();

  GRTLIST_FOREACH(db_mgmt_ServerInstance, _instances, inst)
  {
    TreeNodeRef node = _stored_instance_list.root_node()->add_child();
    node->set_string(0, *(*inst)->name());
  }
}

db_mgmt_ServerInstanceRef ServerInstanceEditor::selected_instance()
{
  TreeNodeRef node= _stored_instance_list.get_selected_node();
  int row = _stored_instance_list.row_for_node(node);
  if (row >= 0)
    return _instances[row];

  return db_mgmt_ServerInstanceRef();
}


void ServerInstanceEditor::autodetect_system()
{
  grt::Module *module= _grtm->get_grt()->get_module("WbAdmin");
  if (module)
  {
    grt::BaseListRef args(_grtm->get_grt());
    args.ginsert(selected_instance());

    module->call_function("detectInstanceSettings", args);
  }
}


void ServerInstanceEditor::test_settings()
{
  grt::Module *module= _grtm->get_grt()->get_module("WbAdmin");
  if (module)
  {
    grt::BaseListRef args(_grtm->get_grt());
    grt::ValueRef ret;
    args.ginsert(selected_instance());

    ret = module->call_function("testInstanceSettings", args);
  }
  else
    fprintf(stderr, "module WbAdmin not found\n");
}

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

void ServerInstanceEditor::toggle_administration()
{
  db_mgmt_ServerInstanceRef instance(selected_instance());
  bool local_connection = false;
  bool ssh_administration= _ssh_remote_admin.get_active();
  bool win_administration= _win_remote_admin.get_active();

  if (instance.is_valid())
  {
    local_connection= is_local_connection(instance->connection());

    // Note: remoteAdmin means actually SSH based remote administration.
    if (ssh_administration)
      instance->serverInfo().gset("remoteAdmin", 1);
    else
      instance->serverInfo().remove("remoteAdmin");

    // Win admin and ssh admin are mutual exclusive. This semantic is enforced by radio buttons in the UI.
#ifdef _WIN32
    if (local_connection || win_administration)
#else
    if (win_administration)
#endif
      instance->serverInfo().gset("windowsAdmin", 1);
    else
      instance->serverInfo().remove("windowsAdmin");
  }

  // Switch on server parameters if we have a remote connection.
  _remote_param_box.set_enabled(!_no_remote_admin.get_active());

  // Enable only fields relevant to that connection.
  _ssh_port.set_enabled(ssh_administration);
  _ssh_keypath.set_enabled(ssh_administration);
  _ssh_usekey.set_enabled(ssh_administration);
  
  //_sys_config_path_browse.set_enabled(is_local);
  
  // If this is a local connection or a remote one with remote management abilities then
  // enable the system commands section.
  bool can_administer= local_connection || ssh_administration || win_administration;
  _sys_box.set_enabled(can_administer);
}

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

void ServerInstanceEditor::add_instance()
{
  db_mgmt_ServerInstanceRef instance(_instances.get_grt());
  std::string name= "new instance";
  TreeNodeRef node;
  bool dupe;
  int i = 1;
  do
  {
    dupe= false;
    GRTLIST_FOREACH(db_mgmt_ServerInstance, _instances, inst)
    {
      if ((*inst)->name() == name)
      {
        name= strfmt("new instance %i", i++);
        dupe= true;
        break;
      }
    }
  } while (dupe);
  
  node = _stored_instance_list.root_node()->add_child();
  if (node)
  {
    node->set_string(0, name);

    instance->owner(_mgmt);
    instance->name(name);
    instance->loginInfo().gset("ssh.hostName", "");
    instance->loginInfo().gset("ssh.localPort", "3306");
    instance->loginInfo().gset("ssh.userName", "mysql");
    instance->loginInfo().gset("ssh.useKey", 1);
    
    std::string homedir=
#ifdef _WIN32
      mforms::Utilities::get_special_folder(mforms::ApplicationData);
#else
      "~";
#endif
    instance->loginInfo().gset("ssh.key", homedir + "/.ssh/ssh_private_key");
    
    if (!_presets.empty())
    {
      instance->serverInfo().gset("sys.system", "Windows");
      instance->serverInfo().gset("sys.preset", _presets["Windows"][0].first);
    }
    
    _instances.insert(instance);
    _stored_instance_list.select_node(node);
  }
  else
    g_warning("add_row returned -1");

  //show_instance();
  instance_changed();
    
  system_type_changed();
  
  connection_changed();
}


void ServerInstanceEditor::delete_instance()
{
  TreeNodeRef node(_stored_instance_list.get_selected_node());
  int row = _stored_instance_list.row_for_node(node);
  if (row >= 0 && row < (int)_instances.count())
  {
    _instances.remove(row);
    refresh_instance_list();
    if (row > 0)
    {
      _stored_instance_list.select_node(_stored_instance_list.node_at_row(row-1));
      instance_changed();
    }
  }
}


void ServerInstanceEditor::duplicate_instance()
{
  db_mgmt_ServerInstanceRef orig(selected_instance());
  db_mgmt_ServerInstanceRef instance(_instances.get_grt());
  
  if (!orig.is_valid())
    return;
  
  std::string name= orig->name();
  bool dupe;
  int i = 1;
  do
  {
    dupe= false;
    GRTLIST_FOREACH(db_mgmt_ServerInstance, _instances, inst)
    {
      if ((*inst)->name() == name)
      {
        name= strfmt("%s %i", orig->name().c_str(), i++);
        dupe= true;
        break;
      }
    }
  } while (dupe);
  
  TreeNodeRef node(_stored_instance_list.root_node()->add_child());
  if (node)
  {
    node->set_string(0, name);
    
    instance->owner(_mgmt);
    instance->name(name);
    instance->connection(orig->connection());
    grt::merge_contents(instance->loginInfo(), orig->loginInfo(), true);
    grt::merge_contents(instance->serverInfo(), orig->serverInfo(), true);
    
    _instances.insert(instance);
    _stored_instance_list.select_node(node);
  }
  else
    g_warning("add_row returned -1");
  
  show_instance();  
}


void ServerInstanceEditor::reorder_instance(bool up)
{
  int row = _stored_instance_list.get_selected_row();

  if (row < 0)
    return;

  if (up)
  {
    if (row > 0)
    {
      _instances.reorder(row, row-1);
      _stored_instance_list.select_node(_stored_instance_list.node_at_row(row-1));
    }
  }
  else
  {
    if (row < _stored_instance_list.root_node()->count()-1)
    {
      _instances.reorder(row, row+1);
      _stored_instance_list.select_node(_stored_instance_list.node_at_row(row+1));
    }
  }

  row= 0;
  GRTLIST_FOREACH(db_mgmt_ServerInstance, _instances, inst)
  {
    _stored_instance_list.root_node()->get_child(row++)->set_string(0, (*inst)->name().c_str());
  }  
}


void ServerInstanceEditor::browse_file()
{
  FileChooser fsel(mforms::OpenFile, true);

  fsel.set_title(_("Pick SSH Private Key"));

  if (fsel.run_modal())
  {
    _ssh_keypath.set_value(fsel.get_path());
    entry_changed(&_ssh_keypath);
  }
}


grt::DictRef ServerInstanceEditor::get_preset(const std::string& system, const std::string& preset_name)
{
  grt::DictRef result;

  for (std::vector<std::pair<std::string,grt::DictRef> >::const_iterator iter = _presets[system].begin();
       iter != _presets[system].end(); ++iter)
  {
    if (iter->first == preset_name)
    {
      result = iter->second;
      break;
    }
  }

  return result;
}


void ServerInstanceEditor::entry_changed(mforms::TextEntry *sender)
{
  const std::string value= sender->get_string_value();
  db_mgmt_ServerInstanceRef instance(selected_instance());
  
  if (instance.is_valid())
  {
    if (&_name_entry == sender)
    {
      TreeNodeRef node(_stored_instance_list.get_selected_node());
      instance->name(value);
      if (node)
        node->set_string(0, value);
    }
    else if (&_server_version == sender)
      instance->serverInfo().gset("serverVersion", value);
    else if (&_remote_host == sender)
    {
      if (_ssh_remote_admin.get_active())
        instance->loginInfo().gset("ssh.hostName", value);
      else
        instance->loginInfo().gset("wmi.hostName", value);
    }
    else if (&_ssh_port == sender)
    {
      instance->loginInfo().gset("ssh.tunnelPort", value);
      instance->loginInfo().gset("ssh.port", value);
    }
    else if (&_remote_user == sender)
    {
      if (_ssh_remote_admin.get_active())
        instance->loginInfo().gset("ssh.userName", value);
      else
        instance->loginInfo().gset("wmi.userName", value);
    }
    else if (&_ssh_keypath == sender)
    {
      instance->loginInfo().gset("ssh.key", value);
      instance->loginInfo().gset("ssh.useKey", 1);
      _ssh_usekey.set_active(true);
    }
    else if (&_sys_config_path == sender)
    {
      instance->serverInfo().gset("sys.config.path", value);
      _sys_profile_type.set_selected(_sys_profile_type.get_item_count()-1);
      instance->serverInfo().gset("sys.preset", "");
    }
    else if (&_sys_myini_section == sender)
    {
      instance->serverInfo().gset("sys.config.section", value);
      _sys_profile_type.set_selected(_sys_profile_type.get_item_count()-1);
      instance->serverInfo().gset("sys.preset", "");
    }
    else if (&_sys_win_service_name == sender)
    {
      instance->serverInfo().gset("sys.mysqld.service_name", value);
      instance->serverInfo().gset("sys.preset", "");
    }
    else if (&_start_cmd == sender)
    {
      instance->serverInfo().gset("sys.mysqld.start", value);
      _sys_profile_type.set_selected(_sys_profile_type.get_item_count()-1);
      instance->serverInfo().gset("sys.preset", "");
    }
    else if (&_stop_cmd == sender)
    {
      instance->serverInfo().gset("sys.mysqld.stop", value);
      _sys_profile_type.set_selected(_sys_profile_type.get_item_count()-1);
      instance->serverInfo().gset("sys.preset", "");
    }
    else if (&_status_cmd == sender)
    {
      instance->serverInfo().gset("sys.mysqld.status", value);
      _sys_profile_type.set_selected(_sys_profile_type.get_item_count()-1);
      instance->serverInfo().gset("sys.preset", "");
    }
  }
}


void ServerInstanceEditor::selector_changed(mforms::Selector* sender)
{
  db_mgmt_ServerInstanceRef instance(selected_instance());
  const std::string value= sender->get_string_value();
  if (&_sudo_cmd == sender)
    instance->serverInfo().gset("sys.sudo", value);
}

void ServerInstanceEditor::check_changed(mforms::CheckBox *sender)
{
  const bool value= sender->get_active();
  db_mgmt_ServerInstanceRef instance(selected_instance());

  if (instance.is_valid())
  {
    grt::DictRef info(instance->serverInfo());

    if (&_ssh_usekey == sender)
      instance->loginInfo().gset("ssh.useKey", value ? 1 : 0);
    else if (&_sudo_check == sender)
      info.gset("sys.usesudo", value ? 1 : 0);
    else if (&_sudo_status_check == sender)
      info.gset("sys.usesudostatus", value ? 1 : 0);
  }
}


void ServerInstanceEditor::text_changed(mforms::TextBox *text)
{
  const std::string value= text->get_string_value();
  db_mgmt_ServerInstanceRef instance(selected_instance());

  if (instance.is_valid())
  {
    instance->serverInfo().gset("sys.script", value);
    _sys_profile_type.set_selected(_sys_profile_type.get_item_count()-1);
  }
}


void ServerInstanceEditor::open_editor(bool edit_selected)
{
  grtui::DbConnectionEditor editor(_mgmt);
  db_mgmt_ConnectionRef selected;
  if (edit_selected)
  {
    const int conn= _connection_selector.get_selected_index();
    if (conn >= 0 && conn < (int)_mgmt->storedConns().count())
      selected = _mgmt->storedConns()[conn];
  }

  selected= editor.run(selected);
  
  if (!edit_selected && selected.is_valid())
  {
    grt::ListRef<db_mgmt_Connection> conns(_mgmt->storedConns());

    size_t index= conns.get_index(selected);
    if (index != grt::BaseListRef::npos)
    {
      refresh_options();
      _connection_selector.set_selected(index);
    }
    else
      refresh_options();
  }
}


void ServerInstanceEditor::connection_changed()
{
  const int conn= _connection_selector.get_selected_index();
  db_mgmt_ServerInstanceRef instance(selected_instance());

  if (instance.is_valid() && conn >= 0 && conn < (int)_mgmt->storedConns().count())
  {
    db_mgmt_ConnectionRef connection(_mgmt->storedConns()[conn]);
    if (is_local_connection(connection))
    {
      // If the MySQL connection is to a local server then remote administration makes no sense.
      // Disable it in this case.
      _no_remote_admin.set_active(true);
      _win_remote_admin.set_enabled(false);
      _ssh_remote_admin.set_enabled(false);

      instance->loginInfo().gset("ssh.hostName", "");
      instance->loginInfo().gset("wmi.hostName", "");
      _remote_host.set_value("");
    }
    else
    {
#ifdef _WIN32
      _win_remote_admin.set_enabled(true);
#else
      _win_remote_admin.set_enabled(false);
#endif
      _ssh_remote_admin.set_enabled(true);

      const std::string driver= connection->driver().is_valid() ? connection->driver()->name() : "";

      // If the MySQL connection is SSH based then copy its settings to the management settings
      // as an easier starting point.
      if (driver == "MysqlNativeSSH")
      {
        std::string host = connection->parameterValues().get_string("sshHost");
        std::string port;

        if (host.find(':') != std::string::npos)
        {
          port = host.substr(host.find(':')+1);
          host = host.substr(0, host.find(':'));
        }
        else
          port = "22";

        _remote_host.set_value(host);
        _ssh_port.set_value(port);
        _remote_user.set_value(connection->parameterValues().get_string("sshUserName"));
        _ssh_keypath.set_value(connection->parameterValues().get_string("sshKeyFile"));

        instance->loginInfo().gset("ssh.hostName", host);
        instance->loginInfo().gset("ssh.port", port);
        instance->loginInfo().gset("ssh.userName", _remote_user.get_string_value());
        instance->loginInfo().gset("ssh.useKey", _ssh_keypath.get_string_value() != "");
        instance->loginInfo().gset("ssh.key", _ssh_keypath.get_string_value());
      }
      else
      {
        const std::string hostname= connection->parameterValues().get_string("hostName");

        if (!hostname.empty())
          instance->loginInfo().gset("wmi.hostName", hostname);
        _remote_host.set_value(hostname);        
      }
    }
    instance->connection(connection);
  }

  toggle_administration();
}


void ServerInstanceEditor::profile_changed()
{
  db_mgmt_ServerInstanceRef instance(selected_instance());
  const int systype = _sys_profile_type.get_selected_index();
  if (systype >= 0 && instance.is_valid())
  {
    const std::string system = instance->serverInfo().get_string("sys.system");
    if (systype < (int)_presets[system].size())
    {
      const std::string name = _presets[system][systype].first;
      grt::DictRef dict = _presets[system][systype].second;

      grt::merge_contents(instance->serverInfo(), dict, true);
      instance->serverInfo().gset("sys.preset", name);
      show_instance();
    }
  }
}

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

void ServerInstanceEditor::refresh_options()
{
  _connection_selector.clear();
  GRTLIST_FOREACH(db_mgmt_Connection, _mgmt->storedConns(), conn)
  {
    grt::DictRef params = (*conn)->parameterValues();
    std::string username= params.get_string("userName");
    std::string hostname= params.get_string("hostName");
    int port = params.get_int("port");
    std::string driver = (*conn)->driver().is_valid() ? (*conn)->driver()->caption() : "";

    // If host name is empty we have a pipe/socket connection.
    std::string connection_string;
    if (!hostname.empty())
    {
      connection_string= strfmt("%s - %s@%s:%i <%s>", (*conn)->name().c_str(), username.c_str(),
                                hostname.c_str(), port, driver.c_str());
    }
    else
    {
      std::string socket= params.get_string("socket");
      connection_string= (*conn)->name();
      if (socket.empty())
        connection_string += " - (default socket/pipe) ";
      else {
        connection_string += " - socket/pipe: " + socket + " ";
      }

      connection_string += " <" + driver + ">";
    }
    _connection_selector.add_item(connection_string);
  }

  _os_type.clear();
  for (int i = 0; system_stat_scripts[i].name; i++)
  {
    _os_type.add_item(system_stat_scripts[i].name);
  }
}


void ServerInstanceEditor::instance_changed()
{
  TreeNodeRef node(_stored_instance_list.get_selected_node());
  
  if (node)
  {
    _content_box.set_enabled(true);
    _move_up_button.set_enabled(true);
    _move_down_button.set_enabled(true);
    _dup_inst_button.set_enabled(true);
  }
  else
  {
    _content_box.set_enabled(false);
    _move_up_button.set_enabled(false);
    _move_down_button.set_enabled(false);
    _dup_inst_button.set_enabled(false);
  }
  
  show_instance();
}

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

void ServerInstanceEditor::show_instance()
{
  int row = _stored_instance_list.get_selected_row();
  db_mgmt_ServerInstanceRef instance;

  if (row >= 0)
  {
    instance= _instances[row];
    _del_inst_button.set_enabled(true);
    _dup_inst_button.set_enabled(true);
  }
  else
  {
    instance= db_mgmt_ServerInstanceRef(_mgmt.get_grt());
    _del_inst_button.set_enabled(false);
    _dup_inst_button.set_enabled(false);
  }

  grt::DictRef defaults;

  grt::DictRef info(instance->serverInfo());

  int j;
  const std::string system = info.get_string("sys.system");
  for (j = 0; system_stat_scripts[j].name; j++)
  {
    if (system.compare(system_stat_scripts[j].name) == 0)
    {
      _os_type.set_selected(j);
      break;
    }
  }

  refresh_profile_list();

  bool found = false;
  const std::string preset = info.get_string("sys.preset");
  j = 0;
  for (std::vector<std::pair<std::string,grt::DictRef> >::const_iterator iter = _presets[system].begin();
       iter != _presets[system].end(); ++iter, ++j)
  {
    if (iter->first == preset)
    {
      defaults = iter->second;
      found= true;
      _sys_profile_type.set_selected(j);
      break;
    }
  }

  if (!found)
    _sys_profile_type.set_selected(_sys_profile_type.get_item_count()-1);

  bool local_connection = false;
  if (instance->connection().is_valid())
  {
    local_connection = is_local_connection(instance->connection());
    int index = _mgmt->storedConns().get_index(instance->connection());
    if (index >= 0)
      _connection_selector.set_selected(index);
    else
      g_message("Invalid stored DB connection %s referenced from server instance profile", instance->connection()->name().c_str());
  }
  
  _name_entry.set_value(instance->name().c_str());
  
  _server_version.set_value(instance->serverInfo().get_string("serverVersion"));
  
  // Remote administration settings.
  grt::DictRef serverInfo(instance->serverInfo());

  // Admin settings are adjusted implicitly if we are not running on Windows but the profile is set for
  // remote Windows administration. Similar for enabled remote management but a local MySQL connection.
  if (serverInfo.get_int("remoteAdmin") != 0)
    _ssh_remote_admin.set_active(true);
  else
#ifdef _WIN32
    if (serverInfo.get_int("windowsAdmin") != 0)
      _win_remote_admin.set_active(true);
    else
#endif
      _no_remote_admin.set_active(true);

  grt::DictRef loginInfo(instance->loginInfo());

  std::string storage_key;
  std::string port = _ssh_port.get_string_value();
  std::string username;
  if (_ssh_remote_admin.get_active())
  {
    _remote_host.set_value(loginInfo.get_string("ssh.hostName"));
    _remote_user.set_value(loginInfo.get_string("ssh.userName"));
    username = _remote_user.get_string_value();
    // WBA stores password key as "ssh@<host>"
    //storage_key = strfmt("ssh@%s:%s", _remote_host.get_string_value().c_str(), port.empty() ? "22" : port.c_str());
    storage_key = strfmt("ssh@%s", _remote_host.get_string_value().c_str());
  }
  else
  {
    _remote_host.set_value(loginInfo.get_string("wmi.hostName"));
    _remote_user.set_value(loginInfo.get_string("wmi.userName"));
    username = _remote_user.get_string_value();
    storage_key = "wmi@" + _remote_host.get_string_value();
  }
  _ssh_port.set_value(loginInfo.get_string("ssh.tunnelPort"));
  _ssh_usekey.set_active(loginInfo.get_int("ssh.useKey") != 0);
  _ssh_keypath.set_value(loginInfo.get_string("ssh.key"));

  std::string dummy;
  
  if (mforms::Utilities::find_password(storage_key, username, dummy))
    _password_clear.set_enabled(true);
  else
    _password_clear.set_enabled(false);

  // Hide system specifics if the target is Windows with wmi.
  if (system == "Windows" && serverInfo.get_int("windowsAdmin"))
  {
    _details_description.show(false);
    _details_tabview.show(false);
    _sys_win_service_name.show(true);
    _sys_win_service_name_label->show(true);
    _sys_win_hint_label.show(true);
  }
  else
  {
    _details_description.show(true);
    _details_tabview.show(true);
    _sys_win_service_name.show(false);
    _sys_win_service_name_label->show(false);
    _sys_win_hint_label.show(false);
  }

  // If the MySQL connection is to a local server then remote administration makes no sense.
  // Disable it in this case.
  if (local_connection)
  {
    _no_remote_admin.set_active(true);
    _win_remote_admin.set_enabled(false);
    _ssh_remote_admin.set_enabled(false);
  }
  else
  {
#ifdef _WIN32
    _win_remote_admin.set_enabled(true);
#else
    _win_remote_admin.set_enabled(false);
#endif
    _ssh_remote_admin.set_enabled(true);
  }
  toggle_administration();

  _sys_config_path.set_value(serverInfo.get_string("sys.config.path"));
  _sys_myini_section.set_value(serverInfo.get_string("sys.config.section"));
  if (system == "Windows")
    _sys_win_service_name.set_value(serverInfo.get_string("sys.mysqld.service_name"));

  _start_cmd.set_value(serverInfo.get_string("sys.mysqld.start"));
  _stop_cmd.set_value(serverInfo.get_string("sys.mysqld.stop"));
  _status_cmd.set_value(serverInfo.get_string("sys.mysqld.status"));

  _sudo_check.set_active(serverInfo.get_int("sys.usesudo", 1) != 0);
  _sudo_status_check.set_active(serverInfo.get_int("sys.usesudostatus", 0) != 0);

  if (serverInfo.has_key("sys.sudo"))
  {
    if (serverInfo.get_string("sys.sudo") == "" && defaults.is_valid() && defaults.has_key("sys.sudo"))
      serverInfo.gset("sys.sudo", defaults.get_string("sys.sudo"));
  }
  else
  {
    if (defaults.is_valid() && defaults.has_key("sys.sudo"))
      serverInfo.gset("sys.sudo", defaults.get_string("sys.sudo"));
    else
      serverInfo.gset("sys.sudo", "");
  }

  _sudo_cmd.set_value(serverInfo.get_string("sys.sudo"));

  _script_text.set_value(serverInfo.get_string("sys.script"));
}

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

