#include "sqlide/grid_view.h"
#include <gtkmm/scrolledwindow.h>
#include <boost/foreach.hpp>
#include "base/wb_iterators.h"
#include "mforms/menu.h"

//------------------------------------------------------------------------------
GridView * GridView::create(bec::GridModel::Ref model, bool fixed_height_mode)
{
  GridView *view= Gtk::manage(new GridView(model, fixed_height_mode));
  view->init();
  return view;
}

GridView::GridView(bec::GridModel::Ref model, bool fixed_height_mode)
         : Glib::ObjectBase(typeid(GridView))
         , _row_count(0)
         , _context_menu(0)
{
  if (fixed_height_mode)
    set_fixed_height_mode(true);
  this->model(model);
}

GridView::~GridView()
{
}

void GridView::set_context_menu(mforms::Menu* menu)
{
  _context_menu = menu;
}

void GridView::set_context_menu_responder(const sigc::slot<void>& slot)
{
  _context_menu_responder = slot;
}

void GridView::init()
{
  //!set_fixed_height_mode(true);
  set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_BOTH);
  set_reorderable(false);
  get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);

  //signal_cell_edited().connect(sigc::mem_fun(*this, &GridView::on_cell_edited));

  show();
}

void GridView::model(bec::GridModel::Ref value)
{
  _model= value;
  _view_model= GridViewModel::create(_model, this, "grid_view");
}

int GridView::refresh(bool reset_columns)
{
  Gtk::ScrolledWindow *swin = dynamic_cast<Gtk::ScrolledWindow*>(get_parent());
  float value=-1;
  Gtk::TreePath path;
  Gtk::TreeViewColumn *col = 0;
  if (swin)
  {
    value = swin->get_vadjustment()->get_value();
    get_cursor(path, col);
  }
  
  if (get_model())
    unset_model();
  _view_model->refresh(reset_columns);
  _row_count= _model->count();
  set_model(_view_model);

  get_column(0)->set_resizable(false);
  
  reset_sorted_columns();

  if (swin)
  {
    swin->get_vadjustment()->set_value(value);
    swin->get_vadjustment()->value_changed();
    if (!path.empty())
    {
      if (col && !reset_columns)
        set_cursor(path, *col, false);
      else
        set_cursor(path);
    }
  }

  return 0;
}

void GridView::scroll_to(const int whence) // whence == 0 seeks to start, whence == 1 seeks to end
{
  Gtk::ScrolledWindow *swin = dynamic_cast<Gtk::ScrolledWindow*>(get_parent());
  if (swin)
  {
    double pos = 0;
    if (whence == 0)
      swin->get_vadjustment()->set_value(swin->get_vadjustment()->get_lower());
    else if (whence == 1)
      swin->get_vadjustment()->set_value(swin->get_vadjustment()->get_upper());
  }
}

bec::NodeId GridView::current_cell(int &row, int &col)
{
  bec::NodeId node;
  Gtk::TreeModel::Path path;
  Gtk::TreeViewColumn *column;
  if (get_selection()->count_selected_rows() > 0)
  {
    get_cursor(path, column);
    node= _view_model->get_node_for_path(path);
    row= node[0];
    col= (column) ? _view_model->column_index(column) : -1;
  }
  else
  {
    row= -1;
    col= -1;
  }
  return node;
}

bec::NodeId GridView::current_node()
{
  int row, col;
  return current_cell(row, col);
}

int GridView::current_row()
{
  int row, col;
  current_cell(row, col);
  return row;
}

bool GridView::on_key_press_event(GdkEventKey *event)
{
  bool processed= false;

  if (GDK_KEY_PRESS == event->type)
  {
    switch (event->keyval)
    {
      case GDK_Delete:
      case GDK_KP_Delete:
      {
        if (0 == event->state)
        { 
          if (!_model->is_readonly())
          {
            delete_selected_rows();
            processed= true;
          }
        }
        break;
      }
      case GDK_Tab:
      case GDK_ISO_Left_Tab:
        {
          Gtk::TreeViewColumn  *col    = _column_edited;
          if (col)
          {
            Gtk::TreePath         path   = _path_edited;

            const std::vector<Gtk::TreeViewColumn*> cols = get_columns();
            const int size = cols.size();

            for (int i = 0; i < size; ++i)
            {
              if (cols[i] == col)
              {
                if ((event->state & GDK_SHIFT_MASK) && event->keyval == GDK_ISO_Left_Tab)
                {
                  if (--i == 0)
                  {
                    path.prev();
                    i = size - 1;
                  }
                }
                else
                {
                  if (++i == size)
                  {
                    path.next();
                    i = 1;
                  }
                }

                if (i < size && i >= 0)
                {
                  col = cols[i];
                  _cell_editable->editing_done();

                  set_cursor(path, *col, true);
                  break;
                }
              }
            }
          }
          processed = true;
          break;
        }
    }
  }

  if (!processed)
    processed= Gtk::TreeView::on_key_press_event(event);

  return processed;
}


bool GridView::on_button_release_event(GdkEventButton *e)
{
  if (e->button == 3)
  {
    if (_context_menu)
      _context_menu->popup();
    else if (!_context_menu_responder.empty())
      _context_menu_responder();
  }
  return Gtk::TreeView::on_button_release_event(e);
}


void GridView::add_node_for_path(const Gtk::TreeModel::Path &path, SelectedNodes *nodes)
{
  bec::NodeId node= _view_model->get_node_for_path(path);
  (*nodes)[node[0]]= node;
}

void GridView::get_selected_nodes(SelectedNodes *nodes)
{
  get_selection()->selected_foreach_path(sigc::bind(sigc::mem_fun(this, &GridView::add_node_for_path), nodes));
}

void GridView::delete_selected_rows()
{
  SelectedNodes nodes;
  get_selected_nodes(&nodes);
  for (SelectedNodes::const_reverse_iterator i= nodes.rbegin(), i_end= nodes.rend(); i != i_end; ++i)
    _model->delete_node(i->second);
  sync_row_count();
}

void GridView::on_cell_edited(const Glib::ustring &path_string, const Glib::ustring &new_text)
{
  _signal_cell_edited.emit(path_string, new_text);
  sync_row_count();
}

void GridView::on_cell_editing_started(Gtk::CellEditable* e, const Glib::ustring &path, Gtk::TreeViewColumn* column)
{
  _path_edited   = Gtk::TreePath(path);
  _column_edited = column;
  _cell_editable = e;
  Gtk::Widget* w = dynamic_cast<Gtk::Widget*>(e);
  if (w)
    w->signal_hide().connect(sigc::mem_fun(this, &GridView::on_cell_editing_done));
}

void GridView::on_cell_editing_done()
{
  _column_edited = 0;
  _cell_editable = 0;
}

void GridView::sync_row_count()
{
  if (_model->count() != _row_count)
  {
    refresh(false);
    _signal_row_count_changed.emit();
  }
}

void GridView::on_column_header_clicked(Gtk::TreeViewColumn *column, int column_index)
{
  if (column_index >= 0)
  {
    int sort_direction= 1;

    if (column->get_sort_indicator())
      sort_direction= (column->get_sort_order() == Gtk::SORT_ASCENDING) ? -1 : 0;

    if (!(sort_direction))
      column->set_sort_indicator(false);

    _model->sort_by(column_index, sort_direction, true);
  }
  else
  {
    _model->sort_by(0, 0, false);
    reset_sorted_columns();
  }
}

void GridView::sort_by_column(int column_index, int sort_direction, bool retaining)
{
  _model->sort_by(column_index, sort_direction, retaining);
  reset_sorted_columns();
}

void GridView::reset_sorted_columns()
{
  bec::GridModel::SortColumns sort_columns= _model->sort_columns();
  BOOST_FOREACH (bec::GridModel::SortColumns::value_type &sort_column, sort_columns)
  {
    Gtk::TreeViewColumn *column= get_column(sort_column.first+1); // as opposed to documentation TtreeView::get_column accepts 1-based column index
    Gtk::SortType sort_type= (sort_column.second != 1) ? Gtk::SORT_DESCENDING : Gtk::SORT_ASCENDING;
    column->set_sort_order(sort_type);
    column->set_sort_indicator(true);
  }
}

int GridView::row_count() const
{
  return _row_count;
}
