#include "listmodel_wrapper.h"
#include "gtk_helpers.h"
#include <gtkmm/cellrenderercombo.h>
#include <gtkmm/icontheme.h>
#include "grt/common.h"
#include "base/string_utilities.h"

//#define DEBUG
#undef DEBUG

#ifdef DEBUG
#define lmwdprint(...) fprintf(stderr, __VA_ARGS__)
#else
#define lmwdprint(...)
#endif


static void process_menu_actions(const std::string &command
                                , bec::ListModel* model
                                , const std::vector<bec::NodeId>& nodes
                                , const sigc::slot<void, const std::string&, const std::vector<bec::NodeId>&> fe_menu_handler
                                )
{
  if (!model->activate_popup_item_for_nodes(command, nodes) && !fe_menu_handler.empty())
    fe_menu_handler(command, nodes);
}

//------------------------------------------------------------------------------
static void run_menu_and_forward_action(const bec::MenuItemList& items
                                        ,const int x
                                        ,const int y
                                        ,const int time
                                        ,bec::ListModel* model
                                        ,const std::vector<bec::NodeId>& nodes
                                        ,const sigc::slot<void, const std::string&, const std::vector<bec::NodeId>&> fe_menu_handler
                                        ,Gtk::Menu *menu)
{
  if (!items.empty())
    run_popup_menu(items
                   ,time
                   ,sigc::bind(sigc::ptr_fun(process_menu_actions), model, nodes, fe_menu_handler)
                   ,menu);
}



Index::ExternalMap Index::_ext_map;

//------------------------------------------------------------------------------
Index::Index(GtkTreeIter* it, const bec::NodeId& node)
      : _raw_data((char *)it)
      , _ext(0)
{
  //memset(_raw_data, 0xff, sizeof(*it));
  //*_raw_data = 0;
  reset_iter(it);

  const int depth = node.depth();

  const Mode m = (depth < MaxDepth) ? (depth == 1 ? ListNode : Internal) : External;
  mode(m);

  if (m == External)
  {
    const std::string nrepr = node.repr();
    std::pair<ExternalMap::iterator, bool> res = _ext_map.insert(nrepr);
    _ext = const_cast<std::string*>(&(*(res.first)));
    it->user_data = (void*)_ext;
  }
  else if (m == Internal)
  {
    for (int i = 0; i < depth; ++i)
      word(i, node[i]);
  }
  else if (m == ListNode)
  {
    it->user_data = (void*)(intptr_t)(node[0]);
  }
}

//------------------------------------------------------------------------------
Index::Index(const GtkTreeIter* it)
      : _raw_data((char *)it)
      , _ext(0)
{
  if (mode() == External)
    _ext = reinterpret_cast<std::string*>(((GtkTreeIter*)_raw_data)->user_data);
}

//------------------------------------------------------------------------------
void Index::stamp(const int st)
{
  ((Info*)_raw_data)->stamp = st % StampMax;
}

//------------------------------------------------------------------------------
int Index::stamp() const
{
  return ((Info*)_raw_data)->stamp;
}

//------------------------------------------------------------------------------
bool Index::cmp_stamp(const int st) const
{
  return ((Info*)_raw_data)->stamp == (st % StampMax);
}

//------------------------------------------------------------------------------
int Index::word(const int w) const
{
  int ret = 0;

  if (mode() == Internal)
  {
    char* dest = (char*)&ret;
    const int start_byte = (w * K) + 1;

    memcpy(dest, _raw_data + start_byte, K);
  }

  return ret;
}

//------------------------------------------------------------------------------
void Index::word(const int w, const int v)
{
  if (mode() == Internal)
  {
    int start_byte = (w * K) + 1;
    char* dest = _raw_data + start_byte;
    char* src = (char*)&v;

    memcpy(dest, src, K);
  }
  else
  {
    throw std::logic_error("Can't change external Node ref\n");
  }
}

//------------------------------------------------------------------------------
bec::NodeId Index::to_node() const
{
  bec::NodeId node;
  if (mode() == Internal)
  {
    int v;
    char* dst = (char*)&v;
    char* src = _raw_data + 1;

    for (int i = 0; i < MaxDepth; ++i)
    {
      v = MaxValue;
      memcpy(dst, src, K);
      src += K;
      if (v != MaxValue)
        node.append(v);
      else
        break;
    }
  }
  else if (mode() == External)
  {
    if (_ext)
      node = bec::NodeId(*_ext);
  }
  else if (mode() == ListNode)
    node.append((long)(((GtkTreeIter*)_raw_data)->user_data));

  return node;
}

//------------------------------------------------------------------------------
void Index::reset_iter(GtkTreeIter* it)
{
  memset(it, 0xff, sizeof(*it));
  *((char*)it) = 0;
}



//------------------------------------------------------------------------------
Gtk::TreeModel::Path node2path(const ::bec::NodeId& node)
{
  const int depth = node.depth();
  Gtk::TreeModel::Path path;

  for ( int i = 0; i < depth; i++ )
    path.append_index(node[i]);

  return path;
}

//------------------------------------------------------------------------------
ColumnsModel::~ColumnsModel()
{
  reset(true); // true means clean up only self, and do not touch tree view
}

//------------------------------------------------------------------------------
void ColumnsModel::reset(const bool cleanup_only_self)
{
  if (!cleanup_only_self)
    _treeview->remove_all_columns();

  std::list<Gtk::TreeModelColumnBase*>::iterator         it = _columns.begin();
  std::list<Gtk::TreeModelColumnBase*>::const_iterator last = _columns.end();

  for ( ; last != it; ++it )
    delete *it;

  _columns.clear();
}

//------------------------------------------------------------------------------
void ColumnsModel::add_bec_index_mapping(const int bec_tm_index)
{
  _ui2bec.push_back(bec_tm_index);
}

//------------------------------------------------------------------------------
int ColumnsModel::ui2bec(const int index_of_ui_column) const
{
  g_assert((size_t)index_of_ui_column < _ui2bec.size());

  return _ui2bec[index_of_ui_column];
}

//------------------------------------------------------------------------------
const StringColumn&
ColumnsModel::set_text_column(const int          bec_tm_idx
                                ,const bool         editable
                                ,Gtk::IconView     *iv
                                )
{
  // Create columns
  // add mapping from ui to bec
  // add them with add() to the model
  // add columns to the IconView
  // check if editable and bind signals

  Gtk::TreeModelColumn<Glib::ustring> *text = new Gtk::TreeModelColumn<Glib::ustring>;
  Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > *icon = new Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> >;

  _columns.push_back(text);
  _columns.push_back(icon);

  add_bec_index_mapping(bec_tm_idx);
  add_bec_index_mapping(bec_tm_idx);

  add(*text);
  add(*icon);

  iv->set_text_column(*text);
  iv->set_pixbuf_column(*icon);

  return *text;
}


//------------------------------------------------------------------------------
void ColumnsModel::add_tooltip_column(int bec_tm_idx)
{
  Gtk::TreeModelColumn<Glib::ustring> *col = new Gtk::TreeModelColumn<Glib::ustring>;
  add(*col);
  add_bec_index_mapping(-bec_tm_idx); // negative means pick description

  _columns.push_back(col);
}

//------------------------------------------------------------------------------
const IntColumn&
ColumnsModel::append_int_column(const int bec_tm_idx, const std::string& name, const Editable editable)
{
  Gtk::TreeModelColumn<int> *col = new Gtk::TreeModelColumn<int>;

  add(*col);
  add_bec_index_mapping(bec_tm_idx);

  int nr_of_cols;
  if ( editable == EDITABLE )
  {
    nr_of_cols= _treeview->append_column_editable(bec::replace_string(name, "_", "__"), *col);

    Gtk::CellRendererText *cell = (Gtk::CellRendererText*)(_treeview->get_column_cell_renderer(nr_of_cols - 1));
    cell->signal_edited().connect(sigc::bind
                                  (sigc::mem_fun(*_tmw
                                                ,&ListModelWrapper::after_cell_edit<int>
                                                )
                                                , sigc::ref(*col)
                                  )
                                 );
  }
  else
    nr_of_cols= _treeview->append_column(bec::replace_string(name, "_", "__"), *col);

  _treeview->get_column(nr_of_cols-1)->set_resizable(true);

  _columns.push_back(col);

  return *col;
}

//------------------------------------------------------------------------------
const DoubleColumn&
ColumnsModel::append_double_column(const int bec_tm_idx, const std::string& name, const Editable editable)
{
  Gtk::TreeModelColumn<double> *col = new Gtk::TreeModelColumn<double>;
  add_bec_index_mapping(bec_tm_idx);
  add(*col);

  _columns.push_back(col);

  return *col;
}

//------------------------------------------------------------------------------
void ColumnsModel::disable_edit_first_row(Gtk::CellRenderer* cell, const Gtk::TreeIter& iter)
{
  Gtk::CellRendererText *txt = (Gtk::CellRendererText *)cell;
  if(txt)
  {
    Gtk::TreeModel::Path path = this->_treeview->get_model()->get_path(iter);
    if (path[0] == 0)
      txt->property_editable() = false;
    else
      txt->property_editable() = true;
  }



}

const StringColumn&
ColumnsModel::append_string_column(const int bec_tm_idx, const std::string& name, const Editable editable, const Iconic have_icon)
{
  Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > *icon= 0;

  Gtk::TreeViewColumn *column= Gtk::manage(new Gtk::TreeViewColumn(bec::replace_string(name, "_", "__")));

  if ( have_icon == WITH_ICON )
  {
    icon = new Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> >;
    add(*icon);
    add_bec_index_mapping(bec_tm_idx);
    column->pack_start(*icon, false);

    _columns.push_back(icon);
  }

  Gtk::TreeModelColumn<Glib::ustring> *col = new Gtk::TreeModelColumn<Glib::ustring>;
  add(*col);
  add_bec_index_mapping(bec_tm_idx);
  column->pack_start(*col);

  _columns.push_back(col);

  int nr_of_cols= _treeview->append_column(*column);
  _treeview->get_column(nr_of_cols-1)->set_resizable(true);

  if (editable == EDITABLE || editable == EDITABLE_WO_FIRST)
  {

    std::vector<Gtk::CellRenderer*> rends= column->get_cell_renderers();

    Gtk::CellRendererText *cell = (Gtk::CellRendererText*)rends[icon ? 1 : 0];
    cell->property_editable()= true;
    cell->signal_edited().connect(sigc::bind
                                  (sigc::mem_fun(*_tmw
                                                ,&ListModelWrapper::after_cell_edit<Glib::ustring>
                                                )
                                                , sigc::ref(*col)
                                  )
                                 );

    if (editable == EDITABLE_WO_FIRST)
      column->set_cell_data_func(*cell, sigc::mem_fun(this, &ColumnsModel::disable_edit_first_row));
  }

  return *col;
}

//------------------------------------------------------------------------------
const StringColumn&
ColumnsModel::append_markup_column(const int bec_tm_idx, const std::string& name, const Iconic have_icon)
{
  Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > *icon= 0;

  Gtk::TreeViewColumn *column= Gtk::manage(new Gtk::TreeViewColumn(bec::replace_string(name, "_", "__")));

  if ( have_icon == WITH_ICON )
  {
    icon = new Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> >;
    add(*icon);
    add_bec_index_mapping(bec_tm_idx);
    column->pack_start(*icon, false);

    _columns.push_back(icon);
  }

  Gtk::TreeModelColumn<Glib::ustring> *col = new Gtk::TreeModelColumn<Glib::ustring>;
  Gtk::CellRendererText *cell = Gtk::manage(new Gtk::CellRendererText());
  add(*col);
  add_bec_index_mapping(bec_tm_idx);
  column->pack_start(*cell);
  column->add_attribute(cell->property_markup(), *col);

  _columns.push_back(col);

  int nr_of_cols= _treeview->append_column(*column);
  _treeview->get_column(nr_of_cols-1)->set_resizable(true);

  return *col;
}


//------------------------------------------------------------------------------
const StringColumn&
ColumnsModel::append_combo_column(const int bec_tm_idx
                                     ,const std::string            &name
                                     ,Glib::RefPtr<Gtk::ListStore>  list_w
                                     ,const Editable                editable
                                     ,bool popup_only)
{
  Gtk::TreeModelColumn<Glib::ustring> *choosen = new Gtk::TreeModelColumn<Glib::ustring>;
  _columns.push_back(choosen);
  add(*choosen);
  add_bec_index_mapping(bec_tm_idx);

  Gtk::TreeView::Column   *col = Gtk::manage(new Gtk::TreeViewColumn(bec::replace_string(name, "_", "__")));
  Gtk::CellRendererCombo *cell = Gtk::manage(new Gtk::CellRendererCombo);
  col->pack_start(*cell);

  col->add_attribute(cell->property_text(), *choosen);
  cell->property_model() = list_w;
  cell->property_text_column() = 0;
  cell->property_editable() = editable;
  cell->property_has_entry() = !popup_only;
  
  Gtk::TreeModelColumn<Glib::RefPtr<Gtk::TreeModel> > *model_col = new Gtk::TreeModelColumn<Glib::RefPtr<Gtk::TreeModel> >();
  add_bec_index_mapping(bec_tm_idx);
  add(*model_col);
  const int nr_of_cols = _treeview->append_column(*col);

  _columns.push_back(model_col);

  _treeview->get_column(nr_of_cols-1)->set_resizable(true);

  if ( editable == EDITABLE )
  {
    Gtk::CellRendererText *cell = (Gtk::CellRendererText*)(_treeview->get_column_cell_renderer(nr_of_cols - 1));
    cell->signal_edited().connect(sigc::bind
                                    (sigc::mem_fun(*_tmw
                                                  ,&ListModelWrapper::after_cell_edit<Glib::ustring>
                                                  )
                                                  , sigc::ref(*choosen)
                                   )
                                 );
  }

  return *choosen;
}

//------------------------------------------------------------------------------
int
ColumnsModel::append_check_column(const int bec_tm_idx
                                 ,const std::string         &name
                                 ,const Editable             editable
                                 ,const ToggleAction         action
                                 )
{
  Gtk::TreeModelColumn<bool> *col = new Gtk::TreeModelColumn<bool>;
  _columns.push_back(col);
  add(*col);
  add_bec_index_mapping(bec_tm_idx);

  int nr_of_cols;
  // If we have bec_tm_idx set to negative value it means that column added is not
  // directly mapped to a model. Handling of values set/get are done through
  // ListModelWrapper::_fake_column_value_getter/setter slot
  if ( editable == EDITABLE  )
  {
    nr_of_cols= _treeview->append_column_editable(bec::replace_string(name, "_", "__"), *col);

    Gtk::CellRendererToggle *cell = (Gtk::CellRendererToggle*)(_treeview->get_column_cell_renderer(nr_of_cols - 1));
    cell->property_activatable() = true;
    if ( action == TOGGLE_BY_WRAPPER /* && bec_tm_idx >= 0 */)
    {
      cell->signal_toggled().connect(sigc::bind
                                    (sigc::mem_fun(*_tmw
                                                  ,&ListModelWrapper::after_cell_toggle/*<bool>*/
                                                  )
                                                  , sigc::ref(*col)
                                    )
                                    );
    }
  }
  else
    nr_of_cols= _treeview->append_column(bec::replace_string(name, "_", "__"), *col);

  _treeview->get_column(nr_of_cols-1)->set_resizable(true);

  return nr_of_cols;
}

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

int ColumnsModel::add_generic_column(const int bec_tm_idx, Gtk::TreeModelColumnBase *column,
                                     Gtk::TreeViewColumn *vcolumn)
{
  add(*column);
  add_bec_index_mapping(bec_tm_idx);

  return _treeview->append_column(*vcolumn);
}

//------------------------------------------------------------------------------
void ColumnsModel::add_model_column(Gtk::TreeModelColumnBase *col, int bec_tm_idx)
{
  add(*col);
  add_bec_index_mapping(bec_tm_idx);
  _columns.push_back(col);
}

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

ListModelWrapper::ListModelWrapper(bec::ListModel* tm, Gtk::TreeView *treeview, const std::string& name)
                    : Glib::ObjectBase(typeid(ListModelWrapper))
                    , Glib::Object()
                    , _treeview(treeview)
                    , _iconview(0)
                    , _context_menu(0)
                    , _stamp(1)
                    , _columns(this, treeview)
                    , _icon_size(bec::Icon16)
                    , _name(name)
{
  _invalid = false;

  scoped_connect(tm->tree_changed_signal(), boost::bind(&::ListModelWrapper::model_changed, this, _1, _2));

  // keep an indirect pointer because sometimes bec::ListModel is destroyed before the wrapper
  // and in that case we don't want to keep using the dead pointer
  _tm = new bec::ListModel*();
  *_tm = tm;

  tm->add_destroy_notify_callback(_tm, &ListModelWrapper::on_bec_model_destroyed);
  #ifndef NO_MENU_MANAGER
  if (_treeview)
    _treeview->signal_event().connect(sigc::mem_fun(this, &ListModelWrapper::handle_popup_event));
  #endif
}

//------------------------------------------------------------------------------
void* ListModelWrapper::on_bec_model_destroyed(void* data)
{
  bec::ListModel **ptr = (bec::ListModel**)data;
  *ptr = 0;
  return 0;
}

//------------------------------------------------------------------------------
ListModelWrapper::~ListModelWrapper()
{
  delete _context_menu;
  if (*_tm)
    (*_tm)->remove_destroy_notify_callback(_tm);
  *_tm= 0;
}

//------------------------------------------------------------------------------
void ListModelWrapper::set_iconview(Gtk::IconView* iv)
{
  _iconview = iv;
  #ifndef NO_MENU_MANAGER
  if (_iconview)
    _iconview->signal_event().connect(sigc::mem_fun(this, &ListModelWrapper::handle_popup_event));
  #endif
}

//------------------------------------------------------------------------------
ListModelWrapper::NodeIdArray ListModelWrapper::get_selection() const
{
  NodeIdArray                 nodes;
  std::vector<Gtk::TreePath>  paths;

  if (_treeview)
    paths = _treeview->get_selection()->get_selected_rows();
  else if (_iconview)
    paths = _iconview->get_selected_items();

  const int size = paths.size();
  nodes.reserve(size);
  for (int i = 0; i < size; ++i)
    nodes.push_back(get_node_for_path(paths[i]));

  return nodes;
}

//------------------------------------------------------------------------------
void ListModelWrapper::set_be_model(bec::ListModel* tm)
{
  if (*_tm)
    (*_tm)->remove_destroy_notify_callback(_tm);
  *_tm = tm;
  if (*_tm)
  {
    (*_tm)->add_destroy_notify_callback(_tm, &ListModelWrapper::on_bec_model_destroyed);
  }
}

#ifndef NO_MENU_MANAGER
//------------------------------------------------------------------------------
void ListModelWrapper::handle_popup(const int x, const int y, const int time, GdkEventButton* evb)
{
  Gtk::TreeModel::Path   path;
  Gtk::TreeView::Column *column(0);
  int                    cell_x(-1);
  int                    cell_y(-1);

  ListModelWrapper::NodeIdArray list = get_selection();

  bool there_is_path_at_pos = false;
  if (_treeview)
    there_is_path_at_pos = _treeview->get_path_at_pos(x, y, path, column, cell_x, cell_y);
  else if (_iconview)
  {
    path = _iconview->get_path_at_pos(x, y);
    there_is_path_at_pos = path.gobj() && !path.empty();
  }

  if ( there_is_path_at_pos )
  {
    // Check that @path is on selection, otherwise add @path to selection
    bec::NodeId node = get_node_for_path(path);
    // list stores current selection
    bool path_at_pos_is_in_selection = false;
    for (int i = list.size() - 1; i >= 0; --i)
    {
      if (node == list[i])
      {
        path_at_pos_is_in_selection = true;
        break;
      }
    }

    if (!path_at_pos_is_in_selection)
    {
      // Add it, if user holds Ctrl while clicking right mouse btn
      // Otherwise clear selection, and select only @path
      const bool clear_selection = evb ? (!(evb->state & GDK_CONTROL_MASK)) : false;
      if (clear_selection)
      {
        if (_treeview)
          _treeview->get_selection()->unselect_all();
        if (_iconview)
          _iconview->unselect_all();
      }

      if (_treeview)
        _treeview->get_selection()->select(path);
      if (_iconview)
        _iconview->select_path(path);

      list = get_selection();
    }
  }
  if (!_context_menu)
    _context_menu = new Gtk::Menu();

  run_menu_and_forward_action((*_tm)->get_popup_items_for_nodes(list), x, y, time, *_tm, list, _fe_menu_handler, _context_menu);
}

//------------------------------------------------------------------------------
bool ListModelWrapper::handle_popup_event(GdkEvent* event)
{
  bool ret = false;
  if ( event->type == GDK_BUTTON_PRESS && event->button.button == 3 )
  {
    GdkEventButton* evb = (GdkEventButton*)event;
    handle_popup((int)event->button.x, (int)event->button.y, event->button.time, evb);
    // Return true to preserve selection. In handle_popup check that popup is requested
    // on selection.
    ret = true;
  }
  return ret;
}
#endif

//------------------------------------------------------------------------------
Gtk::TreeModelFlags ListModelWrapper::get_flags_vfunc() const
{
  return Gtk::TREE_MODEL_ITERS_PERSIST | Gtk::TREE_MODEL_LIST_ONLY;
}

//------------------------------------------------------------------------------
bec::NodeId ListModelWrapper::get_node_for_path(const Gtk::TreeModel::Path &path) const
{
  if (path.empty())
    return bec::NodeId();
  return bec::NodeId(path.to_string());
}

//------------------------------------------------------------------------------
bool ListModelWrapper::init_gtktreeiter(GtkTreeIter *it, const bec::NodeId &node) const
{
  if ( *_tm && it && node.is_valid() )
  {
    Index id(it, node);
    id.stamp(_stamp);
  }
  return it && node.is_valid();
}


//------------------------------------------------------------------------------
bec::NodeId ListModelWrapper::node_for_iter(const iterator &iter) const
{
  const GtkTreeIter* it = iter.gobj();
  bec::NodeId  node;

  if ( it )
  {
    const Index id(it);
    if (id.cmp_stamp(_stamp))
      node = id.to_node();
  }

  return node;
}

//------------------------------------------------------------------------------
void ListModelWrapper::refresh()
{
  if (*_tm) (*_tm)->refresh();
  model_changed(bec::NodeId(), -1);
}


//------------------------------------------------------------------------------
void ListModelWrapper::note_row_added()
{
  if (*_tm)
  {
    (*_tm)->refresh();
    Gtk::TreePath path((*_tm)->count()-1);

    row_inserted(path, get_iter(path));
  }
}

//------------------------------------------------------------------------------
void ListModelWrapper::set_fake_column_value_getter(FakeColumnValueGetter fake_getter)
{
  _fake_column_value_getter = fake_getter;
}

//------------------------------------------------------------------------------
void ListModelWrapper::set_fake_column_value_setter(FakeColumnValueSetter fake_setter)
{
  _fake_column_value_setter = fake_setter;
}

//------------------------------------------------------------------------------
void ListModelWrapper::reset_iter(iterator& iter) const throw()
{
  GtkTreeIter *it = iter.gobj();
  Index::reset_iter(it);
}

//------------------------------------------------------------------------------
int ListModelWrapper::get_n_columns_vfunc() const
{
  return _columns.size();
}

//------------------------------------------------------------------------------
GType ListModelWrapper::get_column_type_vfunc(int index) const
{
  return *(_columns.types() + index);
}

//------------------------------------------------------------------------------
bool ListModelWrapper::iter_next_vfunc(const iterator& iter, iterator& iter_next) const
{
  bool ret = false;
  bec::NodeId current_node= node_for_iter(iter);

  //g_message("LMW::iter_next_vfunc: %s", _name.c_str());

  // Invalidate iter_next
  reset_iter(iter_next);

  if (*_tm)
  {
    if (current_node.is_valid() && (*_tm)->has_next(current_node))
    {
      try
      {
        // Obtain parent of the current node to get number of children of the parent node
        current_node = (*_tm)->get_next(current_node);
      }
      catch (...)
      {
        current_node= bec::NodeId();
      }
      // Check if the resulted nodeid is valid and setup iter_next with values pointing to the nodeid
      if ( current_node.is_valid() )
        ret = init_gtktreeiter(iter_next.gobj(), current_node);
    }
  }
  return ret;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::get_iter_vfunc(const Path& path, iterator& iter) const
{
  bool ret = false;
  GtkTreeIter* it = iter.gobj();

  // Invalidate iter_next
  reset_iter(iter);

  if (*_tm)
  {
    bec::NodeId node(path.to_string());

    if ( node.is_valid() && node.back() < (*_tm)->count() )
      ret = init_gtktreeiter(it, node);
  }
  return ret;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::iter_children_vfunc(const iterator& parent, iterator& iter) const
{
  return false;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::iter_parent_vfunc(const iterator& child, iterator& iter) const
{
  return false;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::iter_nth_child_vfunc(const iterator& parent, int n, iterator& iter) const
{
  return false;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::iter_nth_root_child_vfunc(int n, iterator& iter) const
{
  bool ret = false;
  //Sets @a iter to be the child of at the root level using the given index.  The first
  //index is 0.  If @a n is too big, or if there are no children, @a iter is set
  //to an invalid iterator and false is returned.
  //See also iter_nth_child_vfunc().
  if ( *_tm && n >= 0 && n < iter_n_root_children_vfunc() )
  {
    bec::NodeId node = (*_tm)->get_node(n);
    init_gtktreeiter(iter.gobj(), node);
    ret = true;
  }

  return ret;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::iter_has_child_vfunc(const iterator& iter) const
{
  return iter_n_children_vfunc(iter) != 0;
}

//------------------------------------------------------------------------------
int ListModelWrapper::iter_n_children_vfunc(const iterator& iter) const
{
  return 0;
}

//------------------------------------------------------------------------------
int ListModelWrapper::iter_n_root_children_vfunc() const
{
  const int ret = *_tm ? (*_tm)->count() : 0;
  return ret;
}

//------------------------------------------------------------------------------
Gtk::TreeModel::Path ListModelWrapper::get_path_vfunc(const iterator& iter) const
{
  const bec::NodeId node = node_for_iter(iter);
  Gtk::TreeModel::Path path;

  if ( node.is_valid() )
  {
    const int node_depth = node.depth();

    for ( int i = 0; i < node_depth; i++ )
      path.push_back(node[i]);
  }

  return path;
}

//------------------------------------------------------------------------------
void ListModelWrapper::get_icon_value(const iterator& iter, int column, const bec::NodeId &node, Glib::ValueBase& value) const
{
  if (!*_tm)
    return;

  static ImageCache       *pixbufs      =  ImageCache::get_instance();
  static Glib::RefPtr<Gtk::IconTheme> icon_theme= Gtk::IconTheme::get_default();
  GValue *gval = value.gobj();

  bec::IconId               icon_id = (*_tm)->get_field_icon(node, column, get_icon_size());

  g_value_init(gval, GDK_TYPE_PIXBUF);
  if (icon_id != 0)
  {
    Glib::RefPtr<Gdk::Pixbuf>  pixbuf = pixbufs->image(icon_id);

    if ( pixbuf )
    {
      g_value_set_object(gval, pixbuf->gobj());
    }
  }
}

//------------------------------------------------------------------------------
void ListModelWrapper::get_value_vfunc(const iterator& iter, int column, Glib::ValueBase& value) const
{
  if (!*_tm)
    return;

  bec::NodeId node= node_for_iter(iter);

  if ( node.is_valid() )
  {
    const GType type = *(_columns.types() + column);

    column = _columns.ui2bec(column);

    if ( column < 0 )
    {
      if ( !_fake_column_value_getter.empty() )
        _fake_column_value_getter(iter, column, type, value);
    }
    else if ( type == GDK_TYPE_PIXBUF )
    {
      get_icon_value(iter, column, node, value);
    }
    else
    {
      switch ( type )
      {
      case G_TYPE_BOOLEAN:
        {
          bool bv = false;
          (*_tm)->get_field(node, column, bv);
          set_glib_bool(value, bv);
          lmwdprint("LMW::get_value_vfunc: %s: node %s, col %i: value: %i\n", _name.c_str(), node.repr().c_str(), column, bv);
          break;
        }
      case G_TYPE_INT:
      case G_TYPE_UINT:
        {
          ssize_t iv = 0;
          (*_tm)->get_field(node, column, iv);
          set_glib_int(value, iv);
          lmwdprint("LMW::get_value_vfunc: %s: node %s, col %i: value: %i\n", _name.c_str(), node.repr().c_str(), column, iv);
          break;
        }
      case G_TYPE_LONG:
      case G_TYPE_ULONG:
      case G_TYPE_INT64:
      case G_TYPE_UINT64:
        {
          throw std::logic_error("Imlement long ints in get_value_func");
          break;
        }
      case G_TYPE_FLOAT:
      case G_TYPE_DOUBLE:
        {
          double dv = 0.0;
          (*_tm)->get_field(node, column, dv);
          set_glib_double(value, dv);
          lmwdprint("LMW::get_value_vfunc: %s: node %s, col %i: value: %f\n", _name.c_str(), node.repr().c_str(), column, dv);
          break;
        }
      case G_TYPE_STRING:
        {
          std::string sv;
          if (column < 0)
            sv= (*_tm)->get_field_description(node, -column);
          else
            (*_tm)->get_field_repr(node, column, sv);
          set_glib_string(value, sv, true);
          lmwdprint("LMW::get_value_vfunc: %s: node %s, col %i: value: '%s'\n", _name.c_str(), node.repr().c_str(), column, sv.c_str());
          break;
        }
      default:
        set_glib_string(value, "<unkn>");
        break;
      }
    }
  }
}

//------------------------------------------------------------------------------
bool ListModelWrapper::iter_is_valid(const iterator& iter) const
{
  bec::NodeId node(node_for_iter(iter));

  return node.is_valid();
}

//------------------------------------------------------------------------------
void ListModelWrapper::set_value_impl(const iterator& row, int column, const Glib::ValueBase& value)
{
  if (!*_tm)
    return;

  bec::NodeId node(node_for_iter(row));

  if ( node.is_valid() )
  {
    const GType type = *(_columns.types() + column);
    column = _columns.ui2bec(column);

    if ( column < 0 )
    {
      if ( !_fake_column_value_setter.empty() )
        _fake_column_value_setter(row, column, type, value);
    }
    else
    {
      switch ( type )
      {
      case G_TYPE_BOOLEAN:
        {
          Glib::Value<bool>   v;
          v.init(value.gobj());
          lmwdprint("LMW::set_value_impl:%s node %s, column %i, value %i\n", _name.c_str(), node.repr().c_str(), column, v.get());
          (*_tm)->set_field(node, column, (ssize_t)v.get());
          break;
        }
      case G_TYPE_INT:
      case G_TYPE_UINT:
        {
          Glib::Value<ssize_t>   v;
          v.init(value.gobj());
          lmwdprint("LMW::set_value_impl: node %s, column %i, value %i\n", node.repr().c_str(), column, v.get());
          (*_tm)->set_field(node, column, v.get());
          break;
        }
      case G_TYPE_FLOAT:
      case G_TYPE_DOUBLE:
        {
          Glib::Value<double>  v;
          v.init(value.gobj());
          (*_tm)->set_field(node, column, v.get());
          break;
        }
      case G_TYPE_STRING:
        {
          Glib::Value<std::string>   v;
          v.init(value.gobj());
          (*_tm)->set_field(node, column, v.get());
          lmwdprint("LMW::set_value: %s '%s'\n", _name.c_str(), v.get().c_str());
          break;
        }
      default:
        break;
      }
    }
  }
}

//------------------------------------------------------------------------------
void ListModelWrapper::get_value_impl(const iterator& row, int column, Glib::ValueBase& value) const
{
  get_value_vfunc(row, column, value);
}

//------------------------------------------------------------------------------
void ListModelWrapper::set_row_draggable_slot(const sigc::slot<bool, Gtk::TreeModel::Path> &slot)
{
  _row_draggable= slot;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::drag_data_get_vfunc(const Gtk::TreeModel::Path& path, Gtk::SelectionData& selection_data) const
{
  selection_data.set("text/path", path.to_string());
  return true;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::drag_data_delete_vfunc (const Gtk::TreeModel::Path& path)
{
  return true;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::row_draggable_vfunc(const TreeModel::Path &path) const
{
  if (_row_draggable)
    return _row_draggable(path);

  return true;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data) const
{
  return true;
}

//------------------------------------------------------------------------------
bool ListModelWrapper::drag_data_received_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data)
{
  bool ret = false;
  // Currently this works for linear lists
  try
  {
    (*_tm)->reorder(bec::NodeId((const char*)selection_data.get_data()), dest.front());
    ret = true;
  }
  catch (const std::logic_error& e)
  {}
  return ret;
}



//------------------------------------------------------------------------------
void ListModelWrapper::invalidate()
{
  _invalid= true;
  *_tm= 0;
  _stamp++;
}

