/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include "stdafx.h"

#include "grtdb_connect_panel.h"
#include "grtdb_connection_editor.h"

#include <grt/common.h>

using namespace grtui;
using namespace mforms;

DbConnectPanel::DbConnectPanel(bool show_connection_combo)
  : Box(false), _connection(0), _show_connection_combo(show_connection_combo)
{
  _rdbms_index= -1;
  _driver_index= -1;
  _initialized= false;
  _updating= 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)
    _stored_connection_sel.signal_changed().connect(sigc::mem_fun(this, &DbConnectPanel::change_active_stored_conn));
  _rdbms_sel.signal_changed().connect(sigc::mem_fun(this, &DbConnectPanel::change_active_rdbms));
  _driver_sel.signal_changed().connect(sigc::mem_fun(this, &DbConnectPanel::change_active_driver));
  
  _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(&_label2, 0, 1, 1, 2, mforms::HFillFlag);
//  _table.add(&_rdbms_sel, 1, 2, 1, 2, mforms::HExpandFlag | mforms::HFillFlag);
//  _table.add(&_desc2, 2, 3, 1, 2, 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(2);
  _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(2);
  _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::init(DbConnection *conn)
{
  _connection= conn;
  _delete_connection_be= false;
  
  _connection->set_control_callbacks(sigc::mem_fun(this, &DbConnectPanel::clear_param_controls),
                                     sigc::mem_fun(this, &DbConnectPanel::create_control));
  
  if (_show_connection_combo)
    refresh_stored_connections();
  
  _anonymous_connection= 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);
  
  _connection->set_connection_keeping_parameters(_anonymous_connection);

  _initialized= true;
}


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

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

  init(conn);
  _delete_connection_be= true;
}


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= _controls.begin();
       iter != _controls.end(); ++iter)
    (*iter)->set_enabled(flag);
}



void DbConnectPanel::param_value_changed(mforms::Control *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.emit(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)
  {
    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);
  show();

  _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.emit("", _last_validation.empty());
}


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;
  }
}



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());
  }
  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= _controls.rbegin();
       iter != _controls.rend(); ++iter)
  {
    Box *box= dynamic_cast<Box*> ((*iter)->get_parent());
    if (box)
      box->remove(*iter);
    else
      dynamic_cast<Table*> ((*iter)->get_parent())->remove(*iter);
  
    delete *iter;
  }
  _controls.clear();
  _param_rows.clear();
  _advanced_rows.clear();
}


void DbConnectPanel::create_control(::DbDriverParam *driver_param, const ::ControlType ctrl_type, 
                                    const ::Position &pos, const ::Size &size, const std::string &caption)
{
  Table *table= 0;
  Box *box= 0;

  switch (driver_param->object()->layoutAdvanced())
  {
  case 0:
    {
      if (pos.y >= (int)_param_rows.size())
      {
        _params_table.set_row_count(_param_rows.size()+1);
        _param_rows.push_back(box= new Box(true));
        _params_table.add(box, 1, 2, pos.y, pos.y+1, mforms::HExpandFlag|mforms::HFillFlag);
        box->set_spacing(4);
        table= &_params_table;
        _controls.push_back(box);
      }
      else
        box= _param_rows[pos.y];
      break;
    }
  case 1:
    {
      if (pos.y >= (int)_advanced_rows.size())
      {
        _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);
        }
        _controls.push_back(box);
      
        table= &_advanced_table;
        _advanced_table.add(box, 1, 2, pos.y, pos.y+1, mforms::HExpandFlag|mforms::HFillFlag);
      }
      else
        box= _advanced_rows[pos.y];
      break;
    }
  default:
    return;
  }

  switch (ctrl_type)
  {
  case ::ctLabel:
  case ::ctDescriptionLabel:
    {
      Label *ctrl= new Label();

      ctrl->set_text(caption);
      if (ctrl_type == ::ctDescriptionLabel)
        ctrl->set_style(mforms::SmallHelpTextStyle);
      else
        ctrl->set_text_align(mforms::MiddleRight);

      if (table)
        table->add(ctrl, 0, 1, pos.y, pos.y+1, 0);
      else
        box->add(ctrl, false, true);
      _controls.push_back(ctrl);
      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");
      }

      ctrl->signal_clicked().connect(sigc::bind(sigc::mem_fun(this, &DbConnectPanel::param_value_changed), ctrl));

      box->add(ctrl, false, true);
      _controls.push_back(ctrl);

      break;
    }
  case ::ctTextBox:
    {
      TextEntry *ctrl= new TextEntry(::DbDriverParam::ptPassword == driver_param->get_type());
      //const db_mgmt_DriverParameterRef &param= driver_param->object();

      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);
      }

      // filter out inappropriate characters for params of int type
      //if (::Db_driver_param::ptInt == driver_param->get_type())
      //  ctrl->KeyPress+= _handle_int_param_key_press;

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

      ctrl->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &DbConnectPanel::param_value_changed), ctrl));

      box->add(ctrl, false, true);
      _controls.push_back(ctrl);
      
      break;
    }      
   default:
      g_message("Unknown param type for %s", driver_param->get_control_name().c_str());
      break;
  }
}
