#include <gtkmm.h>
#include "toolbar_manager.h"
#include "workbench/wb_command_ui.h"
#include "image_cache.h"
#include "linux_utilities/gtk_helpers.h"


class ColorComboColumns : public Gtk::TreeModel::ColumnRecord
{
public:
  Gtk::TreeModelColumn<std::string> color;
  Gtk::TreeModelColumn<Glib::RefPtr<Gdk::Pixbuf> > image;
  
  ColorComboColumns()
  {
    add(color);
    add(image);
  }
};

static ColorComboColumns *color_combo_columns= 0;

static Gtk::ComboBox *create_color_combo(const std::vector<std::string> &colors, const std::string &selected)
{
  int i= 0;
  if (!color_combo_columns)
  {
    static ColorComboColumns color_combo_columns_instance;
    color_combo_columns= &color_combo_columns_instance;
  }

  Glib::RefPtr<Gtk::ListStore> model= Gtk::ListStore::create(*color_combo_columns);
  Gtk::ComboBox *combo= new Gtk::ComboBox(Glib::RefPtr<Gtk::TreeModel>(model));

  combo->pack_start(color_combo_columns->image);

  for (std::vector<std::string>::const_iterator iter= colors.begin();
       iter != colors.end(); ++iter, ++i)
  {
    Gtk::TreeRow row= *model->append();
    Glib::RefPtr<Gdk::Pixbuf> pixbuf;
    Gdk::Color color(*iter);

    combo->get_colormap()->alloc_color(color);
    
    pixbuf= Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, false, 8, 16, 14);
    pixbuf->fill(color.get_pixel()<<8);
    
    row[color_combo_columns->color]= *iter;
    row[color_combo_columns->image]= pixbuf;
    
    if (selected == *iter)
      combo->set_active(i);
  }

  return combo;
}


//------------------------------------------------------------------------------
ToolbarManager::ToolbarManager(wb::CommandUI *be)
  : _be(be)
{}

//------------------------------------------------------------------------------
static Glib::RefPtr<Gdk::Pixbuf> crop_pixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf, int x, int y)
{
  return Gdk::Pixbuf::create_subpixbuf(pixbuf, x, y, pixbuf->get_width() - 2*x, pixbuf->get_height() - 2*y);
}

//------------------------------------------------------------------------------
void ToolbarManager::add_toolbar_item(Gtk::Box* toolbar, const bec::ToolbarItem& item, bool right)
{
  static ImageCache *images = ImageCache::get_instance();
  Gtk::Widget *w= 0;

  switch ( item.type )
  {
    case bec::ToolbarAction:
    {
      Gtk::Image *img = new Gtk::Image(images->image(item.icon));
      Gtk::Button *btn = new Gtk::Button();
      btn->set_focus_on_click(false);
      btn->set_border_width(0);
      btn->add(*Gtk::manage(img));
      btn->set_name(item.command);
#if GTK_VERSION_GT(2,10)
      btn->set_tooltip_text(item.tooltip);
#endif
      btn->set_relief(Gtk::RELIEF_NONE);

      btn->show_all();

      btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ToolbarManager::clicked), btn));

      w= btn;
      break;
    }

    case bec::ToolbarToggle:
    {
      Gtk::ToggleButton *btn;
      if (item.icon != 0)
      {
        btn= new Gtk::ToggleButton();
        btn->add(*Gtk::manage(new Gtk::Image(images->image(item.checked && item.alt_icon ? item.alt_icon : item.icon))));
      }
      else
        btn= new Gtk::CheckButton(item.caption);
      btn->set_name(item.command);
#if GTK_VERSION_GT(2,10)
      btn->set_tooltip_text(item.tooltip);
#endif
      btn->set_relief(Gtk::RELIEF_NONE);
      btn->set_active(item.checked);

      btn->show_all();

      btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ToolbarManager::clicked), btn));

      w= btn;
      break;
    }

    case bec::ToolbarRadio:
    {
      Gtk::Image *img = new Gtk::Image(crop_pixbuf(images->image(item.icon), 1, 1));
      Gtk::ToggleButton *btn = new Gtk::ToggleButton();
      btn->add(*Gtk::manage(img));
      btn->set_name(item.command);
      btn->set_focus_on_click(false);
      btn->set_border_width(0);
      if (!item.caption.empty())
        btn->set_label(item.caption);
#if GTK_VERSION_GT(2,10)
      btn->set_tooltip_text(item.tooltip);
#endif
      btn->set_relief(Gtk::RELIEF_NONE);
      btn->set_active(item.checked);

      btn->show_all();
        
      btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ToolbarManager::clicked), btn));

      w= btn;
      break;
    }
    case bec::ToolbarCheck:
    {
      Gtk::CheckButton *btn = new Gtk::CheckButton(item.caption);
      btn->set_name(item.command);
#if GTK_VERSION_GT(2,10)
      btn->set_tooltip_text(item.tooltip);
#endif
      btn->set_active(item.checked);

      btn->show_all();

      btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ToolbarManager::toggled), btn, item.name, item.command));

      w= btn;
      break;
    }
    case bec::ToolbarSeparator:
    {
      Gtk::Alignment *align= new Gtk::Alignment(0.5, 0.5);
      Gtk::Separator *sep;

      if (dynamic_cast<Gtk::HBox*>(toolbar))
        sep= new Gtk::VSeparator();
      else
        sep= new Gtk::HSeparator();

      align->set_padding(2, 1, 2, 1);

      sep->show();
      align->add(*Gtk::manage(sep));

      w= align;
      break;
    }
    case bec::ToolbarLabel:
    {
      Gtk::Label *label= new Gtk::Label(item.command, 0.0, 0.5);
      label->set_markup("<small>"+item.command+"</small>");
      
      w= label;
    }
    break;
  case bec::ToolbarDropDown:
    {
      Gtk::Alignment *align= new Gtk::Alignment(0.5, 0.5, 0.0, 0.0);
      std::vector<std::string> items;
      std::string selected;
      Gtk::ComboBox *combo;

     //!!! items= _be->get_dropdown_items(item.name, item.command, selected);

      if (items.size() > 0 && items[0][0] == '#')
      {
        combo= create_color_combo(items, selected);
        combo->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &ToolbarManager::color_combo_changed),
                                                item.name, combo));

      }
      else
      {
        int i= 0, index= 0;
        combo= new Gtk::ComboBoxText();

        for (std::vector<std::string>::const_iterator iter= items.begin();
             iter != items.end(); ++iter, ++i)
        {
          ((Gtk::ComboBoxText*)combo)->append_text(*iter);
          if (*iter == selected)
            index= i;
         
          combo->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &ToolbarManager::combo_changed),
                                                     item.name, (Gtk::ComboBoxText*)combo));

        }
        
        combo->set_active(index);
      }
      combo->set_name(item.command);
      align->add(*Gtk::manage(combo));

      combo->show();

      w= align;
    }
    break;
  }

  if (w)
  {
    w->set_sensitive(item.enabled);
    
    w->show();
    if (right)
      toolbar->pack_end(*Gtk::manage(w), false, false);
    else
      toolbar->pack_start(*Gtk::manage(w), false, false);
  }
}


//------------------------------------------------------------------------------
void ToolbarManager::rebuild_toolbar(Gtk::Box *toolbar, const std::vector<bec::ToolbarItem> &items)
{
  toolbar->foreach(sigc::mem_fun(*toolbar, &Gtk::Container::remove));

  std::vector<bec::ToolbarItem>::const_iterator cur_toolbar_item = items.begin();
  for (; cur_toolbar_item != items.end(); ++cur_toolbar_item)
  {
    add_toolbar_item(toolbar, *cur_toolbar_item, g_str_has_suffix(cur_toolbar_item->name.c_str(), "right"));
  }
    
  toolbar->show_all_children();
}

//------------------------------------------------------------------------------
// This function is used to free toolbar action slot, when toolbar is deleted.
// The function is passed as DestoryNotify to toolbar->set_data, see below in 
// ToolbarManager::rebuild_toolbar
static void deleteAllocatedCallback(gpointer data)
{
  if (data)
  {
    ToolbarManager::CallbackSlot* cb = (ToolbarManager::CallbackSlot*)data;
    delete cb;
  }
}

//------------------------------------------------------------------------------
//static
void ToolbarManager::rebuild_toolbar(Gtk::Box *toolbar, const std::vector<bec::ToolbarItem> items,
                                     const CustomItemSlot &create_item,
                                     const CallbackSlot &cb)
{
  static ImageCache *images = ImageCache::get_instance();
  Gtk::Widget            *w = 0;

  // Allocate callback on heap, to store ptr in toolbar for later use
  ToolbarManager::CallbackSlot* cb_wrapped = new ToolbarManager::CallbackSlot(cb);
  toolbar->set_data("slot", cb_wrapped, &deleteAllocatedCallback);

  toolbar->foreach(sigc::mem_fun(*toolbar, &Gtk::Container::remove));

  bool right = false;
  std::vector<bec::ToolbarItem>::const_iterator cur_toolbar_item = items.begin();
  for (; cur_toolbar_item != items.end(); ++cur_toolbar_item)
  {
    const bec::ToolbarItem &item = *cur_toolbar_item;

    if (create_item)
      w = create_item(item);
    else
      w = 0;
    if (!w)
    {    
      //add_toolbar_item(toolbar, *cur_toolbar_item, g_str_has_suffix(cur_toolbar_item->name.c_str(), "right"), cb_wrapped);
      switch (item.type)
      {
      case bec::ToolbarAction:
      case bec::ToolbarCheck:
        {
          Gtk::Image *img = new Gtk::Image(images->image(item.checked ? item.alt_icon : item.icon));
          Gtk::Button *btn = new Gtk::Button();
          btn->set_focus_on_click(false);
          btn->set_border_width(0);
          btn->add(*Gtk::manage(img));
          btn->set_name(item.command);
#if GTK_VERSION_GT(2,10)
          btn->set_tooltip_text(item.tooltip);
#endif
          btn->set_relief(Gtk::RELIEF_NONE);
          btn->set_data("slot", cb_wrapped); // No need to add a DestroyNotiy callback here. It is handled in toolbar
          
          btn->show_all();
          
          btn->signal_clicked().connect(sigc::bind(sigc::ptr_fun(&ToolbarManager::clicked_to_slot), btn));
          
          w= btn;
          break;
        }
        
      case bec::ToolbarLabel:
        {
          Gtk::Label *label= new Gtk::Label(item.caption, 0.0, 0.5);
	  label->set_markup("<small>"+item.caption+"</small>");
          w= label;
          break;
        }
        
      case bec::ToolbarSeparator:
        {
          if (item.command == "expander")
            right = true;
          else
          {
            Gtk::Alignment *align= new Gtk::Alignment(0.5, 0.5);
            Gtk::Separator *sep;
          
            if (dynamic_cast<Gtk::HBox*>(toolbar))
              sep= new Gtk::VSeparator();
            else
              sep= new Gtk::HSeparator();
            
            align->set_padding(2, 1, 2, 1);
          
            sep->show();
            align->add(*Gtk::manage(sep));
            
            w= align;
          }
          break;
        }
        
      default:
        g_message("skipping toolbar item %s", item.command.c_str());
        break;
      }
    }

    if (w)
    {
      w->set_sensitive(item.enabled);

      w->show();
      if (right)
        toolbar->pack_end(*Gtk::manage(w), false, false);
      else
        toolbar->pack_start(*Gtk::manage(w), false, false);
    }
  }

  toolbar->show_all_children();
}

//------------------------------------------------------------------------------
void ToolbarManager::clicked(Gtk::Button *btn)
{
  _be->activate_command(btn->get_name());
}

//------------------------------------------------------------------------------
void ToolbarManager::clicked_to_slot(Gtk::Button *btn)
{
  ToolbarManager::CallbackSlot* cb = (ToolbarManager::CallbackSlot*)btn->get_data("slot");
  if (cb)
    (*cb)(btn->get_name());
}


void ToolbarManager::toggled(const Gtk::ToggleButton *btn, const std::string &name, const std::string &option)
{
 //!!! _be->toggle_checkbox_item(name, option, btn->get_active());
}


void ToolbarManager::combo_changed(const std::string &toolbar, Gtk::ComboBoxText *combo)
{
  if (combo->get_active_row_number() >= 0)
  {  
   //!!! _be->select_dropdown_item(toolbar, combo->get_name(), combo->get_active_text());
  }
}


void ToolbarManager::color_combo_changed(const std::string &toolbar, Gtk::ComboBox *combo)
{
  if (combo->get_active_row_number() >= 0)
  { 
    Gtk::TreeRow row= *combo->get_active();
    
   //!!! _be->select_dropdown_item(toolbar, combo->get_name(), row[color_combo_columns->color]);
  }
}

bool ToolbarManager::on_searchitem_key_release_event(GdkEventKey* event, Gtk::Entry* e)
{
  if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter)
  {
    _search_text = e->get_text();
    _be->activate_command("builtin:find");
  }
  return false;
}


void ToolbarManager::focus_searchbox(Gtk::Box *toolbar)
{
  Gtk::Entry *entry = reinterpret_cast<Gtk::Entry*>(toolbar->get_data("search_entry"));

  if (entry)
    entry->grab_focus();
}

