#include "gtk/lf_mforms.h"

#include <mforms_grttreeview.h>
#include <mforms/mforms.h>
#include "lf_view.h"
#include "gtk_helpers.h"
#include "linux_utilities/treemodel_wrapper.h"

//------------------------------------------------------------------------------
mforms::gtk::GRTTreeViewImpl::GRTTreeViewImpl(GRTTreeView *self, TreeOptions options)
                : ViewImpl(self)
{
  _swin= Gtk::manage(new Gtk::ScrolledWindow());
  _swin->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  if (options & TreeNoBorder)
    _swin->set_shadow_type(Gtk::SHADOW_NONE);
  else
    _swin->set_shadow_type(Gtk::SHADOW_IN);
  _tree= Gtk::manage(new Gtk::TreeView());
  _tree->show();
  
  _tree->signal_row_activated().connect(sigc::bind(sigc::mem_fun(this, &mforms::gtk::GRTTreeViewImpl::row_activated), self));
  _tree->get_selection()->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &mforms::gtk::GRTTreeViewImpl::selection_changed), self));

  _swin->add(*_tree);

  if (options & TreeNoHeader)
    _tree->set_headers_visible(false);
  else
    _tree->set_headers_visible(true);
  _swin->show();
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::row_activated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column, GRTTreeView *tree)
{
  std::list<Gtk::TreeViewColumn*> columns= _tree->get_columns();
  int c= 0;

  for (std::list<Gtk::TreeViewColumn*>::const_iterator iter= columns.begin();
       iter != columns.end(); ++iter)
  {
    if (*iter == column)
      break;
    c++;
  }

  if (_tree->row_expanded(path))
    _tree->collapse_row(path);
  else
    _tree->expand_row(path, false);

  tree->row_activate_callback(_store->get_node_for_path(path), _columns[c].column);

  _store->row_changed(path, _store->get_iter(path));
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::selection_changed(GRTTreeView *tree)
{
  tree->changed();
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::set_model(bec::TreeModel *model)
{
  if (!model)
  {
    _tree->unset_model();
    _store.clear();
    return;
  }
  _store= TreeModelWrapper::create(model, _tree, "mforms");
  _store->set_expanded_rows_storage(&_expanded_rows);
  // this is necessary to make SCHEMAs list not fetch everything on load
  _store->set_delay_expanding_nodes(dynamic_cast<mforms::View*>(owner)->get_name() == "LiveSchemaTree");

  _tree->set_model(_store);

  _tree->remove_all_columns();
  for (size_t column= 0; column < _columns.size(); column++)
  {
    switch (_columns[column].type)
    {
    case StringGRTColumnType:
      _store->model().append_string_column(_columns[column].column, _columns[column].name, _columns[column].editable ? EDITABLE : RO);
      break;
    case IconStringGRTColumnType:
      if (_columns[column].column / 100 == 42) // magic value to enable markup column in gtk (42xx)
        _store->model().append_markup_column(_columns[column].column, _columns[column].name, WITH_ICON);
      else
        _store->model().append_string_column(_columns[column].column, _columns[column].name, _columns[column].editable ? EDITABLE : RO, WITH_ICON);
      break;
    case IntegerGRTColumnType:
      break;
    case CheckGRTColumnType:
      break;
    case IconGRTColumnType:
      _store->model().append_string_column(_columns[column].column, _columns[column].name, _columns[column].editable ? EDITABLE : RO, WITH_ICON);
      break;
    }
    _tree->get_column(column)->set_resizable(true);
    if (_columns[column].default_width > 0)
      _tree->get_column(column)->set_fixed_width(_columns[column].default_width);
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::set_column_width(int model_column, int width)
{
  for (size_t column= 0; column < _columns.size(); column++)
  {
    if (_columns[column].column == model_column)
    {
      Gtk::TreeViewColumn *c= _tree->get_column(column);
      if (!c)
      {
        _columns[column].default_width = width;
        break;
      }
      c->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED);
      c->set_resizable(true);
      c->set_fixed_width(width);
      break;
    }
  }
}

//------------------------------------------------------------------------------
int mforms::gtk::GRTTreeViewImpl::add_column(GRTTreeColumnType type, int model_column, const std::string &name, bool editable)
{
  ColumnSpec c;
  c.type= type;
  c.column= model_column;
  c.name= name;
  c.editable= editable;
  c.default_width= 0;
  _columns.push_back(c);
  
  return _columns.size()-1;
}

//#include "wbdbg.h"
//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::refresh(const bec::NodeId &node)
{
  //wb_ptrace();

  Gtk::Adjustment* vadj = _swin->get_vadjustment();
  const double vpos = vadj->get_value();

  _tree->set_model(get_empty_model());
  _tree->set_model(_store);

  if (_store)
    expand_tree_nodes_as_in_be(_store, _tree);

  //fprintf(stderr, "\n--------------------------------------------\nvalue %f\n", (float)vpos);
  vadj->set_value(vpos);
  vadj->changed();
}

//------------------------------------------------------------------------------
bool mforms::gtk::GRTTreeViewImpl::create(GRTTreeView *self, mforms::TreeOptions options)
{
  return new GRTTreeViewImpl(self, options) != 0;
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::set_model(GRTTreeView *self, bec::TreeModel *model)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();
  
  tree->set_model(model);
}

//------------------------------------------------------------------------------
int mforms::gtk::GRTTreeViewImpl::add_column(GRTTreeView *self, GRTTreeColumnType type, int model_column, const std::string &name)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();

  return tree->add_column(type, model_column, name, false);
}

//------------------------------------------------------------------------------
int mforms::gtk::GRTTreeViewImpl::add_column_editable(GRTTreeView *self, GRTTreeColumnType type, int model_column, const std::string &name)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();

  return tree->add_column(type, model_column, name, true);
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::refresh(GRTTreeView *self, const bec::NodeId &node)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();
  
  tree->refresh(node);
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::set_column_width(GRTTreeView *self, int model_column, int width)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();
  
  tree->set_column_width(model_column, width);
}

//------------------------------------------------------------------------------
bool mforms::gtk::GRTTreeViewImpl::get_selected_node(GRTTreeView *self, bec::NodeId &node)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();
  std::list<Gtk::TreePath> selection= tree->_tree->get_selection()->get_selected_rows();
  const bool has_items = !selection.empty();

  if (has_items)
    node = tree->_store->get_node_for_path(*selection.begin());

  return has_items;
}

//------------------------------------------------------------------------------
int mforms::gtk::GRTTreeViewImpl::get_selection(GRTTreeView *self, std::vector<bec::NodeId> &nodes)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();
  std::list<Gtk::TreePath> selection= tree->_tree->get_selection()->get_selected_rows();
  nodes.clear();
  for (std::list<Gtk::TreePath>::const_iterator iter= selection.begin(); iter != selection.end(); ++iter)
  {
    nodes.push_back(tree->_store->get_node_for_path(*iter));
  }
  return nodes.size();
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::set_allow_multi_selection(GRTTreeView *self, bool flag)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();
  tree->_tree->get_selection()->set_mode(flag ? Gtk::SELECTION_MULTIPLE : Gtk::SELECTION_SINGLE);
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::row_count_changed(GRTTreeView *self, const bec::NodeId &parent, int old_count)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();
  if (tree)
  {
    if (old_count == 424242)
    { // hack... toggle and untoggle the node to force a refresh
      Gtk::TreePath path(node2path(parent));
      bool collapsed = !tree->_tree->row_expanded(path); //!static_cast<bec::TreeModel*>(tree->_tree->get_be_model())->is_expanded(parent);
      if (collapsed)
      {
        tree->_tree->expand_to_path(path);
        tree->_tree->collapse_row(path);
      }
      else
      {
        tree->_tree->collapse_row(path);
        tree->_tree->expand_to_path(path);
      }
      return;
    }
    Gtk::TreePath first_row, last_row;
    tree->_tree->get_visible_range(first_row, last_row);
    std::list<Gtk::TreePath> selected(tree->_tree->get_selection()->get_selected_rows());

    tree->_store->refresh();
    tree->refresh(parent);
    if (!first_row.empty())
      tree->_tree->scroll_to_row(first_row);

    for (std::list<Gtk::TreePath>::const_iterator path = selected.begin(); path != selected.end(); ++path)
      tree->_tree->get_selection()->select(*path);
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::set_expanded(GRTTreeView *self, const bec::NodeId &node, bool expanded)
{
  GRTTreeViewImpl* tree = self->get_data<GRTTreeViewImpl>();

  if (tree)
  {
    if (expanded)
    {
      int depth = node.depth();
      int pos   = 0;

      bec::NodeId cur_node;
      while (depth > 0)
      {
        cur_node.append(node[pos]);
        const Gtk::TreeModel::Path path = node2path(cur_node);

        tree->_tree->collapse_row(path); // hack to force gtk to re-request children info from model
        tree->_tree->expand_row(path, false);
        --depth;
        ++pos;
      }
    }
    else
    {
      tree->_tree->collapse_row(node2path(node));
    }
  }
}

//------------------------------------------------------------------------------
void mforms::gtk::GRTTreeViewImpl::init()
{
  ::mforms::ControlFactory *f = ::mforms::ControlFactory::get_instance();

  f->_grttreeview_impl.create= &mforms::gtk::GRTTreeViewImpl::create;
  f->_grttreeview_impl.set_model= &mforms::gtk::GRTTreeViewImpl::set_model;
  f->_grttreeview_impl.add_column= &mforms::gtk::GRTTreeViewImpl::add_column;
  f->_grttreeview_impl.add_column_editable= &mforms::gtk::GRTTreeViewImpl::add_column_editable;
  f->_grttreeview_impl.refresh= &mforms::gtk::GRTTreeViewImpl::refresh;
  f->_grttreeview_impl.set_column_width= &mforms::gtk::GRTTreeViewImpl::set_column_width;
  f->_grttreeview_impl.set_allow_multi_selection= &mforms::gtk::GRTTreeViewImpl::set_allow_multi_selection;
  f->_grttreeview_impl.get_selected_node= &mforms::gtk::GRTTreeViewImpl::get_selected_node;
  f->_grttreeview_impl.get_selection= &mforms::gtk::GRTTreeViewImpl::get_selection;
  f->_grttreeview_impl.row_count_changed= &mforms::gtk::GRTTreeViewImpl::row_count_changed;
  f->_grttreeview_impl.set_expanded= &mforms::gtk::GRTTreeViewImpl::set_expanded;
}
