#ifndef _CUSTOM_RENDERERS_H_
#define _CUSTOM_RENDERERS_H_

#include <gtkmm/cellrendererspin.h>
#include <gtkmm/treeview.h>

#include <sstream>
#include <limits>

//==============================================================================
template <class Renderer>
class CellRendererProxy : public Renderer
{
public:
  CellRendererProxy() : Glib::ObjectBase(typeid(CellRendererProxy)), Renderer() {}
  virtual ~CellRendererProxy() {}

  virtual void get_size_vfunc (Gtk::Widget& widget, const Gdk::Rectangle* cell_area, int* x_offset, int* y_offset, int* width, int* height) const
  { Renderer::get_size_vfunc (widget, cell_area, x_offset, y_offset, width, height); }

  virtual void render_vfunc (const Glib::RefPtr<Gdk::Drawable>& window, Gtk::Widget& widget, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, const Gdk::Rectangle& expose_area, Gtk::CellRendererState flags)
  { Renderer::render_vfunc (window, widget, background_area, cell_area, expose_area, flags); }

  virtual bool activate_vfunc (GdkEvent* event, Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags)
  { return Renderer::activate_vfunc (event, widget, path, background_area, cell_area, flags); }

  virtual Gtk::CellEditable* start_editing_vfunc(GdkEvent* event, Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags)
  { return Renderer::start_editing_vfunc(event, widget, path, background_area, cell_area, flags); }

  virtual void on_editing_canceled()
  { Renderer::on_editing_canceled(); }
};

class CustomRendererOps
{
  public:
    virtual ~CustomRendererOps() {};
    virtual Gtk::CellRenderer* data_renderer() {return 0;}
};

//==============================================================================
template <typename Renderer, typename RendererValueType, typename ModelValueType>
class CustomRenderer : public Gtk::CellRenderer, public CustomRendererOps
{
public:
  CustomRenderer();
  virtual ~CustomRenderer() {}

  virtual Glib::PropertyProxy_Base _property_renderable() { return property_data_; }

  enum RendererType { rt_data, rt_pixbuf };
  typedef  CellRendererProxy<Renderer>          RealRenderer;
  RendererType                                  _active_renderer_type;
  RealRenderer                                  _data_renderer;
  CellRendererProxy<Gtk::CellRendererPixbuf>    _pixbuf_renderer;

  Glib::PropertyProxy<bool> property_editable() { return property_editable_; }

  Glib::SignalProxy2<void, const RendererValueType&, const RendererValueType&> signal_edited()
  { return _data_renderer.signal_edited(); }

  Gtk::TreeViewColumn * bind_columns(GridView *treeview, const std::string& name, int index, Gtk::TreeModelColumn<ModelValueType> *model_data_column, Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > *model_pixbuf_column);
  sigc::slot<void, int> set_edit_state;
  void floating_point_visible_scale(int val);
  virtual Gtk::CellRenderer* data_renderer() {return &_data_renderer;}

protected:
  Glib::Property<Glib::RefPtr<Gdk::Pixbuf> >            _property_pixbuf;
  Glib::Property<RendererValueType>                     _property_data;
  Glib::Property<bool>                                  _property_editable;
  Glib::Property<bool>                                  _property_cell_background_set;
  Glib::Property<Glib::ustring>                         _property_cell_background;
  Glib::Property<Gdk::Color>                            _property_cell_background_gdk;
  Glib::PropertyProxy<Glib::RefPtr<Gdk::Pixbuf> >       property_pixbuf_;
  Glib::PropertyProxy<RendererValueType>                property_data_;
  Glib::PropertyProxy<bool>                             property_editable_;
  Glib::PropertyProxy<RendererValueType>                _data_renderer_data;
  Glib::PropertyProxy<bool>                             property_cell_background_set_;
  Glib::PropertyProxy<Glib::ustring>                    property_cell_background_;
  Glib::PropertyProxy<Gdk::Color>                       property_cell_background_gdk_;

  virtual void get_size_vfunc (Gtk::Widget& widget, const Gdk::Rectangle* cell_area, int* x_offset, int* y_offset, int* width, int* height) const;
  virtual void render_vfunc (const Glib::RefPtr<Gdk::Drawable>& window, Gtk::Widget& widget, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, const Gdk::Rectangle& expose_area, Gtk::CellRendererState flags);
  virtual bool activate_vfunc (GdkEvent* event, Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags);
  virtual Gtk::CellEditable* start_editing_vfunc(GdkEvent* event, Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags);
  virtual void on_editing_canceled();
  void on_editing_done(Gtk::CellEditable* editable);

  virtual void on_data_changed();
  virtual void on_pixbuf_changed();
  virtual void on_editable_changed();
  virtual void on_cell_background_set_changed();
  virtual void on_cell_background_changed();
  virtual void on_cell_background_gdk_changed();

private:
  void on_cell_data(Gtk::CellRenderer*, const Gtk::TreeModel::iterator&, Gtk::TreeView *tree);

  Gtk::TreeModelColumn<ModelValueType>                 *_model_data_column;
  Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> >     *_model_pixbuf_column;
  bool                                                  _editing;
  GridView                                              *_treeview;
  sigc::slot<void>                                      _on_editing_done;
  Gtk::TreeModel::Path                                  _editing_path;
  std::string                                           _floating_point_visible_scale;
  int                                                   _column_index;
};

//==============================================================================
template <typename Renderer, typename ModelValueType>
void init_data_renderer(Renderer *renderer)
{
}

//------------------------------------------------------------------------------
template <typename Renderer, typename ModelValueType>
void init_data_renderer(Gtk::CellRendererSpin *renderer)
{
  renderer->property_adjustment()= manage(new Gtk::Adjustment (0, std::numeric_limits<ModelValueType>::min(), std::numeric_limits<ModelValueType>::max()));
}

//==============================================================================
template <typename Renderer, typename RendererValueType, typename ModelValueType>
CustomRenderer<Renderer, RendererValueType, ModelValueType>::CustomRenderer()
            :Glib::ObjectBase(typeid(CustomRenderer)),
            Gtk::CellRenderer(),
            _active_renderer_type(rt_pixbuf),
            _data_renderer(),
            _pixbuf_renderer(),
            _property_pixbuf(*this, _pixbuf_renderer._property_renderable().get_name()),
            _property_data(*this, _data_renderer._property_renderable().get_name()),
            _property_editable(*this, _data_renderer.property_editable().get_name()),
            _property_cell_background_set(*this, _data_renderer.property_cell_background_set().get_name()),
            _property_cell_background(*this, _data_renderer.property_cell_background().get_name()),
            _property_cell_background_gdk(*this, _data_renderer.property_cell_background_gdk().get_name()),
            property_pixbuf_(this, _pixbuf_renderer._property_renderable().get_name()),
            property_data_(this, _data_renderer._property_renderable().get_name()),
            property_editable_(this, _data_renderer.property_editable().get_name()),
            _data_renderer_data(&_data_renderer, _data_renderer._property_renderable().get_name()),
            property_cell_background_set_(this, _data_renderer.property_cell_background_set().get_name()),
            property_cell_background_(this, _data_renderer.property_cell_background().get_name()),
            property_cell_background_gdk_(this, _data_renderer.property_cell_background_gdk().get_name()),
            _model_data_column(NULL),
            _model_pixbuf_column(NULL),
            _editing(false),
            _treeview(NULL),
            _floating_point_visible_scale("%.3f"),
            _column_index(-1)
{
  _pixbuf_renderer.property_xalign()= _data_renderer.property_xalign().get_value();

  property_data_.signal_changed().connect(sigc::mem_fun(*this, &CustomRenderer::on_data_changed));
  property_pixbuf_.signal_changed().connect(sigc::mem_fun(*this, &CustomRenderer::on_pixbuf_changed));
  property_editable_.signal_changed().connect(sigc::mem_fun(*this, &CustomRenderer::on_editable_changed));
  property_cell_background_set_.signal_changed().connect(sigc::mem_fun(*this, &CustomRenderer::on_cell_background_set_changed));
  property_cell_background_.signal_changed().connect(sigc::mem_fun(*this, &CustomRenderer::on_cell_background_changed));
  property_cell_background_gdk_.signal_changed().connect(sigc::mem_fun(*this, &CustomRenderer::on_cell_background_gdk_changed));

  init_data_renderer<Renderer, ModelValueType>(&_data_renderer);
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
floating_point_visible_scale(int val)
{
  if ((val >=0) && (val < 15))
  {
    std::ostringstream oss;
    oss << "%." << val << "f";
    _floating_point_visible_scale= oss.str();
  } 
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
on_data_changed()
{
  _data_renderer_data= _property_data.get_value();
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
on_pixbuf_changed()
{
  _active_renderer_type= (_pixbuf_renderer.property_pixbuf().get_value() ? rt_pixbuf : rt_data);
  _pixbuf_renderer.property_pixbuf()= _property_pixbuf;
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
on_editable_changed()
{
  _data_renderer.property_editable()= _property_editable;
  property_mode()= _data_renderer.property_mode().get_value();
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
on_cell_background_set_changed()
{
  _data_renderer.property_cell_background_set()= _property_cell_background_set;
  _pixbuf_renderer.property_cell_background_set()= _property_cell_background_set;
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
on_cell_background_changed()
{
  _data_renderer.property_cell_background()= _property_cell_background;
  _pixbuf_renderer.property_cell_background()= _property_cell_background;
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
on_cell_background_gdk_changed()
{
  _data_renderer.property_cell_background_gdk()= _property_cell_background_gdk;
  _pixbuf_renderer.property_cell_background_gdk()= _property_cell_background_gdk;
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
Gtk::TreeViewColumn *
CustomRenderer<Renderer, RendererValueType, ModelValueType>::bind_columns(
                                                                GridView *treeview,
                                                                const std::string& name,
                                                                int index,
                                                                Gtk::TreeModelColumn<ModelValueType> *model_data_column,
                                                                Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > *model_pixbuf_column
                                                                )
{
  _treeview= treeview; 
  _column_index = index;

  int column_count= treeview->insert_column_with_data_func(-1, name, *this,
            sigc::bind(sigc::mem_fun(this, &CustomRenderer::on_cell_data), treeview));
  Gtk::TreeViewColumn *treeview_column= treeview->get_column(column_count-1);
  treeview_column->set_resizable(true);

  _model_data_column= model_data_column;
  treeview_column->set_renderer(*this, *model_data_column);

  _model_pixbuf_column= model_pixbuf_column;

  return treeview_column;
}

//------------------------------------------------------------------------------
template <typename RendererValueType, typename ModelValueType>
void load_cell_data(Glib::Property<RendererValueType> &prop, const ModelValueType &val, bool for_edit, const std::string &floating_point_visible_scale)
{
  std::ostringstream oss;
  oss << val;
  prop= oss.str();
}

//------------------------------------------------------------------------------
template <typename RendererValueType>
void load_cell_data(Glib::Property<RendererValueType> &prop, const double &val, bool for_edit, const std::string &floating_point_visible_scale)
{
  std::string s;
  
  if (for_edit)
  {
    std::ostringstream oss;
    oss.precision(15);
    oss << val;
    s= oss.str();
  
    // remove trailing zeros
    std::string::size_type point_pos= s.find_first_of(".,");
    if (std::string::npos != point_pos)
    {
      std::string::reverse_iterator i= s.rbegin();
      for (std::string::reverse_iterator end(s.rend()); i != end; ++i)
        if (*i != '0')
          break;
      s.erase(i.base(), s.end());
    }
  }
  else
  {
    char cstr[32];
    sprintf(cstr, floating_point_visible_scale.c_str(), val);
    s= cstr;
  }

  prop= s;
}

//------------------------------------------------------------------------------
template <typename RendererValueType>
void load_cell_data(Glib::Property<RendererValueType> &prop, const RendererValueType &val, bool for_edit, const std::string &floating_point_visible_scale)
{
  prop= val;
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
on_cell_data(Gtk::CellRenderer* cr, const Gtk::TreeModel::iterator &iter, Gtk::TreeView *tree)
{
  Gtk::TreeIter editing_iter;
  if (!_editing_path.empty())
    editing_iter= tree->get_model()->get_iter(_editing_path);

  load_cell_data(_property_data, iter->get_value(*_model_data_column), (_editing && editing_iter == iter), _floating_point_visible_scale);
  if (_model_pixbuf_column)
    _property_pixbuf= iter->get_value(*_model_pixbuf_column);
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
get_size_vfunc(Gtk::Widget& widget, const Gdk::Rectangle* cell_area, int* x_offset, int* y_offset, int* width, int* height) const
{
  _data_renderer.get_size_vfunc(widget, cell_area, x_offset, y_offset, width, height);
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::
render_vfunc(const Glib::RefPtr<Gdk::Drawable>& window, Gtk::Widget& widget, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, const Gdk::Rectangle& expose_area, Gtk::CellRendererState flags)
{

  int row = -1;
  Gtk::TreePath path;
  if (_treeview->get_path_at_pos(cell_area.get_x()+1, cell_area.get_y()+1, path))
  {
    row = path[0];
    if (row >= 0 && _column_index >= 0)
    {
      int srow, scol;
      _treeview->current_cell(srow, scol);
      if (_treeview->selection_is_cell() && srow >= 0 && scol >= 0 && srow == row && scol == _column_index)
      {
        _treeview->get_style()->paint_flat_box(Glib::RefPtr<Gdk::Window>::cast_dynamic(window), Gtk::STATE_SELECTED, Gtk::SHADOW_ETCHED_IN, background_area, 
                    widget, "", background_area.get_x(), background_area.get_y(), 
                    background_area.get_width(), background_area.get_height());
        flags |= Gtk::CELL_RENDERER_SELECTED;
      }
    }
  }
  switch (_active_renderer_type)
  {
  case rt_data:
    _data_renderer.render_vfunc(window, widget, background_area, cell_area, expose_area, flags);
    break;
  default:
    _pixbuf_renderer.render_vfunc(window, widget, background_area, cell_area, expose_area, flags);
    break;
  }
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
bool CustomRenderer<Renderer, RendererValueType, ModelValueType>::
activate_vfunc(GdkEvent* event, Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags)
{
  return _data_renderer.activate_vfunc(event, widget, path, background_area, cell_area, flags);
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
Gtk::CellEditable* CustomRenderer<Renderer, RendererValueType, ModelValueType>::
start_editing_vfunc(GdkEvent* event, Gtk::Widget& widget, const Glib::ustring& path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags)
{
  Gtk::TreeIter editing_iter = _treeview->get_model()->get_iter(path);
  _editing_path= editing_iter;
  _editing= true;
  Gtk::TreePath tree_path(path);
  int row_index= tree_path[0];
  set_edit_state(row_index);
  load_cell_data(_property_data, editing_iter->get_value(*_model_data_column), true, _floating_point_visible_scale);
  
  Gtk::CellEditable* editable= _data_renderer.start_editing_vfunc(event, widget, path, background_area, cell_area, flags);
  if (editable)
  {
    _on_editing_done= sigc::bind(sigc::mem_fun(this, &CustomRenderer::on_editing_done), editable);
    editable->signal_editing_done().connect(_on_editing_done);
  }
  return editable;
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::on_editing_canceled()
{
  _editing= false;
  Gtk::TreePath path;
  Gtk::TreeViewColumn *col;
  _treeview->get_cursor(path, col);
  if (!path.empty())
    set_edit_state(path[0]);
 _data_renderer.on_editing_canceled();
}

//------------------------------------------------------------------------------
template <typename Renderer, typename RendererValueType, typename ModelValueType>
void CustomRenderer<Renderer, RendererValueType, ModelValueType>::on_editing_done(Gtk::CellEditable* editable)
{
  _editing= false;
  Gtk::TreePath path;
  Gtk::TreeViewColumn *col;
  _treeview->get_cursor(path, col);
  if (!path.empty())
    set_edit_state(path[0]);
  _on_editing_done.disconnect();
}


#endif // _CUSTOM_RENDERERS_H_
