/*
 *	subtitleeditor -- a tool to create or edit subtitle
 *
 *	https://kitone.github.io/subtitleeditor/
 *	https://github.com/kitone/subtitleeditor/
 *
 *	Copyright @ 2005-2009, kitone
 *
 *	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 3 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, see <http://www.gnu.org/licenses/>.
 */
 
#include <extension/action.h>
#include <utility.h>
#include <gtkmm_utility.h>
#include <gui/dialogutility.h>
#include <memory>
#include <gui/comboboxtextcolumns.h>

/*
 * TODO Add FPS finder
 *
 * (new_sub_time x fps_video) / old_sub_time = fps_src
 */

class DialogChangeFramerate : public DialogActionMultiDoc
{
	/*
	 */
	class ComboBoxEntryText : public Gtk::ComboBoxText
	{
	public:
		ComboBoxEntryText(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& )
		:Gtk::ComboBoxText(cobject)
		{
			get_entry()->signal_focus_out_event().connect(
					sigc::mem_fun(*this, &ComboBoxEntryText::on_focus_out));
		}

		/*
		 * Add new value to the combobox, only if it doesn't already there
		 */
		void append_text(const Glib::ustring &text)
		{
			Gtk::TreeNodeChildren rows = get_model()->children();
			
			ComboBoxTextColumns m_columns;

			for(Gtk::TreeIter it = rows.begin(); it; ++it)
			{
				if((*it)[m_columns.m_col_name] == text)
				{
					set_active(it);
					return;
				}
			}
			Gtk::ComboBoxText::append(text);
		}

		/*
		 * Add the value if it's a number when the focus out
		 */
		bool on_focus_out(GdkEventFocus*)
		{
			Glib::ustring text = get_entry()->get_text();

			double value = 0;
			if(from_string(text, value))
			{
				if(value > 0)
					append_text(to_string(value));
				else
					set_active(0);
			}
			else
				set_active(0);

			return true;
		}
	};
public:

	/*
	 *
	 */
	DialogChangeFramerate(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& xml)
	:DialogActionMultiDoc(cobject, xml)
	{
		utility::set_transient_parent(*this);

		xml->get_widget_derived("combo-src", m_comboSrc);
		xml->get_widget_derived("combo-dest", m_comboDest);

		m_comboSrc->get_entry()->signal_activate().connect(
				sigc::bind<ComboBoxEntryText*>(
					sigc::mem_fun(*this, &DialogChangeFramerate::combo_activate), m_comboSrc));
		m_comboDest->get_entry()->signal_activate().connect(
				sigc::bind<ComboBoxEntryText*>(
					sigc::mem_fun(*this, &DialogChangeFramerate::combo_activate), m_comboDest));

		m_comboSrc->append_text(to_string(23.976));
		m_comboSrc->append_text(to_string(24.000));
		m_comboSrc->append_text(to_string(25.000));
		m_comboSrc->append_text(to_string(29.970));

		m_comboDest->append_text(to_string(23.976));
		m_comboDest->append_text(to_string(24.000));
		m_comboDest->append_text(to_string(25.000));
		m_comboDest->append_text(to_string(29.970));

		m_comboSrc->set_active(0);
		m_comboDest->set_active(1);

		set_default_response(Gtk::RESPONSE_OK);
	}

	/*
	 */
	void execute()
	{
		show();

		if(run() == Gtk::RESPONSE_OK)
		{
			DocumentList docs;

			if(apply_to_all_documents())
				docs = SubtitleEditorWindow::get_instance()->get_documents();
			else
			{
				Document *doc = SubtitleEditorWindow::get_instance()->get_current_document();
				docs.push_back(doc);
			}

			double src = get_value(m_comboSrc);
			double dest = get_value(m_comboDest);
			
			if(src != 0 && dest != 0)
			{
				for(DocumentList::const_iterator it = docs.begin(); it!= docs.end(); ++it)
				{
					signal_change_framerate(*it, src, dest);
				}
			}
		}

		hide();
	}

	/*
	 */
	sigc::signal<void, Document*, double, double> signal_change_framerate;
protected:

	/*
	 */
	double get_value(ComboBoxEntryText *combo)
	{
		Glib::ustring text = combo->get_entry()->get_text();
		
		double value = 0;

		if(from_string(text, value))
			return value;
		return 0;
	}

	/*
	 */
	void combo_activate(ComboBoxEntryText *combo)
	{
		Glib::ustring text = combo->get_entry()->get_text();

		double value = 0;
		if(from_string(text, value))
		{
			if(value > 0)
			{
				combo->append_text(to_string(value));
				combo->set_active_text(to_string(value));
				return;
			}
		}

		combo->set_active(0);
	}
protected:
	ComboBoxEntryText* m_comboSrc;
	ComboBoxEntryText* m_comboDest;
};



/*
 *
 */
class ChangeFrameratePlugin : public Action
{
public:

	ChangeFrameratePlugin()
	{
		activate();
		update_ui();
	}

	~ChangeFrameratePlugin()
	{
		deactivate();
	}

	/*
	 *
	 */
	void activate()
	{
		se_debug(SE_DEBUG_PLUGINS);

		// actions
		action_group = Gtk::ActionGroup::create("ChangeFrameratePlugin");

		action_group->add(
				Gtk::Action::create("change-framerate", Gtk::Stock::CONVERT, _("Change _Framerate"), _("Convert framerate")),
					sigc::mem_fun(*this, &ChangeFrameratePlugin::on_execute));

		// ui
		Glib::RefPtr<Gtk::UIManager> ui = get_ui_manager();

		ui_id = ui->new_merge_id();

		ui->insert_action_group(action_group);

		ui->add_ui(ui_id, "/menubar/menu-timings/change-framerate", "change-framerate", "change-framerate");
	}

	/*
	 *
	 */
	void deactivate()
	{
		se_debug(SE_DEBUG_PLUGINS);

		Glib::RefPtr<Gtk::UIManager> ui = get_ui_manager();

		ui->remove_ui(ui_id);
		ui->remove_action_group(action_group);
	}

	/*
	 *
	 */
	void update_ui()
	{
		se_debug(SE_DEBUG_PLUGINS);

		bool visible = (get_current_document() != NULL);

		action_group->get_action("change-framerate")->set_sensitive(visible);
	}

protected:

	/*
	 *
	 */
	void on_execute()
	{
		se_debug(SE_DEBUG_PLUGINS);

		Document *doc = get_current_document();
		g_return_if_fail(doc);

		// create dialog
		std::unique_ptr<DialogChangeFramerate> dialog(
				gtkmm_utility::get_widget_derived<DialogChangeFramerate>(
						SE_DEV_VALUE(SE_PLUGIN_PATH_UI, SE_PLUGIN_PATH_DEV), 
						"dialog-change-framerate.ui", 
						"dialog-change-framerate"));

		dialog->signal_change_framerate.connect(
				sigc::mem_fun(*this, &ChangeFrameratePlugin::change_framerate));

		dialog->execute();
	}

	/*
	 *
	 */
	void change_framerate(Document *doc, double src_fps, double dest_fps)
	{
		se_debug(SE_DEBUG_PLUGINS);

		g_return_if_fail(doc);
		
		doc->start_command(_("Change Framerate"));

		Subtitles subtitles = doc->subtitles();
	
		Subtitle subtitle = subtitles.get_first();

		while(subtitle)
		{
			SubtitleTime start = change_fps(subtitle.get_start(), src_fps, dest_fps);
			SubtitleTime end = change_fps(subtitle.get_end(), src_fps, dest_fps);

			subtitle.set_start_and_end(start, end);

			++subtitle;
		}
	
		doc->emit_signal("subtitle-time-changed");
		doc->finish_command();

		doc->flash_message(_("The new framerate was applied. (%s to %s)"), to_string(src_fps).c_str(), to_string(dest_fps).c_str());
	}

	/*
	 *
	 */
	SubtitleTime change_fps(const SubtitleTime &time, double src, double dest)
	{
		se_debug(SE_DEBUG_PLUGINS);

		double frame = time.totalmsecs * src;
		double tot_sec = frame / dest;

		return SubtitleTime((long)tot_sec);
	}

protected:
	Gtk::UIManager::ui_merge_id ui_id;
	Glib::RefPtr<Gtk::ActionGroup> action_group;
};

REGISTER_EXTENSION(ChangeFrameratePlugin)
