/*
 * Copyright (C) 2008-2016 Paul Davis <paul@linuxaudiosystems.com>
 * Copyright (C) 2009-2012 David Robillard <d@drobilla.net>
 * Copyright (C) 2011-2013 Sakari Bergen <sakari.bergen@beatwaves.net>
 * Copyright (C) 2012-2016 Tim Mayberry <mojofunk@gmail.com>
 * Copyright (C) 2014 Nick Mainsbridge <mainsbridge@gmail.com>
 * Copyright (C) 2015-2018 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 <string>

#include <glibmm/miscutils.h>
#include <glibmm/fileutils.h>

#include "pbd/xml++.h"
#include "pbd/string_convert.h"
#include "pbd/enumwriter.h"
#include "pbd/localtime_r.h"

#include "ardour/libardour_visibility.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
#include "ardour/export_filename.h"
#include "ardour/export_format_specification.h"
#include "ardour/export_channel_configuration.h"
#include "ardour/export_timespan.h"
#include "ardour/utils.h"

#include "pbd/i18n.h"

using namespace PBD;
using namespace Glib;
using std::string;

namespace ARDOUR
{

ExportFilename::ExportFilename (Session & session) :
  include_label (false),
  include_session (false),
  use_session_snapshot_name (false),
  include_revision (false),
  include_channel_config (false),
  include_format_name (false),
  include_channel (false),
  include_timespan (true), // Include timespan name always
  include_time (false),
  include_date (false),
  session (session),
  revision (1),
  date_format (D_None),
  time_format (T_None)
{
	time_t rawtime;
	std::time (&rawtime);
	localtime_r (&rawtime, &time_struct);

	folder = session.session_directory().export_path();

	XMLNode * extra_node = session.extra_xml ("ExportFilename");
	/* Legacy sessions used Session instant.xml for this */
	if (!extra_node) {
		session.instant_xml ("ExportFilename");
	}

	if (extra_node) {
		set_state (*extra_node);
	}
}

XMLNode &
ExportFilename::get_state () const
{
	XMLNode * node = new XMLNode ("ExportFilename");
	XMLNode * child;

	FieldPair dir = analyse_folder();
	child = node->add_child ("Folder");
	child->set_property ("relative", dir.first);
	child->set_property ("path", dir.second);

	add_field (node, "label", include_label, label);
	add_field (node, "session", include_session);
	add_field (node, "snapshot", use_session_snapshot_name);
	add_field (node, "timespan", include_timespan);
	add_field (node, "revision", include_revision);
	add_field (node, "time", include_time, enum_2_string (time_format));
	add_field (node, "date", include_date, enum_2_string (date_format));

	XMLNode * extra_node = new XMLNode ("ExportRevision");
	extra_node->set_property ("revision", revision);
	session.add_extra_xml (*extra_node);

	return *node;
}

int
ExportFilename::set_state (const XMLNode & node)
{
	XMLNode * child;
	FieldPair pair;

	child = node.child ("Folder");
	if (!child) { return -1; }

	folder = "";

	bool is_relative;
	if (child->get_property ("relative", is_relative) && is_relative) {
		folder = session.session_directory ().root_path ();
	}

	std::string tmp;
	if (child->get_property ("path", tmp)) {
		tmp = Glib::build_filename (folder, tmp);
		if (!Glib::file_test (tmp, Glib::FILE_TEST_EXISTS)) {
			warning << string_compose (_("Existing export folder for this session (%1) does not exist - using default"), tmp) << endmsg;
			folder = session.session_directory().export_path();
	} else {
			folder = tmp;
		}
	}

	if (folder.empty() || !Glib::file_test (folder, FileTest (FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
		folder = session.session_directory().export_path();
	}

	pair = get_field (node, "label");
	include_label = pair.first;
	label = pair.second;

	pair = get_field (node, "session");
	include_session = pair.first;

	pair = get_field (node, "snapshot");
	use_session_snapshot_name = pair.first;

	pair = get_field (node, "timespan");
	include_timespan = pair.first;

	pair = get_field (node, "revision");
	include_revision = pair.first;

	pair = get_field (node, "time");
	include_time = pair.first;
	time_format = (TimeFormat) string_2_enum (pair.second, time_format);

	pair = get_field (node, "date");
	include_date = pair.first;
	date_format = (DateFormat) string_2_enum (pair.second, date_format);

	XMLNode * extra_node = session.extra_xml ("ExportRevision");
	/* Legacy sessions used Session instant.xml for this */
	if (!extra_node) {
		extra_node = session.instant_xml ("ExportRevision");
	}

	if (extra_node) {
		extra_node->get_property ("revision", revision);
	}

	return 0;
}

string
ExportFilename::get_path (ExportFormatSpecPtr format) const
{
	string path;
	bool filename_empty = true;
	bool with_timespan = include_timespan;

	if (!include_session
			&& !include_label
			&& !include_revision
			&& !include_timespan
			&& !include_channel_config
			&& !include_format_name
			&& !include_channel
			&& !include_date) {
		with_timespan = true;
	}

	if (include_session) {
		path += filename_empty ? "" : "_";
		if (use_session_snapshot_name) {
			path += session.snap_name();
		} else {
			path += session.name();
		}
		filename_empty = false;
	}

	if (include_label) {
		path += filename_empty ? "" : "_";
		path += label;
		filename_empty = false;
	}

	if (include_revision) {
		path += filename_empty ? "" : "_";
		path += "r";
		path += to_string (revision);
		filename_empty = false;
	}

	if (with_timespan && timespan) {
		path += filename_empty ? "" : "_";
		path += timespan->name();
		filename_empty = false;
	}

	if (include_channel_config && channel_config) {
		path += filename_empty ? "" : "_";
		path += channel_config->name();
		filename_empty = false;
	}

	if (include_channel) {
		path += filename_empty ? "" : "_";
		path += "channel";
		path += to_string (channel);
		filename_empty = false;
	}

	if (include_date) {
		path += filename_empty ? "" : "_";
		path += get_date_format_str (date_format);
		filename_empty = false;
	}

	if (include_time) {
		path += filename_empty ? "" : "_";
		path += get_time_format_str (time_format);
		filename_empty = false;
	}

	if (include_format_name && format) {
		path += filename_empty ? "" : "_";
		path += format->name();
		filename_empty = false;
	}

	if (path.empty ()) {
		path = "export";
	}

	if (format) {
		path += ".";
		if (channel_config && channel_config->get_channels().size () == 1 && channel_config->get_channels().front()->midi ()) {
			path += "mid";
		} else {
			path += format->extension ();
		}
	}

	path = legalize_for_universal_path (path);

#if 0
	std::cout << "ExportFilename::get_path"
		<< " SN: " << include_session
		<< " LB: " << include_label
		<< " RV: " << include_revision
		<< " TS: " << include_timespan
		<< " CC: " << include_channel_config
		<< " FN: " << include_format_name
		<< " CN: " << include_channel
		<< " DT: " << include_date
		<< " '" << path << "'\n";
#endif

	return Glib::build_filename (folder, path);
}

string
ExportFilename::get_time_format_str (TimeFormat format) const
{
	switch ( format ) {
	  case T_None:
		return _("No Time");

	  case T_NoDelim:
		return get_formatted_time ("%H%M");

	  case T_Delim:
		return get_formatted_time ("%H.%M");

	  default:
		return _("Invalid time format");
	}
}

string
ExportFilename::get_date_format_str (DateFormat format) const
{
	switch (format) {
	  case D_None:
		return _("No Date");

	  case D_BE:
		return get_formatted_time ("%Y%m%d");

	  case D_ISO:
		return get_formatted_time ("%Y-%m-%d");

	  case D_BEShortY:
		return get_formatted_time ("%y%m%d");

	  case D_ISOShortY:
		return get_formatted_time ("%y-%m-%d");

	  default:
		return _("Invalid date format");
	}
}

void
ExportFilename::set_time_format (TimeFormat format)
{
	time_format = format;

	if (format == T_None) {
		include_time = false;
	} else {
		include_time = true;
	}
}

void
ExportFilename::set_date_format (DateFormat format)
{
	date_format = format;

	if (format == D_None) {
		include_date = false;
	} else {
		include_date = true;
	}
}

void
ExportFilename::set_label (string value)
{
	label = value;
	include_label = !value.compare ("");
}

bool
ExportFilename::set_folder (string path)
{
	// TODO check folder existence
	folder = path;
	return true;
}

string
ExportFilename::get_formatted_time (string const & format) const
{
	char buffer [80];
	strftime (buffer, 80, format.c_str(), &time_struct);

	string return_value (buffer);
	return return_value;
}

void
ExportFilename::add_field (XMLNode * node, string const & name, bool enabled, string const & value)
{
	XMLNode * child = node->add_child ("Field");

	if (!child) {
		std::cerr << "Error adding a field to ExportFilename XML-tree" << std::endl;
		return;
	}

	child->set_property ("name", name);
	child->set_property ("enabled", enabled);
	if (!value.empty()) {
		child->set_property ("value", value);
	}
}

ExportFilename::FieldPair
ExportFilename::get_field (XMLNode const & node, string const & name)
{
	FieldPair pair;
	pair.first = false;

	XMLNodeList children = node.children();

	for (XMLNodeList::iterator it = children.begin(); it != children.end(); ++it) {
		std::string str;
		if ((*it)->get_property ("name", str) && name == str) {

			(*it)->get_property ("enabled", pair.first);
			(*it)->get_property ("value", pair.second);

			return pair;
		}
	}

	return pair;
}

ExportFilename::FieldPair
ExportFilename::analyse_folder () const
{
	FieldPair pair;

	string session_dir = session.session_directory().root_path();
	string::size_type session_dir_len = session_dir.length();

	string folder_beginning = folder.substr (0, session_dir_len);

	if (!folder_beginning.compare (session_dir)) {
		pair.first = true;
		// remove the leading slash if needed. 
		pair.second = folder.substr (folder.length() > session_dir_len ? session_dir_len+1 : session_dir_len);
	} else {
		pair.first = false;
		pair.second = folder;
	}

	return pair;
}

} // namespace ARDOUR
