// src/graphical_preferences_editor.cc - Copyright 2005, 2006, University
//               of Padova, dept. of Pure and Applied
//               Mathematics
//
// This file is part of SGPEMv2.
//
// This 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; either version 2 of the License, or
// (at your option) any later version.
//
// SGPEMv2 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 SGPEMv2; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#include "config.h"
#include "gettext.h"

#include "graphical_preferences_editor.hh"

#include <sgpemv2/cpu_policy_manager.hh>
#include <sgpemv2/cpu_policies_gatekeeper.hh>
#include <sgpemv2/cpu_policy.hh>
#include <sgpemv2/module.hh>
#include <sgpemv2/plugin_manager.hh>

#include <glibmm/ustring.h>
#include <gtkmm/filechooserdialog.h>

#ifndef NDEBUG
#include <iostream>
#endif

using namespace sgpem;
using Gnome::Glade::Xml;

PreferencesEditor::PreferencesEditor(const std::string& gladefile)
	: _refXml(Xml::create(gladefile)),
		preferences_dialog(NULL),
    plugins_dirs_treeview(NULL), plugins_treeview(NULL),
    policies_dirs_treeview(NULL), policies_treeview(NULL),
    speed_spin(NULL)
{
	_refXml->get_widget("ConfigureDialog", preferences_dialog);




  // ========================================
  // PLUGINS



	// List of the currently loaded plugins

  // set up the model
	Gtk::TreeModelColumnRecord column_record_plugins;
	Gtk::TreeModelColumn<Glib::ustring> column_name_plug;
	Gtk::TreeModelColumn<Glib::ustring> column_desc_plug;
	Gtk::TreeModelColumn<Glib::ustring> column_author_plug;
	column_record_plugins.add(column_name_plug);
	column_record_plugins.add(column_desc_plug);
	column_record_plugins.add(column_author_plug);
	plugins_model = Gtk::ListStore::create(column_record_plugins);

  // set up the widget
	_refXml->get_widget("Plugins.Loaded.TreeView", plugins_treeview);
	plugins_treeview->set_model(plugins_model);
	plugins_treeview->append_column("name",  column_name_plug);
  plugins_treeview->get_column_cell_renderer(0)->property_yalign().set_value(0.0);
	plugins_treeview->append_column("description",  column_desc_plug);
  plugins_treeview->get_column_cell_renderer(1)->property_yalign().set_value(0.0);
	plugins_treeview->append_column("author",  column_author_plug);
  plugins_treeview->get_column_cell_renderer(2)->property_yalign().set_value(0.0);

  // update the model with the backend information
  update_plugins();



	// List of the plugins search paths

  // set up the model
	Gtk::TreeModelColumnRecord column_record_plugins_dirs;
	Gtk::TreeModelColumn<Glib::ustring> single_column;
	column_record_plugins_dirs.add(single_column);
	plugins_dirs_model = Gtk::ListStore::create(column_record_plugins_dirs);


  // set up the widget
	_refXml->get_widget("Plugins.Additional.TreeView", plugins_dirs_treeview);
	plugins_dirs_treeview->set_model(plugins_dirs_model);
	plugins_dirs_treeview->append_column("path",  single_column);
  plugins_dirs_treeview->get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
		
	// update the model with the backend information
	typedef std::vector<Glib::ustring> Strings;

  // clear the content of the model
  plugins_dirs_model->clear();

  // for all the paths provided by the singleton global preferences
	Strings plugin_dirs = GlobalPreferences::get_instance().get_plugin_dirs();
	for (Strings::const_iterator i_pd = plugin_dirs.begin(); i_pd != plugin_dirs.end(); i_pd++)
	{
    // append one record to the model, describing the path
    // note that plugin dirs are returned in reverse order
		Gtk::TreeModel::iterator new_row = plugins_dirs_model->append();
		new_row->set_value(0, *i_pd);
	}
  // when the model gets updated, the associated TreeView
  // widget is automatically updated


  // Add and remove directory buttons

	Gtk::Button* add_plugins_dir = NULL;
	_refXml->get_widget("Plugins.Add", add_plugins_dir);
	add_plugins_dir->signal_clicked().connect(sigc::mem_fun(*this, &PreferencesEditor::on_add_plugins_dir));

	Gtk::Button* remove_plugins_dir = NULL;
	_refXml->get_widget("Plugins.Remove", remove_plugins_dir);
	remove_plugins_dir->signal_clicked().connect(sigc::mem_fun(*this, &PreferencesEditor::on_remove_plugins_dir));




  // ========================================
  // POLICIES



	// List of the currently loaded policies

  // set up the model
	Gtk::TreeModelColumnRecord column_record_policies;
	Gtk::TreeModelColumn<Glib::ustring> column_name_pol;
	Gtk::TreeModelColumn<Glib::ustring> column_desc_pol;
	column_record_policies.add(column_name_pol);
	column_record_policies.add(column_desc_pol);
	policies_model = Gtk::ListStore::create(column_record_policies);

  // set up the widget
	_refXml->get_widget("Policies.Loaded.TreeView", policies_treeview);
	policies_treeview->set_model(policies_model);
	policies_treeview->append_column("name",  column_name_pol);
  policies_treeview->get_column_cell_renderer(0)->property_yalign().set_value(0.0);
	policies_treeview->append_column("description",  column_desc_pol);
  policies_treeview->get_column_cell_renderer(1)->property_yalign().set_value(0.0);


  // update the model with the backend information
  update_policies();



	// List of the policies search paths

  // set up the model
	Gtk::TreeModelColumnRecord column_record_policies_dirs;
	Gtk::TreeModelColumn<Glib::ustring> column_path_policies_dirs;
	column_record_policies_dirs.add(column_path_policies_dirs);
	policies_dirs_model = Gtk::ListStore::create(column_record_policies_dirs);

  // set up the widget
	_refXml->get_widget("Policies.Additional.TreeView", policies_dirs_treeview);
	policies_dirs_treeview->set_model(policies_dirs_model);
	policies_dirs_treeview->append_column("path", column_path_policies_dirs);
  policies_dirs_treeview->get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
		
	// update the model with the backend information
	typedef std::vector<Glib::ustring> Strings;

  // clear the content of the model
  policies_dirs_model->clear();

  // for all the paths provided by the singleton global preferences
	Strings policies_dirs = GlobalPreferences::get_instance().get_policy_dirs();
	for (Strings::const_iterator i_pd = policies_dirs.begin(); i_pd != policies_dirs.end(); i_pd++)
	{
    // append one record to the model, describing the path
    // note that policies dirs are returned in reverse order
		Gtk::TreeModel::iterator new_row = policies_dirs_model->append();
		new_row->set_value(0, *i_pd);
	}
  // when the model gets updated, the associated TreeView
  // widget is automatically updated



  // Add and remove directory buttons

	Gtk::Button* add_policies_dir = NULL;
	_refXml->get_widget("Policies.Add", add_policies_dir);
	add_policies_dir->signal_clicked().connect(sigc::mem_fun(*this, &PreferencesEditor::on_add_policies_dir));

	Gtk::Button* remove_policies_dir = NULL;
	_refXml->get_widget("Policies.Remove", remove_policies_dir);
	remove_policies_dir->signal_clicked().connect(sigc::mem_fun(*this, &PreferencesEditor::on_remove_policies_dir));




  // ========================================
  // SPEED



	_refXml->get_widget("Speed.SpinButton", speed_spin);
	speed_spin->set_value(GlobalPreferences::get_instance().get_speed());
	speed_spin->signal_value_changed().connect(sigc::mem_fun(*this, &PreferencesEditor::on_set_speed));
	



  // ========================================
  // GENERAL


  // Close button
	Gtk::Button* close = NULL;
	_refXml->get_widget("Close", close);
	close->signal_clicked().connect(sigc::mem_fun(*this, &PreferencesEditor::on_close));

	// Window& main_window = get_initial_window();
  preferences_dialog->show();
}



void PreferencesEditor::update_policies()
{
  typedef std::vector<CPUPolicyManager*> Managers;
  typedef std::vector<CPUPolicy*> Policies;

  // clear the content of the model
  policies_model->clear();

  // for all the cpu policy managers provided by the singleton gatekeeper
  Managers managers = CPUPoliciesGatekeeper::get_instance().get_registered();
  for (Managers::const_iterator i_m = managers.begin(); i_m != managers.end(); i_m++)
  {
    // for all the policies provided by the manager
    const Policies & policies = (**i_m).get_avail_policies();
    for (Policies::const_iterator i_p = policies.begin(); i_p != policies.end(); i_p++)
    {
      // append one record to the model, describing the policy
      Gtk::TreeModel::iterator new_row = policies_model->append();
      // featuring its name and its description
      new_row->set_value(0, (**i_p).get_name());
      new_row->set_value(1, (**i_p).get_description());
    }  
  }
  // when the model gets updated, the associated TreeView
  // widget is automatically updated
}



void PreferencesEditor::update_plugins()
{
  typedef std::vector<Module*> Modules;

  // clear the content of the model
  plugins_model->clear();

  // for all the plugins provided by the singleton manager
  Modules plugins = PluginManager::get_instance().get_module_list();
  for (Modules::const_iterator i_p = plugins.begin(); i_p != plugins.end(); i_p++)
  {
    // append one record to the model, describing the plugin
    Gtk::TreeModel::iterator new_row = plugins_model->append();
    // featuring its name, its description, and its author

    new_row->set_value(0, (**i_p).get_name());
    new_row->set_value(1, (**i_p).describe());
    new_row->set_value(2, (**i_p).get_author());
  }
  // when the model gets updated, the associated TreeView
  // widget is automatically updated
}



void
PreferencesEditor::on_close()
{
	preferences_dialog->hide();
  GlobalPreferences::get_instance().write_configrc();
}



void
PreferencesEditor::on_add_plugins_dir()
{
	// I must get a string
	Glib::ustring path;
	Gtk::FileChooserDialog* ask_path = new Gtk::FileChooserDialog::FileChooserDialog(
		_("Select a directory to add"),
		Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path);
		ask_path->add_button("gtk-cancel", Gtk::RESPONSE_CANCEL);
	ask_path->add_button("gtk-ok", Gtk::RESPONSE_OK);
	ask_path->show();
	int response = ask_path->run();
	ask_path->hide();
	if (response == Gtk::RESPONSE_OK)
	{
		path = ask_path->get_filename();
    std::vector<Glib::ustring>& modules = GlobalPreferences::get_instance().get_plugin_dirs();
    modules.push_back(path);

		Gtk::TreeModel::iterator new_row = plugins_dirs_model->append();
		new_row->set_value(0, path);
	}
}



void
PreferencesEditor::on_remove_plugins_dir()
{
  std::vector<Glib::ustring>& modules = GlobalPreferences::get_instance().get_plugin_dirs();
  std::vector<Glib::ustring>::iterator mit = modules.begin();
  Glib::RefPtr<const Gtk::TreeSelection> selection = plugins_dirs_treeview->get_selection();
  Gtk::TreeModel::iterator pit = plugins_dirs_model->children().begin();
  while(pit && mit != modules.end())
  {
    if(selection->is_selected(pit))
    {
      modules.erase(mit);
      pit = plugins_dirs_model->erase(pit);
    }
    else
    {
      mit++;
      pit++;      
    }
  }
}



void
PreferencesEditor::on_add_policies_dir()
{
	// I must get a string
	Glib::ustring path;
	Gtk::FileChooserDialog* ask_path = new Gtk::FileChooserDialog::FileChooserDialog(
		_("Select a directory to add"),
		Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER, path);
		ask_path->add_button("gtk-cancel", Gtk::RESPONSE_CANCEL);
	ask_path->add_button("gtk-ok", Gtk::RESPONSE_OK);
	ask_path->show();
	int response = ask_path->run();
	ask_path->hide();
	if (response == Gtk::RESPONSE_OK)
	{
		path = ask_path->get_filename();
    std::vector<Glib::ustring>& policies = GlobalPreferences::get_instance().get_policy_dirs();
    policies.push_back(path);

		Gtk::TreeModel::iterator new_row = policies_dirs_model->append();
		new_row->set_value(0, path);
	}
}



void
PreferencesEditor::on_remove_policies_dir()
{
  std::vector<Glib::ustring>& policies = GlobalPreferences::get_instance().get_policy_dirs();
  std::vector<Glib::ustring>::iterator mit = policies.begin();
  Glib::RefPtr<const Gtk::TreeSelection> selection = policies_dirs_treeview->get_selection();
  Gtk::TreeModel::iterator pit = policies_dirs_model->children().begin();
  while(pit && mit != policies.end())
  {
    if(selection->is_selected(pit))
    {
     policies.erase(mit);
      pit = policies_dirs_model->erase(pit);
    }
    else
    {
      mit++;
      pit++;      
    }
  }

}



void
PreferencesEditor::on_set_speed()
{
	GlobalPreferences::get_instance().set_speed(static_cast<int>(speed_spin->get_value()));
}



PreferencesEditor::~PreferencesEditor()
{
}
