/*
 * Copyright (C) 2008-2016 Paul Davis <paul@linuxaudiosystems.com>
 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
 * Copyright (C) 2009-2012 David Robillard <d@drobilla.net>
 * Copyright (C) 2015-2016 Colin Fletcher <colin.m.fletcher@googlemail.com>
 * Copyright (C) 2015-2019 Robin Gareus <robin@gareus.org>
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <sstream>
#include <ytkmm/filechooserdialog.h>
#include <ytkmm/messagedialog.h>
#include <ytkmm/stock.h>

#include "pbd/xml++.h"
#include "pbd/error.h"

#include "ardour/filename_extensions.h"
#include "ardour/session.h"
#include "ardour/session_utils.h"

#include "gtkmm2ext/utils.h"

#include "session_metadata_dialog.h"

#include "pbd/i18n.h"

using namespace std;
using namespace Glib;
using namespace PBD;

#define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))

/*** MetadataField ***/

MetadataField::MetadataField (string const & field_name) :
  _name (field_name)
{
}

MetadataField::~MetadataField() { }

/* TextMetadataField */

TextMetadataField::TextMetadataField (Getter getter, Setter setter, string const & field_name, guint width ) :
  MetadataField (field_name),
  getter (getter),
  setter (setter),
  width (width)
{
	entry = 0;
	label = 0;
	value_label = 0;
}

MetadataPtr
TextMetadataField::copy ()
{
	return MetadataPtr (new TextMetadataField (getter, setter, _name, width));
}

void
TextMetadataField::save_data (ARDOUR::SessionMetadata & data) const
{
	CALL_MEMBER_FN (data, setter) (_value);
}

void
TextMetadataField::load_data (ARDOUR::SessionMetadata const & data)
{
	_value = CALL_MEMBER_FN (data, getter) ();
	if (entry) {
		entry->set_text (_value);
	}
}

Gtk::Widget &
TextMetadataField::name_widget ()
{
	label = Gtk::manage (new Gtk::Label(_name + ':'));
	label->set_alignment (1, 0);
	return *label;
}

Gtk::Widget &
TextMetadataField::value_widget ()
{
	value_label = Gtk::manage (new Gtk::Label(_value));
	return *value_label;
}

Gtk::Widget &
TextMetadataField::edit_widget ()
{
	entry = Gtk::manage (new Gtk::Entry());

	entry->set_text (_value);
	entry->set_width_chars (width);
	entry->signal_changed().connect (sigc::mem_fun(*this, &TextMetadataField::update_value));

	return *entry;
}

void
TextMetadataField::update_value ()
{
	_value = entry->get_text ();
}

/* LongTextMetadataField */

LongTextMetadataField::LongTextMetadataField (Getter getter, Setter setter, string const & field_name, guint width ) :
  TextMetadataField (getter, setter, field_name, width)
{
	tview = 0;
	label = 0;
	value_label = 0;
}

MetadataPtr
LongTextMetadataField::copy ()
{
	return MetadataPtr (new TextMetadataField (getter, setter, _name, width));
}

Gtk::Widget &
LongTextMetadataField::edit_widget ()
{
	tview = Gtk::manage (new Gtk::TextView());

	tview->get_buffer()->set_text (_value);
	tview->set_wrap_mode (Gtk::WRAP_WORD);
	tview->set_size_request (-1, 400);
	tview->set_editable (true);

	Glib::RefPtr<Gtk::TextBuffer> tb (tview->get_buffer());
	tb->signal_changed().connect (sigc::mem_fun(*this, &LongTextMetadataField::update_value));

	return *tview;
}

void
LongTextMetadataField::update_value ()
{
	_value = tview->get_buffer()->get_text ();
}

/* NumberMetadataField */

NumberMetadataField::NumberMetadataField (Getter getter, Setter setter, string const & field_name, guint numbers, guint width) :
  MetadataField (field_name),
  getter (getter),
  setter (setter),
  numbers (numbers),
  width (width)
{
	entry = 0;
	label = 0;
	value_label = 0;
}

MetadataPtr
NumberMetadataField::copy ()
{
	return MetadataPtr (new NumberMetadataField (getter, setter, _name, numbers, width));
}

void
NumberMetadataField::save_data (ARDOUR::SessionMetadata & data) const
{
	uint32_t number = str_to_uint (_value);
	CALL_MEMBER_FN (data, setter) (number);
}

void
NumberMetadataField::load_data (ARDOUR::SessionMetadata const & data)
{
	uint32_t number = CALL_MEMBER_FN (data, getter) ();
	_value = uint_to_str (number);
	if (entry) {
		entry->set_text (_value);
	}
}

void
NumberMetadataField::update_value ()
{
	// Accept only numbers that will fit into a uint32_t
	uint32_t number = str_to_uint (entry->get_text());
	_value = uint_to_str (number);
	entry->set_text (_value);
}

Gtk::Widget &
NumberMetadataField::name_widget ()
{
	label = Gtk::manage (new Gtk::Label(_name + ':'));
	label->set_alignment (1, 0);
	return *label;
}

Gtk::Widget &
NumberMetadataField::value_widget ()
{
	value_label = Gtk::manage (new Gtk::Label(_value));
	return *value_label;
}

Gtk::Widget &
NumberMetadataField::edit_widget ()
{
	entry = Gtk::manage (new Gtk::Entry());

	entry->set_text (_value);
	entry->set_width_chars (width);
	entry->set_max_length (numbers);
	entry->signal_changed().connect (sigc::mem_fun(*this, &NumberMetadataField::update_value));

	return *entry;
}

string
NumberMetadataField::uint_to_str (uint32_t i) const
{
	std::ostringstream oss ("");
	oss << i;
	if (oss.str().compare("0")) {
		return oss.str();
	} else {
		return "";
	}
}

uint32_t
NumberMetadataField::str_to_uint (string const & str) const
{
	string tmp (str);
	string::size_type i;
	while ((i = tmp.find_first_not_of("1234567890")) != string::npos) {
		tmp.erase (i, 1);
	}

	std::istringstream iss(tmp);
	uint32_t result = 0;
	iss >> result;
	return result;
}



/* EAN13MetadataField */

EAN13MetadataField::EAN13MetadataField (Getter getter, Setter setter, string const & field_name, guint width) :
  MetadataField (field_name),
  getter (getter),
  setter (setter),
  width (width)
{
	entry = 0;
	label = 0;
	value_label = 0;
	status_label = Gtk::manage (new Gtk::Label (""));
}

MetadataPtr
EAN13MetadataField::copy ()
{
	return MetadataPtr (new EAN13MetadataField (getter, setter, _name, width));
}

void
EAN13MetadataField::save_data (ARDOUR::SessionMetadata & data) const
{
	CALL_MEMBER_FN (data, setter) (_value);
}

void
EAN13MetadataField::load_data (ARDOUR::SessionMetadata const & data)
{
	_value = CALL_MEMBER_FN (data, getter) ();
	if (entry) {
		entry->set_text (_value);
	}
	update_status ();
}

void
EAN13MetadataField::update_value ()
{
	// Accept only numeric characters
	_value = numeric_string (entry->get_text());
	entry->set_text (_value);
	update_status ();
}

void
EAN13MetadataField::update_status ()
{
	int len = _value.length ();
	if (len == 13) {
		// calculate EAN-13 modulo 10 check digit
		int sum = 0;
		const char *p = _value.c_str();
		for (int i = 0; i < 12; i++) {
			char c = p[i] - '0';
			if (i % 2) {
				sum += c * 3;
			} else {
				sum += c;
			}
		}
		sum %= 10;
		if (sum)
			sum = 10 - sum;

		if (sum == p[12] - '0') {
			status_label->set_markup (string_compose(
						"<span color=\"green\">%1: %2</span>",
						_("EAN Check digit OK"), sum));
		} else {
			status_label->set_markup (string_compose(
						"<span color=\"#ffa755\">%1: %2 (%3 %4)</span>",
						_("EAN Check digit error"), p[12] - '0', _("expected"), sum));
		}
	} else if (len > 0) {
		status_label->set_markup (string_compose(
					"<span color=\"#ffa755\">%1: %2 (&lt;13)</span>",
					_("EAN Length error"), len));
	} else {
		status_label->set_text("");
	}
}

Gtk::Widget &
EAN13MetadataField::name_widget ()
{
	label = Gtk::manage (new Gtk::Label(_name + ':'));
	label->set_alignment (1, 0);
	return *label;
}

Gtk::Widget &
EAN13MetadataField::value_widget ()
{
	value_label = Gtk::manage (new Gtk::Label(_value));
	return *value_label;
}

Gtk::Widget &
EAN13MetadataField::edit_widget ()
{
	entry = Gtk::manage (new Gtk::Entry());

	entry->set_text (_value);
	entry->set_width_chars (width);
	entry->set_max_length (13);
	entry->signal_changed().connect (sigc::mem_fun(*this, &EAN13MetadataField::update_value));

	return *entry;
}

string
EAN13MetadataField::numeric_string (string const & str) const
{
	string tmp (str);
	string::size_type i;
	while ((i = tmp.find_first_not_of("1234567890")) != string::npos) {
		tmp.erase (i, 1);
	}
	return tmp;
}

/* SessionMetadataSet */

SessionMetadataSet::SessionMetadataSet (string const & name)
  : name (name)
{
}

void
SessionMetadataSet::add_data_field (MetadataPtr field)
{
	list.push_back (field);
}

/* SessionMetadataSetEditable */

SessionMetadataSetEditable::SessionMetadataSetEditable (string const & name)
  : SessionMetadataSet (name)
{
	table.set_row_spacings (6);
	table.set_col_spacings (12);
	table.set_homogeneous (false);
	vbox.pack_start (table, false, false);
	vbox.set_spacing (6);
	vbox.set_border_width (6);
}

Gtk::Widget &
SessionMetadataSetEditable::get_tab_widget ()
{
	tab_widget.set_text (name);
	return tab_widget;
}

void
SessionMetadataSetEditable::set_session (ARDOUR::Session * s)
{
	SessionHandlePtr::set_session (s);

	if (!_session) {
		return;
	}

	ARDOUR::SessionMetadata const & data = *(ARDOUR::SessionMetadata::Metadata());

	table.resize (list.size(), 2);
	uint32_t row = 0;
	MetadataPtr field;
	for (DataList::const_iterator it = list.begin(); it != list.end(); ++it) {
		field = *it;
		field->load_data (data);
		table.attach (field->name_widget(), 0, 1, row, row + 1, Gtk::FILL);
		table.attach (field->edit_widget(), 1, 2, row, row + 1);
		++row;
	}
}

void
SessionMetadataSetEditable::save_data ()
{
	ARDOUR::SessionMetadata & data = *(ARDOUR::SessionMetadata::Metadata());
	for (DataList::const_iterator it = list.begin(); it != list.end(); ++it) {
		(*it)->save_data(data);
	}
}

/* SessionMetadataSetImportable */

SessionMetadataSetImportable::SessionMetadataSetImportable (string const & name)
  : SessionMetadataSet (name)
  , session_list (list)
{
	tree = Gtk::ListStore::create (tree_cols);
	tree_view.set_model (tree);

	Gtk::TreeView::Column * viewcol;

	// Add import column
	Gtk::CellRendererToggle * import_render = Gtk::manage(new Gtk::CellRendererToggle());
	import_render->signal_toggled().connect (sigc::mem_fun(*this, &SessionMetadataSetImportable::selection_changed));
	viewcol = Gtk::manage(new Gtk::TreeView::Column (_("Import"), *import_render));
	viewcol->add_attribute (import_render->property_active(), tree_cols.import);
	tree_view.append_column (*viewcol);

	// Add field name column
	tree_view.append_column(_("Field"), tree_cols.field);

	// Add values column with pango markup
	Gtk::CellRendererText * values_render = Gtk::manage(new Gtk::CellRendererText());
	viewcol = Gtk::manage(new Gtk::TreeView::Column (_("Values (current value on top)"), *values_render));
	viewcol->add_attribute (values_render->property_markup(), tree_cols.values);
	tree_view.append_column (*viewcol);

	select_all_check.signal_toggled().connect (sigc::mem_fun(*this, &SessionMetadataSetImportable::select_all));
}

Gtk::Widget &
SessionMetadataSetImportable::get_tab_widget ()
{
	tab_widget.set_text (name);
	return tab_widget;
}

Gtk::Widget &
SessionMetadataSetImportable::get_select_all_widget ()
{
	select_all_check.set_label (name);
	return select_all_check;
}

void
SessionMetadataSetImportable::load_extra_data (ARDOUR::SessionMetadata const & data)
{
	if (!_session) {
		error << string_compose (_("programming error: %1"), "no session set for SessionMetaDataSetImportable (in load_data)!") << endmsg;
		return;
	}

	ARDOUR::SessionMetadata const & session_data = *(ARDOUR::SessionMetadata::Metadata());

	MetadataPtr session_field;
	MetadataPtr import_field;
	DataList::iterator session_it;
	DataList::iterator import_it;

	// Copy list and load data to import
	for (session_it = session_list.begin(); session_it != session_list.end(); ++session_it) {
		session_field = *session_it;
		session_field->load_data(session_data);
		import_list.push_back (session_field->copy());
	}

	// Fill widget
	session_it = session_list.begin();
	import_it = import_list.begin();
	while (session_it != session_list.end() && import_it != import_list.end()) { // _should_ be the same...
		session_field = *session_it;
		import_field = *import_it;

		import_field->load_data(data); // hasn't been done yet

		// Make string for values TODO get color from somewhere?
		string values = "<span weight=\"ultralight\" color=\"#777\">" + session_field->value() + "</span>\n"
                        + "<span weight=\"bold\">" + import_field->value() + "</span>";

		Gtk::TreeModel::iterator row_iter = tree->append();
		Gtk::TreeModel::Row row = *row_iter;

		row[tree_cols.field] = import_field->name();
		row[tree_cols.values] = values;
		row[tree_cols.import] = false;
		row[tree_cols.data] = import_field;

		++session_it;
		++import_it;
	}
}

void
SessionMetadataSetImportable::save_data ()
{
	if (!_session) {
		error << string_compose (_("programming error: %1"), "no session set for SessionMetaDataSetImportable (in import_data)!") << endmsg;
		return;
	}

	ARDOUR::SessionMetadata & session_data = *(ARDOUR::SessionMetadata::Metadata());

	Gtk::TreeModel::Children fields = tree->children();
	Gtk::TreeModel::Children::iterator it;
	for (it = fields.begin(); it != fields.end(); ++it) {
		if ((*it)[tree_cols.import]) {
			MetadataPtr field = (*it)[tree_cols.data];
			field->save_data (session_data);
		}
	}
}

void
SessionMetadataSetImportable::select_all ()
{
	select_all_check.set_inconsistent (false);
	bool state = select_all_check.get_active();

	Gtk::TreeModel::Children fields = tree->children();
	Gtk::TreeModel::Children::iterator it;
	for (it = fields.begin(); it != fields.end(); ++it) {
		(*it)[tree_cols.import] = state;
	}
}

void
SessionMetadataSetImportable::selection_changed (string const & path)
{
	select_all_check.set_inconsistent (true);

	Gtk::TreeModel::iterator iter = tree->get_iter (path);
	bool value((*iter)[tree_cols.import]);
	(*iter)[tree_cols.import] = !value;
}

/* SessionMetadataDialog */

template <typename DataSet>
SessionMetadataDialog<DataSet>::SessionMetadataDialog (string const & name) :
  ArdourDialog (name, true)
{
	cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
	cancel_button->signal_clicked().connect (sigc::mem_fun(*this, &SessionMetadataDialog::end_dialog));
	save_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
	save_button->signal_clicked().connect (sigc::mem_fun(*this, &SessionMetadataDialog::save_and_close));
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::init_data ( bool skip_user )
{
	if (!_session) {
		error << string_compose (_("programming error: %1"), "no session set for SessionMetaDataDialog (in init_data)!") << endmsg;
		return;
	}

	if (!skip_user)
		init_user_data ();
	init_track_data ();
	init_album_data ();
	init_people_data ();
	init_school_data ();
	init_description_data ();

	for (DataSetList::iterator it = data_list.begin(); it != data_list.end(); ++it) {
		(*it)->set_session (_session);

		notebook.append_page ((*it)->get_widget(), (*it)->get_tab_widget());
	}
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::load_extra_data (ARDOUR::SessionMetadata const & data)
{
	for (DataSetList::iterator it = data_list.begin(); it != data_list.end(); ++it) {
		(*it)->load_extra_data (data);
	}
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::save_data ()
{
	for (DataSetList::iterator it = data_list.begin(); it != data_list.end(); ++it) {
		(*it)->save_data ();
	}
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::save_and_close ()
{
	save_data ();
	_session->set_dirty();
	end_dialog ();
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::end_dialog ()
{
	hide_all();
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::warn_user (string const & string)
{
	Gtk::MessageDialog msg (string, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK, true);
	msg.run();
}

template <typename DataSet>
std::shared_ptr<std::list<Gtk::Widget *> >
SessionMetadataDialog<DataSet>::get_custom_widgets (WidgetFunc f)
{
	WidgetListPtr list (new WidgetList);
	for (DataSetList::iterator it = data_list.begin(); it != data_list.end(); ++it)
	{
		DataSet * set = dynamic_cast<DataSet *> (it->get());
		list->push_back (& CALL_MEMBER_FN (*set, f) ());
	}

	return list;
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::add_widget (Gtk::Widget & widget)
{
	get_vbox()->pack_start (widget, true, true, 0);
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::init_user_data ()
{
	DataSetPtr data_set (new DataSet (_("User")));
	data_list.push_back (data_set);

	MetadataPtr ptr;

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::user_name, &ARDOUR::SessionMetadata::set_user_name, _("Name")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::user_email, &ARDOUR::SessionMetadata::set_user_email, _("Email")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::user_web, &ARDOUR::SessionMetadata::set_user_web, _("Web")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::organization, &ARDOUR::SessionMetadata::set_organization, _("Organization")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::country, &ARDOUR::SessionMetadata::set_country, _("Country")));
	data_set->add_data_field (ptr);

}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::init_description_data ()
{
	DataSetPtr data_set (new DataSet (_("Description")));
	data_list.push_back (data_set);

	MetadataPtr ptr;

	ptr = MetadataPtr (new LongTextMetadataField (&ARDOUR::SessionMetadata::description, &ARDOUR::SessionMetadata::set_description, _("Description")));
	data_set->add_data_field (ptr);
}


template <typename DataSet>
void
SessionMetadataDialog<DataSet>::init_track_data ()
{
	DataSetPtr data_set (new DataSet (_("Track")));
	data_list.push_back (data_set);

	MetadataPtr ptr;

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::title, &ARDOUR::SessionMetadata::set_title, _("Title")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new NumberMetadataField (&ARDOUR::SessionMetadata::track_number, &ARDOUR::SessionMetadata::set_track_number, _("Track Number"), 3));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::subtitle, &ARDOUR::SessionMetadata::set_subtitle, _("Subtitle")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::grouping, &ARDOUR::SessionMetadata::set_grouping, _("Grouping")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::artist, &ARDOUR::SessionMetadata::set_artist, _("Artist")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::genre, &ARDOUR::SessionMetadata::set_genre, _("Genre")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::comment, &ARDOUR::SessionMetadata::set_comment, _("Comment")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::copyright, &ARDOUR::SessionMetadata::set_copyright, _("Copyright")));
	data_set->add_data_field (ptr);
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::init_album_data ()
{
	DataSetPtr data_set (new DataSet (_("Album")));
	data_list.push_back (data_set);

	MetadataPtr ptr;

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::album, &ARDOUR::SessionMetadata::set_album, _("Album")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new NumberMetadataField (&ARDOUR::SessionMetadata::year, &ARDOUR::SessionMetadata::set_year, _("Year"), 4));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::album_artist, &ARDOUR::SessionMetadata::set_album_artist, _("Album Artist")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new NumberMetadataField (&ARDOUR::SessionMetadata::total_tracks, &ARDOUR::SessionMetadata::set_total_tracks, _("Total Tracks"), 3));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::disc_subtitle, &ARDOUR::SessionMetadata::set_disc_subtitle, _("Disc Subtitle")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new NumberMetadataField (&ARDOUR::SessionMetadata::disc_number, &ARDOUR::SessionMetadata::set_disc_number, _("Disc Number"), 2));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new NumberMetadataField (&ARDOUR::SessionMetadata::total_discs, &ARDOUR::SessionMetadata::set_total_discs, _("Total Discs"), 2));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::compilation, &ARDOUR::SessionMetadata::set_compilation, _("Compilation")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::isrc, &ARDOUR::SessionMetadata::set_isrc, _("ISRC")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new EAN13MetadataField (&ARDOUR::SessionMetadata::barcode, &ARDOUR::SessionMetadata::set_barcode, _("EAN barcode")));
	data_set->add_data_field (ptr);

	// EAN13MetadataField is the only kind of MetadataField which has a status label.
	EAN13MetadataField &emf = (EAN13MetadataField &) *ptr;
	((Gtk::VBox &) data_set->get_widget()).pack_end (*emf.status_label);
	emf.update_status ();
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::init_people_data ()
{
	DataSetPtr data_set (new DataSet (_("People")));
	data_list.push_back (data_set);

	MetadataPtr ptr;

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::lyricist, &ARDOUR::SessionMetadata::set_lyricist, _("Lyricist")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::composer, &ARDOUR::SessionMetadata::set_composer, _("Composer")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::conductor, &ARDOUR::SessionMetadata::set_conductor, _("Conductor")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::remixer, &ARDOUR::SessionMetadata::set_remixer, _("Remixer")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::arranger, &ARDOUR::SessionMetadata::set_arranger, _("Arranger")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::engineer, &ARDOUR::SessionMetadata::set_engineer, _("Engineer")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::producer, &ARDOUR::SessionMetadata::set_producer, _("Producer")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::dj_mixer, &ARDOUR::SessionMetadata::set_dj_mixer, _("DJ Mixer")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::mixer, &ARDOUR::SessionMetadata::set_mixer, S_("Metadata|Mixer")));
	data_set->add_data_field (ptr);
}

template <typename DataSet>
void
SessionMetadataDialog<DataSet>::init_school_data ()
{
	DataSetPtr data_set (new DataSet (_("School")));
	data_list.push_back (data_set);

	MetadataPtr ptr;

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::instructor, &ARDOUR::SessionMetadata::set_instructor, _("Instructor")));
	data_set->add_data_field (ptr);

	ptr = MetadataPtr (new TextMetadataField (&ARDOUR::SessionMetadata::course, &ARDOUR::SessionMetadata::set_course, _("Course")));
	data_set->add_data_field (ptr);

}

/* SessionMetadataEditor */

SessionMetadataEditor::SessionMetadataEditor () :
  SessionMetadataDialog<SessionMetadataSetEditable> (_("Edit Session Metadata"))
{

}

SessionMetadataEditor::~SessionMetadataEditor ()
{
	// Remove pages from notebook to get rid of gsignal runtime warnings
	notebook.pages().clear();
}

void
SessionMetadataEditor::run ()
{
	init_data ();
	init_gui();

	ArdourDialog::run();
}

void
SessionMetadataEditor::init_gui ()
{
	add_widget (notebook);

	show_all();
}

/* SessionMetadataImporter */

SessionMetadataImporter::SessionMetadataImporter () :
  SessionMetadataDialog<SessionMetadataSetImportable> (_("Import session metadata"))
{

}

SessionMetadataImporter::~SessionMetadataImporter ()
{
	// Remove pages from notebook to get rid of gsignal runtime warnings
	notebook.pages().clear();
}

void
SessionMetadataImporter::run ()
{
	if (!_session) {
		error << string_compose (_("programming error: %1"), "no session set for SessionMetaDataImporter (in run)!") << endmsg;
		return;
	}

	/* Open session file selector */

	Gtk::FileChooserDialog session_selector(_("Choose session to import metadata from"), Gtk::FILE_CHOOSER_ACTION_OPEN);
	Gtkmm2ext::add_volume_shortcuts (session_selector);
	session_selector.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
	session_selector.add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT);
	session_selector.set_default_response(Gtk::RESPONSE_ACCEPT);

	Gtk::FileFilter session_filter;
	session_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::statefile_suffix));
	session_filter.set_name (string_compose (_("%1 sessions"), PROGRAM_NAME));
	session_selector.add_filter (session_filter);
	session_selector.set_filter (session_filter);

	int response = session_selector.run();
	session_selector.hide ();

	switch (response) {
	case Gtk::RESPONSE_ACCEPT:
		break;
	default:
		return;
	}

	string session_path = session_selector.get_filename();
	string path, name;
	bool isnew;

	if (session_path.length() > 0) {
		if (ARDOUR::find_session (session_path, path, name, isnew) != 0) {
			return;
		}
	} else {
		return;
	}

	/* We have a session: load the data and run dialog */

	string filename = Glib::build_filename (path, name + ARDOUR::statefile_suffix);
	XMLTree session_tree;
	if (!session_tree.read (filename)) {
		warn_user (_("This session file could not be read!"));
		return;
	}

	/* XXX GET VERSION FROM TREE */
	int version = 3000;

	XMLNode * node = session_tree.root()->child ("Metadata");

	if (!node) {
		warn_user (_("The session file didn't contain metadata!\nMaybe this is an old session format?"));
		return;
	}

	//create a temporary
	ARDOUR::SessionMetadata data;
	data.set_state (*node, version);
	init_data ( true );  //skip user data here
	load_extra_data (data);
	init_gui();

	ArdourDialog::run();
}

void
SessionMetadataImporter::init_gui ()
{
	// Select all from -widget
	add_widget (selection_hbox);
	selection_label.set_text (_("Import all from:"));
	selection_hbox.pack_start (selection_label, false, false);

	WidgetListPtr list = get_custom_widgets (&SessionMetadataSetImportable::get_select_all_widget);
	for (WidgetList::iterator it = list->begin(); it != list->end(); ++it) {
		selection_hbox.pack_start (**it, false, false, 6);
	}

	add_widget (notebook);

	show_all();
}
