/* 
 * Copyright (c) 2007, 2011, 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 "grtdb_connect_panel.h"
#include "grtdb_connection_editor.h"
#include "mforms/fs_object_selector.h"
#include "grtdb/db_helpers.h"

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

#include "mforms/uistyle.h"
#include "mforms/utilities.h"
#include "mforms/checkbox.h"

using namespace MySQL::Geometry;
using namespace grtui;
using namespace mforms;

static bool is_ssh_driver(const std::string &driver_name)
{
  char *name = g_strdown(g_strdup(driver_name.c_str()));
  if (name && g_strstr_len(name, strlen(name), "ssh"))
  {
    g_free(name);
    return true;
  }
  g_free(name);
  return false;
}


DbConnectPanel::DbConnectPanel(bool show_connection_combo)
: Box(false), _connection(0), _show_connection_combo(show_connection_combo), _tab(mforms::TabViewSystemStandard)
{
  _rdbms_index= -1;
  _driver_index= -1;
  _initialized= false;
  _updating= false;
  
  _skip_schema_name= false;
  _delete_connection_be= false;
  
  set_spacing(4);

  if (show_connection_combo)
    _label1.set_text(_("Stored Connection:"));
  else
    _label1.set_text(_("Connection Name:"));
  _label2.set_text(_("Database System:"));
  _label3.set_text(_("Connection Method:"));
  
  _label1.set_text_align(MiddleRight);
  _label2.set_text_align(MiddleRight);
  _label3.set_text_align(MiddleRight);
  
  if (show_connection_combo)
    _desc1.set_text(_("Select from saved connection settings"));
  else
    _desc1.set_text(_("Type a name for the connection"));
  _desc1.set_style(mforms::SmallHelpTextStyle);
  _desc2.set_text(_("Select a RDBMS from the list of supported systems"));
  _desc2.set_style(mforms::SmallHelpTextStyle);
  _desc3.set_text(_("Method to use to connect to the RDBMS"));
  _desc3.set_style(mforms::SmallHelpTextStyle);

  if (show_connection_combo)
    scoped_connect(_stored_connection_sel.signal_changed(),boost::bind(&DbConnectPanel::change_active_stored_conn, this));
  scoped_connect(_rdbms_sel.signal_changed(),boost::bind(&DbConnectPanel::change_active_rdbms, this));
  scoped_connect(_driver_sel.signal_changed(),boost::bind(&DbConnectPanel::change_active_driver, this));
  
  _table.set_row_count(2);
  _table.set_column_count(3);
  
  _table.set_column_spacing(4);
  _table.set_row_spacing(4);

  if (show_connection_combo)
  {
    _table.add(&_label1, 0, 1, 0, 1, mforms::HFillFlag);
    _table.add(&_stored_connection_sel, 1, 2, 0, 1, mforms::HExpandFlag | mforms::HFillFlag);
    _table.add(&_desc1, 2, 3, 0, 1, mforms::HFillFlag);
  }
  else
  {
    _table.add(&_label1, 0, 1, 0, 1, mforms::HFillFlag);
    _table.add(&_name_entry, 1, 2, 0, 1, mforms::HExpandFlag | mforms::HFillFlag);
    _table.add(&_desc1, 2, 3, 0, 1, mforms::HFillFlag);    
  }
  
  _table.add(&_label3, 0, 1, 1, 2, mforms::HFillFlag);
  _table.add(&_driver_sel, 1, 2, 1, 2, mforms::HExpandFlag | mforms::HFillFlag);
  _table.add(&_desc3, 2, 3, 1, 2, mforms::HFillFlag);

  _tab.add_page(&_params_table, _("Parameters"));

  _params_table.set_column_count(3);
  _params_table.set_row_spacing(MF_TABLE_ROW_SPACING);
  _params_table.set_column_spacing(MF_TABLE_COLUMN_SPACING);
  _params_table.set_padding(MF_PANEL_PADDING);

  _tab.add_page(&_advanced_table, _("Advanced"));

  _advanced_table.set_column_count(3);
  _advanced_table.set_row_spacing(MF_TABLE_ROW_SPACING);
  _advanced_table.set_column_spacing(MF_TABLE_COLUMN_SPACING);
  _advanced_table.set_padding(MF_PANEL_PADDING);
  
  add(&_table, false, false);
  add(&_tab, true, true);
}


DbConnectPanel::~DbConnectPanel()
{
  if (_delete_connection_be)
    delete _connection;
}


void DbConnectPanel::set_skip_schema_name(bool flag)
{
  _skip_schema_name= flag;
}


void DbConnectPanel::suspend_view_layout(bool flag)
{
  if (flag)
    suspend_layout();
  else
    resume_layout();
}


void DbConnectPanel::init(DbConnection *conn, const db_mgmt_ConnectionRef &default_conn)
{
  _connection= conn;
  _delete_connection_be= false;
  
  _connection->set_control_callbacks(boost::bind(&DbConnectPanel::suspend_view_layout, this, _1),
                                     boost::bind(&DbConnectPanel::clear_param_controls, this),
                                     boost::bind(&DbConnectPanel::create_control, this, _1, _2, _3, _4));
  
  if (_show_connection_combo)
    refresh_stored_connections();
  
  _anonymous_connection= default_conn.is_valid() ? default_conn : db_mgmt_ConnectionRef(_connection->get_grt());

  grt::ListRef<db_mgmt_Rdbms> rdbms(_connection->get_rdbms_list());
  _rdbms_sel.clear();
  for (grt::ListRef<db_mgmt_Rdbms>::const_iterator iter= rdbms.begin();
       iter != rdbms.end(); ++iter)
    _rdbms_sel.add_item((*iter)->caption());
  _rdbms_sel.set_selected(-1);

  
  int index= _connection->get_default_rdbms_index();
  set_active_rdbms(index, -1);
  _rdbms_sel.set_selected(index);
  
  if (default_conn.is_valid())
    _connection->set_connection(_anonymous_connection);
  else
    _connection->set_connection_keeping_parameters(_anonymous_connection);

  _initialized= true;
}


void DbConnectPanel::init(const db_mgmt_ManagementRef &mgmt, const db_mgmt_ConnectionRef &default_conn)
{  
  if (!mgmt.is_valid())
    throw std::invalid_argument("DbConnectPanel::init() called with invalid db mgmt object");

  DbConnection *conn= new DbConnection();
  
  conn->init(mgmt, _skip_schema_name);

  init(conn, default_conn);
  _delete_connection_be= true;
}


db_mgmt_ConnectionRef DbConnectPanel::get_connection()
{
  return _connection->get_connection();
}

void DbConnectPanel::set_connection(const db_mgmt_ConnectionRef& conn)
{
  const grt::ListRef<db_mgmt_Connection> list(_connection->get_db_mgmt()->storedConns());

  int count = 0;
  grt::ListRef<db_mgmt_Connection>::const_iterator iter = list.begin();
  grt::StringRef conn_host = conn->hostIdentifier();
  for (;iter != list.end(); ++iter)
  {
    if (conn == (*iter))
    {
      _stored_connection_sel.set_selected(count + 1);
      change_active_stored_conn();
      break;
    }

    ++count;
  }
}

void DbConnectPanel::set_enabled(bool flag)
{
  _name_entry.set_enabled(flag);
  _stored_connection_sel.set_enabled(flag);
  _rdbms_sel.set_enabled(flag);
  _driver_sel.set_enabled(flag);
  
  for (std::list<mforms::View*>::const_iterator iter= _views.begin();
       iter != _views.end(); ++iter)
    (*iter)->set_enabled(flag);
}


void DbConnectPanel::set_default_host_name(const std::string &host, bool update)
{
  _default_host_name= host;
   /*
  if (update)
  {
    for (std::map<std::string, db_mgmt_ConnectionRef>::iterator iter= _anonymous_connections.begin();
         iter != _anonymous_connections.end(); ++iter)
    {
      if (is_ssh_driver(iter->first))
        iter->second->parameterValues().gset("sshHost", _default_host_name);
      else
        iter->second->parameterValues().gset("hostName", _default_host_name);
    }
    
    // force UI update
    change_active_driver();
  }*/
}


void DbConnectPanel::param_value_changed(mforms::View *sender)
{
  std::string param_name= sender->get_name();
  
  if (_show_connection_combo && !_updating)
  {
    // if stored connections combo is shown, copy the current connection params to the
    // to anonymous connection and select it
    // since stored connections are not editable in this case
    _connection->set_connection_keeping_parameters(_anonymous_connection);
    if (_stored_connection_sel.get_selected_index() != 0)
      _stored_connection_sel.set_selected(0);
  }

  DbDriverParam *param= _connection->get_db_driver_param_handles()->get(param_name);

  param->set_value(grt::StringRef(sender->get_string_value()));
  
  _connection->save_changes();
  
  std::string error= _connection->validate_driver_params();
  if (error != _last_validation)
    _signal_validation_state_changed(error, error.empty());
  _last_validation= error;
}


void DbConnectPanel::change_active_rdbms()
{
  if (_initialized && !_updating)
  {
    set_active_rdbms(_rdbms_sel.get_selected_index(), -1);
    
    if (_show_connection_combo)
    {
      // if stored connections combo is shown, copy the current connection params to the
      // to anonymous connection and select it
      // since stored connections are not editable in this case
      _connection->set_connection_keeping_parameters(_anonymous_connection);
      if (_stored_connection_sel.get_selected_index() != 0)
        _stored_connection_sel.set_selected(0);
    }    
  }
}


void DbConnectPanel::set_active_rdbms(int rdbms_index, int driver_index)
{  
  if (_rdbms_index != rdbms_index)
  {
    _connection->set_active_rdbms(rdbms_index);

    grt::ListRef<db_mgmt_Driver> drivers(_connection->get_driver_list());

    _driver_sel.clear();
    for (grt::ListRef<db_mgmt_Driver>::const_iterator iter= drivers.begin();
         iter != drivers.end(); ++iter)
    {
      // only the default driver is supported ATM
      if ((*iter)->driverLibraryName() == "mysqlcppconn")
        _driver_sel.add_item((*iter)->caption());
    }
    _driver_sel.set_selected(-1);

    _rdbms_index= rdbms_index;
  }

  if (driver_index == -1)
    driver_index= _connection->get_rdbms_default_driver_index();
  set_active_driver(driver_index);
}


void DbConnectPanel::change_active_driver()
{
  if (_initialized && !_updating)
  {
    db_mgmt_ConnectionRef conn(_connection->get_connection());
    grt::DictRef current_params(conn->parameterValues());
    // save current driver params
    for (grt::DictRef::const_iterator iter = current_params.begin(); iter != current_params.end(); ++iter)
      _parameters_per_driver[conn->driver()->name()]= grt::DictRef::cast_from(grt::copy_value(current_params, false));

    db_mgmt_DriverRef new_driver(_connection->get_rdbms()->drivers()[_driver_sel.get_selected_index()]);
    // update params to driver-specific params
    if (_parameters_per_driver.find(new_driver->name()) == _parameters_per_driver.end())
    {
      grt::DictRef params(grt::DictRef::cast_from(grt::copy_value(current_params, false)));
      
      if (!_default_host_name.empty())
      {
        if (is_ssh_driver(new_driver->name()))
          params.gset("sshHost", _default_host_name);
        else
          params.gset("hostName", _default_host_name);
      }

      _parameters_per_driver[new_driver->name()]= params;
    }
    grt::replace_contents(conn->parameterValues(), _parameters_per_driver[new_driver->name()]);

    set_active_driver(_driver_sel.get_selected_index());
    
    if (_show_connection_combo)
    {
      // if stored connections combo is shown, copy the current connection params to the
      // to anonymous connection and select it
      // since stored connections are not editable in this case
      _connection->set_connection_keeping_parameters(_anonymous_connection);
      if (_stored_connection_sel.get_selected_index() != 0)
        _stored_connection_sel.set_selected(0);
    }    
  }
}


void DbConnectPanel::set_active_driver(int driver_index)
{
  // backend will update the parameter UI
  show(false);
  _connection->set_active_driver(driver_index);

  _driver_index= driver_index;
  if (_driver_sel.get_selected_index() != driver_index)
    _driver_sel.set_selected(driver_index);

  // we update the validation msg
  _last_validation= _connection->validate_driver_params();
  // notify the frontend that the state has changed but don't show any error
  // even if there is one
  _signal_validation_state_changed("", _last_validation.empty());
  show();
}


void DbConnectPanel::refresh_stored_connections()
{
  grt::ListRef<db_mgmt_Connection> list(_connection->get_db_mgmt()->storedConns());

  _stored_connection_sel.clear();
  _stored_connection_sel.add_item("");
  for (grt::ListRef<db_mgmt_Connection>::const_iterator iter= list.begin(); iter != list.end(); ++iter)
  {
    _stored_connection_sel.add_item((*iter)->name());
  }
  
  _stored_connection_sel.add_item("-");
  _stored_connection_sel.add_item(_("Manage Stored Connections..."));
  
  if (_stored_connection_sel.get_selected_index() != 0)
    _stored_connection_sel.set_selected(0);
}




void DbConnectPanel::get_connection_details(int &rdbms_index, int &driver_index)
{  
  db_mgmt_DriverRef driver= _connection->get_connection()->driver();
  if (driver.is_valid())
  {
    rdbms_index= (int)find_object_index_in_list(_connection->get_rdbms_list(), driver->owner().id());
    driver_index= (int)find_object_index_in_list(db_mgmt_RdbmsRef::cast_from(driver->owner())->drivers(), driver.id());
  }
  else
  {
    rdbms_index= -1;
    driver_index= -1;
  }
}


bool DbConnectPanel::test_connection()
{
  try
  {
    sql::DriverManager *dbc_drv_man= sql::DriverManager::getDriverManager();
    sql::ConnectionWrapper _dbc_conn= dbc_drv_man->getConnection(get_be()->get_connection());
    
    if (_dbc_conn.get() && !_dbc_conn->isClosed())
    {
      mforms::Utilities::show_message(base::strfmt("Connected to %s", bec::get_description_for_connection(get_be()->get_connection()).c_str()),
                                      "Connection parameters are correct.", "OK");
      return true;
    }
    else
      mforms::Utilities::show_error(base::strfmt("Failed to Connect to %s", bec::get_description_for_connection(get_be()->get_connection()).c_str()),
                                    "Connection Failed", "OK");
  }
  catch (const std::exception& e)
  {
    mforms::Utilities::show_error(base::strfmt("Failed to Connect to %s", bec::get_description_for_connection(get_be()->get_connection()).c_str()),
                                  e.what(), "OK");
  }
  return false;
}


void DbConnectPanel::set_active_stored_conn(int stored_conn_index)
{
  int rdbms_index;
  int driver_index;
  
  grt::ListRef<db_mgmt_Connection> conns(_connection->get_db_mgmt()->storedConns());
  if (stored_conn_index <= 0 || stored_conn_index > (int) conns.count())
    _connection->set_connection(_anonymous_connection);
  else
    _connection->set_connection(conns[stored_conn_index-1]);
  
  get_connection_details(rdbms_index, driver_index);

  if (rdbms_index == -1)
    rdbms_index= _rdbms_sel.get_selected_index();
  if (driver_index == -1)
    driver_index= _driver_sel.get_selected_index();

  set_active_rdbms(rdbms_index, driver_index);
  
  if (!_show_connection_combo)
    _name_entry.set_value(_connection->get_connection()->name());
}


void DbConnectPanel::change_active_stored_conn()
{
  static bool choosing = false;
  show(false);
  if (_initialized && !choosing)
  {
    _updating= true;

    if (_stored_connection_sel.get_selected_index() == _stored_connection_sel.get_item_count()-1)
    {   
      choosing = true;
      int index= open_editor();
      refresh_stored_connections();
      _stored_connection_sel.set_selected(index);
      set_active_stored_conn(index);
      choosing = false;
    }
    else
      set_active_stored_conn(_stored_connection_sel.get_selected_index());
    
    _updating= false;
  }
  show();
}


int DbConnectPanel::open_editor()
{
  DbConnectionEditor editor(_connection->get_db_mgmt());
  db_mgmt_ConnectionRef selected;

  selected= editor.run(_connection->get_connection());

  if (selected.is_valid())
  {
    grt::ListRef<db_mgmt_Connection> conns(_connection->get_db_mgmt()->storedConns());
    
    size_t index= conns.get_index(selected);
    if (index != grt::BaseListRef::npos)
      return index+1;
  }
  return 0;
}


void DbConnectPanel::clear_param_controls()
{
  for (std::list<View*>::reverse_iterator iter= _views.rbegin();
       iter != _views.rend(); ++iter)
  {
    Box *box= dynamic_cast<Box*> ((*iter)->get_parent());
    if (box)
      box->remove(*iter);
    else
      dynamic_cast<Table*> ((*iter)->get_parent())->remove(*iter);
    (*iter)->release();
  }
  _views.clear();
  _param_rows.clear();
  _advanced_rows.clear();
}


void DbConnectPanel::set_keychain_password(DbDriverParam *param, bool clear)
{
  std::string storage_key;
  std::string username;
  grt::DictRef paramValues(get_connection()->parameterValues());
  std::vector<std::string> tokens = base::split(param->get_value_repr(), "::");
  
  if (tokens.size() == 2)
  {
    username = tokens[0];
    storage_key = tokens[1];
  }
  else
    return;
  
  for (grt::DictRef::const_iterator iter = paramValues.begin(); iter != paramValues.end(); ++iter)
  {
    storage_key = bec::replace_string(storage_key, "%"+iter->first+"%", iter->second.repr());
    username = bec::replace_string(username, "%"+iter->first+"%", iter->second.repr());
  }

  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 Connection", 
                                              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");
    }
  }
}


void DbConnectPanel::create_control(::DbDriverParam *driver_param, const ::ControlType ctrl_type, 
                                    const ControlBounds& bounds, const std::string &caption)
{
  bool is_new_line= false;
  Table *table= NULL;
  Box *box= NULL;

  switch (driver_param->object()->layoutAdvanced())
  {
  case 0:
    {
      if (bounds.top >= (int)_param_rows.size())
      {
        is_new_line= true;

        _params_table.set_row_count(_param_rows.size() + 1);
        _param_rows.push_back(box= new Box(true));

        mforms::TableItemFlags flags;
#ifdef __APPLE__ // HExpand is messing layout in mac
        flags = mforms::HFillFlag;
#else
        flags = mforms::HExpandFlag | mforms::HFillFlag;
#endif
        _params_table.add(mforms::manage(box), 1, 2, bounds.top, bounds.top + 1, flags);
        box->set_spacing(4);
        _views.push_back(box);
      }
      else
        box= _param_rows[bounds.top];

      table= &_params_table;
      break;
    }
  case 1:
    {
      if (bounds.top >= (int)_advanced_rows.size())
      {
        is_new_line= true;

        _advanced_table.set_row_count(_advanced_rows.size() + 1);
        if (ctrl_type == ::ctCheckBox)
        {
          _advanced_rows.push_back(box= new Box(false));
          box->set_spacing(0);
        }
        else
        {
          _advanced_rows.push_back(box= new Box(true));
          box->set_spacing(4);
        }
        _views.push_back(box);
#ifdef __APPLE__ // TODO: HExpand is messing layout in mac, needs fix
        _advanced_table.add(mforms::manage(box), 1, 2, bounds.top, bounds.top + 1, mforms::HFillFlag);
#else
        _advanced_table.add(mforms::manage(box), 1, 2, bounds.top, bounds.top + 1, mforms::HExpandFlag|mforms::HFillFlag);
#endif
      }
      else
        box= _advanced_rows[bounds.top];

      table= &_advanced_table;
      break;
    }
  default:
    return;
  }

  switch (ctrl_type)
  {
  case ::ctLabel:
    {
      Label *label= new Label();
      label->set_text(caption);
      label->set_text_align(mforms::MiddleRight);

      if (is_new_line)
        table->add(mforms::manage(label), 0, 1, bounds.top, bounds.top + 1, mforms::HFillFlag | mforms::VFillFlag);
      else
        box->add(mforms::manage(label), false, true);
      _views.push_back(label);
      break;
    }
  case ::ctDescriptionLabel:
    {
      Label *label= new Label();
      label->set_text(caption);
      label->set_style(mforms::SmallHelpTextStyle);
      table->add(mforms::manage(label), 2, 3, bounds.top, bounds.top + 1, mforms::HFillFlag | mforms::VFillFlag);
      _views.push_back(label);
      break;
    }
  case ::ctCheckBox:
    {
      CheckBox *ctrl= new CheckBox();

      ctrl->set_name(driver_param->get_control_name());

      ctrl->set_text(caption);

      // value
      {
        grt::StringRef value= driver_param->get_value_repr();
        if (value.is_valid())
          ctrl->set_active(*value != "" && *value != "0" && *value != "NULL");
      }

      scoped_connect(ctrl->signal_clicked(),boost::bind(&DbConnectPanel::param_value_changed, this, ctrl));

      box->add(mforms::manage(ctrl), false, true);
      _views.push_back(ctrl);

      break;
    }
  case ::ctKeychainPassword:
    {
      Button *btn = new Button();
      
#ifdef _WIN32
      btn->set_text("Store in Vault ...");
      btn->set_tooltip(_("Store the password for this connection in a secured vault"));
#else
      btn->set_text("Store in Keychain ...");
      btn->set_tooltip(_("Store the password for this connection in the system's keychain"));
#endif
      
      box->add(mforms::manage(btn), false, true);
      _views.push_back(btn);
      scoped_connect(btn->signal_clicked(),boost::bind(&DbConnectPanel::set_keychain_password, this, driver_param, false));

      btn = new Button();
      btn->set_text("Clear");
      btn->set_size(100, -1);
#ifdef _WIN32
      btn->set_tooltip(_("Remove the previously stored password from the secured vault"));
#else
      btn->set_tooltip(_("Remove the previously stored password from the system's keychain"));
#endif
      box->add(mforms::manage(btn), false, true);
      _views.push_back(btn);
      scoped_connect(btn->signal_clicked(),boost::bind(&DbConnectPanel::set_keychain_password, this, driver_param, true));

      break;
    }
  case ::ctTextBox:
    {
      bool is_password = ::DbDriverParam::ptPassword == driver_param->get_type();
      TextEntry *ctrl= new TextEntry(is_password ? PasswordEntry : NormalEntry);

      ctrl->set_name(driver_param->get_control_name());
      
      // value
      {
        grt::StringRef value= driver_param->get_value_repr();
        if (value.is_valid())
          ctrl->set_value(*value);
      }

      ctrl->set_size(bounds.width, -1);

      scoped_connect(ctrl->signal_changed(),boost::bind(&DbConnectPanel::param_value_changed, this, ctrl));

      box->add(mforms::manage(ctrl), true, true);
      _views.push_back(ctrl);
      
      break;
    }
    case ::ctFileSelector:
    {
      FsObjectSelector *ctrl= new FsObjectSelector();
      ctrl->set_name(driver_param->get_control_name());
      
      // value
      grt::StringRef value= driver_param->get_value_repr();
      std::string initial_value= "";
      if (value.is_valid())
        initial_value= *value;
      
      ctrl->initialize(initial_value, mforms::OpenFile, "", "...", true,
        boost::bind(&DbConnectPanel::param_value_changed, this, ctrl));
      box->add(mforms::manage(ctrl), true, true);
      _views.push_back(ctrl);
      
      break;
    }
    default:
      g_message("Unknown param type for %s", driver_param->get_control_name().c_str());
      break;
  }
}

