/*
 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "plugin_editor_base.h"
#include "grt/editor_base.h"
#include "base/string_utilities.h"
#include <gtkmm/combobox.h>
#include "mforms/code_editor.h"
#include "gtk/mforms_gtk.h"

using base::strfmt;


#define TEXT_UPDATE_TIMER 500

//------------------------------------------------------------------------------
PluginEditorBase::PluginEditorBase(grt::Module *module, bec::GRTManager *grtm, const grt::BaseListRef &args, const char* glade_file)
  : GUIPluginBase(module)
  , _editor_notebook(0)
  , _grtm(grtm)
  , _xml(0)
  , _live_object_editor_decorator_xml(0)
  , _live_object_editor_decorator_control(0)
  , _old_embedded_editor(0)
  , _old_embedded_find(0)
{
  _refreshing= false;
  set_shadow_type(Gtk::SHADOW_NONE);

  if (glade_file)
    _xml= Gtk::Builder::create_from_file(grtm->get_data_file_path(glade_file));
}

//------------------------------------------------------------------------------
PluginEditorBase::~PluginEditorBase()
{
}

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

std::string PluginEditorBase::get_title()
{ 
  return get_be()->get_title(); 
}

//------------------------------------------------------------------------------
void PluginEditorBase::load_glade(const char* glade_xml_filename)
{
  if (_xml)
    throw std::logic_error("XML already created"); // TODO: better message here

  if (glade_xml_filename)
  {
    _xml = Gtk::Builder::create_from_file(_grtm->get_data_file_path(glade_xml_filename));
    if (!_xml)
      throw std::logic_error("Can't load glade xml");
  }
}

//------------------------------------------------------------------------------
void PluginEditorBase::refresh_form_data()
{
  if (_refreshing)
    return;

  _refreshing= true;

  decorate_object_editor();

  do_refresh_form_data();
  
  _refreshing= false;
}

//------------------------------------------------------------------------------
bool PluginEditorBase::is_editing_live_object()
{
  return get_be()->is_editing_live_object();
}
//------------------------------------------------------------------------------
void PluginEditorBase::decorate_object_editor()
{
  if (!_editor_notebook)
    return;

  if (is_editing_live_object())
  {
    if (!_live_object_editor_decorator_control)
    {
      _live_object_editor_decorator_xml= Gtk::Builder::create_from_file(_grtm->get_data_file_path("modules/data/live_editor_decoration.glade"));
      _live_object_editor_decorator_xml->get_widget("vbox1", _live_object_editor_decorator_control);
      _live_object_editor_decorator_xml->get_widget("live_editor_placeholder", _live_editor_placeholder);

      Gtk::Button *apply_live_edtior_button= 0;
      _live_object_editor_decorator_xml->get_widget("apply_live_edtior_button", apply_live_edtior_button);
      apply_live_edtior_button->signal_pressed().connect(sigc::mem_fun(this, &PluginEditorBase::apply_changes_to_live_object));
      Gtk::Button *revert_live_edtior_button= 0;
      _live_object_editor_decorator_xml->get_widget("revert_live_edtior_button", revert_live_edtior_button);
      revert_live_edtior_button->signal_pressed().connect(sigc::mem_fun(this, &PluginEditorBase::revert_changes_to_live_object));
 //     Gtk::Button *close_live_edtior_button= 0;
 //     _live_object_editor_decorator_xml->get_widget("close_live_edtior_button", close_live_edtior_button);
 //     close_live_edtior_button->signal_pressed().connect(sigc::mem_fun(this, &PluginEditorBase::close_live_object_editor));
    }

    if (_editor_notebook->get_parent() != _live_object_editor_decorator_control)
    {
      _editor_notebook->reparent(*_live_editor_placeholder);
      _live_object_editor_decorator_control->reparent(*this);
      _live_object_editor_decorator_control->show();
    }
  }
  else
  {
    if (_editor_notebook->get_parent() != this)
    {
      if (_editor_notebook->get_parent() == _live_object_editor_decorator_control)
        _live_object_editor_decorator_control->unparent();
      _editor_notebook->reparent(*this);
    }
  }
}
//------------------------------------------------------------------------------
void PluginEditorBase::apply_changes_to_live_object()
{
  // make sure changes to a treeview are committed
  Gtk::Widget *focus= dynamic_cast<Gtk::Window*>(_editor_notebook->get_toplevel())->get_focus();
  if (focus && dynamic_cast<Gtk::Entry*>(focus))
  {
    // if this is an entry, then it could be a tree cell being edited.. check it's parent
    if (dynamic_cast<Gtk::TreeView*>(focus->get_parent()))
      focus->activate();
  }

  bec::BaseEditor *editor= get_be();
  editor->apply_changes_to_live_object();
}
//------------------------------------------------------------------------------
void PluginEditorBase::revert_changes_to_live_object()
{
  get_be()->revert_changes_to_live_object();
}
//------------------------------------------------------------------------------
void PluginEditorBase::close_live_object_editor()
{
  if (get_be()->can_close())
  {
    Gtk::Notebook *notebook= dynamic_cast <Gtk::Notebook *> (get_parent());
    if (notebook)
    {
      hide();
      notebook->remove_page(*this);
      bool visible= false;
      for (int c= notebook->get_n_pages(), i= 0; i < c; i++)
      {
        if (notebook->get_nth_page(i)->is_visible())
        {
          visible= true;
          break;
        }
      }
      if (!visible)
        notebook->hide();
    }
    else
    {
      delete get_toplevel();
    }
  }
}
//------------------------------------------------------------------------------

void PluginEditorBase::add_option_combo_change_handler(Gtk::ComboBox *combo, const std::string &option, const sigc::slot<void,std::string,std::string> &setter)
{
  combo->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &PluginEditorBase::combo_changed), combo, option, setter));
}

//------------------------------------------------------------------------------
sigc::connection PluginEditorBase::add_entry_change_timer(Gtk::Entry *entry, const sigc::slot<void,std::string> &setter)
{
  TextChangeTimer timer;

  timer.commit= sigc::bind(sigc::mem_fun(this, &PluginEditorBase::entry_timeout), entry);
  timer.setter= setter;
  _timers[entry]= timer;

  return entry->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &PluginEditorBase::entry_changed), entry));
}

//------------------------------------------------------------------------------
sigc::connection PluginEditorBase::add_text_change_timer(Gtk::TextView *text, const sigc::slot<void,std::string> &setter)
{
  TextChangeTimer timer;

  timer.commit= sigc::bind(sigc::mem_fun(this, &PluginEditorBase::text_timeout), text);
  timer.setter= setter;
  _timers[text]= timer;

  return text->get_buffer()->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &PluginEditorBase::text_changed), text));
}

//------------------------------------------------------------------------------
bool PluginEditorBase::entry_timeout(Gtk::Entry *entry)
{
  _timers[entry].setter(entry->get_text());
  return false;
}

//------------------------------------------------------------------------------
bool PluginEditorBase::text_timeout(Gtk::TextView *text)
{
  _timers[text].setter(text->get_buffer()->get_text());
  return false;
}

//------------------------------------------------------------------------------
void PluginEditorBase::entry_changed(Gtk::Entry *entry)
{
  if (!_refreshing)
  {
    if (_timers[entry].conn)
      _timers[entry].conn.disconnect();

    _timers[entry].conn= Glib::signal_timeout().connect(_timers[entry].commit, TEXT_UPDATE_TIMER);
  }
}

//------------------------------------------------------------------------------
void PluginEditorBase::text_changed(Gtk::TextView *text)
{
  if (!_refreshing)
  {
    if (_timers[text].conn)
      _timers[text].conn.disconnect();

    _timers[text].conn= Glib::signal_timeout().connect(_timers[text].commit, TEXT_UPDATE_TIMER);
  }
}

//------------------------------------------------------------------------------
void PluginEditorBase::commit_text_changes()
{
  for (std::map<Gtk::Widget*,TextChangeTimer>::iterator iter= _timers.begin();
       iter != _timers.end(); ++iter)
  {
    if (iter->second.conn)
    {
      iter->second.commit();
      iter->second.conn.disconnect();
    }
  }
}


//------------------------------------------------------------------------------
void PluginEditorBase::combo_changed(Gtk::ComboBox *combo, const std::string &option, const sigc::slot<void,std::string,std::string> &setter)
{
  if (!_refreshing)
  {
    Gtk::TreeModel::iterator iter = combo->get_active();
    if (iter)
    {
      Gtk::TreeRow row= *iter;
      Glib::ustring text;
      row.get_value(0, text);
      setter(option, text);
    }
  }
}

bool PluginEditorBase::should_close_on_delete_of(const std::string &oid)
{
  return get_be()->should_close_on_delete_of(oid); 
}


//------------------------------------------------------------------------------
void PluginEditorBase::embed_code_editor(mforms::View *container, Gtk::VBox *vbox, bool commit_on_focus_out)
{
  if (_old_embedded_editor)
    vbox->remove(*_old_embedded_editor);
  if (_old_embedded_find)
    vbox->remove(*_old_embedded_find);
  _old_embedded_find = 0;
  _old_embedded_editor = 0;

  if (container)
  {
    Gtk::Widget *editorw = mforms::widget_for_view(container);
    _old_embedded_editor = editorw;

    editorw->set_size_request(-1, 100);
    vbox->pack_end(*editorw, true, true);
    vbox->resize_children();

    if (commit_on_focus_out)
      editorw->signal_focus_out_event().connect(sigc::bind_return(sigc::hide(sigc::mem_fun(get_be(), &bec::BaseEditor::commit_changes)), false));
  }
}


void PluginEditorBase::focus_widget_when_idle(Gtk::Widget *w)
{
  Glib::signal_idle().connect(sigc::bind_return(sigc::mem_fun(w, &Gtk::Widget::grab_focus), false));
}

