#include "gtk_helpers.h"
#include "image_cache.h"

#include "config.h"

#include <gtkmm/image.h>
#include <gtkmm/filechooserdialog.h>
#include <gtkmm/stock.h>
#include <gtkmm/combobox.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/comboboxentrytext.h>
#include "text_list_columns_model.h"

#ifdef HAVE_LIBGNOME
#include <libgnome/libgnome.h>
#endif

#include "treemodel_wrapper.h"

// This list_model is used for all functions which operate on GTKListStore
static TextListColumnsModel _wb_list_model;

//------------------------------------------------------------------------------
Gtk::HBox &create_icon_label(const std::string &icon, const std::string &text)
{
  Gtk::HBox *hbox= Gtk::manage(new Gtk::HBox(false, 0));
  
  Gtk::Image *image= Gtk::manage(new Gtk::Image(ImageCache::get_instance()->image_from_filename(icon)));
  Gtk::Label *label= Gtk::manage(new Gtk::Label(text));
  
  label->set_use_markup(true);
  
  hbox->pack_start(*image);
  hbox->pack_start(*label, true, true);
  
  hbox->show_all();
  
  return *hbox;
}

//------------------------------------------------------------------------------
void open_url(const std::string &url)
{
#ifdef HAVE_LIBGNOME
  gnome_url_show(url.c_str(), NULL);
#else
  g_message("Open URL %s (no url handler)", url.c_str());
#endif
}

//------------------------------------------------------------------------------
Glib::RefPtr<Gtk::ListStore> model_from_string_list(const std::vector<std::string>& list, TextListColumnsModel* columns)
{
  Glib::RefPtr<Gtk::ListStore> model = Gtk::ListStore::create(*columns);
  
  std::vector<std::string>::const_iterator last = list.end();
  
  for (std::vector<std::string>::const_iterator iter = list.begin(); iter != last; ++iter)
    (*model->append())[columns->item] = *iter;
  
  return model;
}

//------------------------------------------------------------------------------
Glib::RefPtr<Gtk::ListStore> model_from_string_list(const std::vector<std::string>& list, TextListColumnsModel** columns)
{
  if ( columns )
    *columns = &_wb_list_model;

  return model_from_string_list(list, &_wb_list_model);
}

//------------------------------------------------------------------------------
void recreate_model_from_string_list(Glib::RefPtr<Gtk::ListStore> model, const std::vector<std::string>& list)
{
  model->clear();
  
  std::vector<std::string>::const_iterator last = list.end();
  
  for (std::vector<std::string>::const_iterator iter = list.begin(); iter != last; ++iter)
    (*model->append())[_wb_list_model.item] = *iter;
}


//------------------------------------------------------------------------------
void setup_combo_for_string_list(Gtk::ComboBox *combo)
{
  Gtk::CellRendererText *cell= Gtk::manage(new Gtk::CellRendererText());
  combo->pack_end(*cell, true);
  combo->add_attribute(*cell, "text", 0);
}

//------------------------------------------------------------------------------
void set_glib_string(Glib::ValueBase& value, const std::string& str)
{
  GValue *gval = value.gobj();

  g_value_init(gval, G_TYPE_STRING);
  g_value_set_string(gval, str.c_str());
}

//------------------------------------------------------------------------------
void set_glib_int(Glib::ValueBase& value, const int i)
{
  GValue *gval = value.gobj();

  g_value_init(gval, G_TYPE_INT);
  g_value_set_int(gval, i);
}

//------------------------------------------------------------------------------
void set_glib_bool(Glib::ValueBase& value, const bool b)
{
  GValue *gval = value.gobj();

  g_value_init(gval, G_TYPE_BOOLEAN);
  g_value_set_boolean(gval, b);
}

//------------------------------------------------------------------------------
void set_glib_double(Glib::ValueBase& value, const double d)
{
  GValue *gval = value.gobj();

  g_value_init(gval, G_TYPE_DOUBLE);
  g_value_set_double(gval, d);
}

//------------------------------------------------------------------------------
void fill_combo_from_string_list(Gtk::ComboBoxText* combo, const std::vector<std::string>& list)
{
  std::vector<std::string>::const_iterator it   = list.begin();
  std::vector<std::string>::const_iterator last = list.end();

  for (; last != it; ++it )
    combo->append_text(*it);
}

//------------------------------------------------------------------------------
void fill_combo_from_string_list(Gtk::ComboBoxEntryText* combo, const std::vector<std::string>& list)
{
  std::vector<std::string>::const_iterator it   = list.begin();
  std::vector<std::string>::const_iterator last = list.end();

  for (; last != it; ++it )
    combo->append_text(*it);
}

//------------------------------------------------------------------------------
static std::string file_chooser_impl(const bool is_for_save, const std::string &filter)
{
  std::string filename;
  Gtk::FileChooserDialog dialog("Please choose a file",
          is_for_save ? Gtk::FILE_CHOOSER_ACTION_SAVE : Gtk::FILE_CHOOSER_ACTION_OPEN);
  dialog.set_transient_for(*get_mainwindow());

  //Add response buttons the the dialog:
  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
  dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);

  Gtk::FileFilter filter_any;
  //filter_any.set_name("Any files");
  filter_any.add_pattern(filter);
  dialog.add_filter(filter_any);
  
  const int result = dialog.run();

  switch(result)
  {
    case(Gtk::RESPONSE_OK):
    {
      filename = dialog.get_filename();
      break;
    }
    default:
      break;
  }
  
  return filename;
}

//------------------------------------------------------------------------------
std::string open_file_chooser(const std::string &filter)
{
  return file_chooser_impl(false, filter); // false - is not for save
}

std::string save_file_chooser(const std::string &filter)
{
  return file_chooser_impl(true, filter); // true - is for save
}

//------------------------------------------------------------------------------
static void expand_node(bec::TreeModel* tm, const bec::NodeId& node, Gtk::TreeView* tv)
{
  // Expand the node itself if needed
  if (tm->is_expandable(node))
  {
    if (tm->is_expanded(node))
    {
      const int             node_depth = node.depth();
      Gtk::TreeModel::Path  path;

      for (int i = 0; i < node_depth; ++i)
          path.push_back(node[i]);
 
      tv->expand_row(path, true);
    }

    // Apply the same thing for all node's children
    const int children_count = tm->count_children(node);
    if ( children_count > 0 )
    {
      for (int i = 0; i < children_count; ++i)
      {
        bec::NodeId child = tm->get_child(node, i);
        expand_node(tm, child, tv);
      }
    }
  }
}

//------------------------------------------------------------------------------
void expand_tree_nodes_as_in_be(const Glib::RefPtr<TreeModelWrapper> &model, Gtk::TreeView *tv)
{
  model->block_expand_collapse_signals();
  //TODO: This vv will work once grt_value_tree.h will have correct support for storing expanded/collapsed states 
  //bec::TreeModel *tm = static_cast<bec::TreeModel*>(model->get_be_model());
  //expand_node(tm, tm->get_root(), tv);

  // The code below is a workaround while we do not have working support for store/retrive expanded state of the node
  ExpandedRowsStorage* rows = model->expanded_rows_storage();
  // As: The insert members shall not affect the validity of iterators and references 
  // to the container, and the erase members shall invalidate only iterators and 
  // references to the erased elements. We need to have temp vector of values of invalid rows
  std::vector<ExpandedRowsStorage::key_type> invalid_rows;
  if ( rows )
  {
    ExpandedRowsStorage::const_iterator       row  = rows->begin();
    const ExpandedRowsStorage::const_iterator last = rows->end();
    
    for (; last != row; ++row)
    {
      if (!tv->expand_row(Gtk::TreeModel::Path(*row), true))
        invalid_rows.push_back(*row);
    }
    
    std::vector<ExpandedRowsStorage::key_type>::const_iterator i_row  = invalid_rows.begin();
    std::vector<ExpandedRowsStorage::key_type>::const_iterator i_last = invalid_rows.end();
    for(; i_last != i_row; ++i_row)
      rows->erase(*i_row);
  }
  model->unblock_expand_collapse_signals();
}

//------------------------------------------------------------------------------
//std::string run_string_dialog(const std::string& title, const std::string& init_value)
//{
//  Gtk::Entry entry;
//  Gtk::Dialog dlg;
//  entry.set_text(init_value);
//  entry.show();
//  dlg.add_action_widget(entry, 0xff);
//  dlg.set_title(title);
//  dlg.set_position(Gtk::WIN_POS_MOUSE);
//  dlg.set_transient_for(*get_mainwindow());
//  const int result = dlg.run();
//
//  std::string ret = init_value;
//  switch (result)
//  {
//    case 0xff: // for the magic number 0xff see above add_action_widget
//      ret = entry.get_text();
//      break;
//    default: break;
//  }
//  
//  return ret;
//}
