/*
 *	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 <i18n.h>
#include <debug.h>
#include <algorithm>


class TypewriterPlugin : public Action
{
public:
	
	TypewriterPlugin()
	{
		activate();
		update_ui();
	}

	~TypewriterPlugin()
	{
		deactivate();
	}

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

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

		action_group->add(
				Gtk::Action::create("typewriter", _("_Typewriter")));

		action_group->add(
				Gtk::Action::create("typewriter-characters-linear",  _("Characters - Linear")),
				sigc::bind(
					sigc::mem_fun(*this, &TypewriterPlugin::split_selected_subtitles), CHARACTERS, LINEAR));

		action_group->add(
				Gtk::Action::create("typewriter-characters-random",  _("Characters - Random")),
				sigc::bind(
					sigc::mem_fun(*this, &TypewriterPlugin::split_selected_subtitles), CHARACTERS, RANDOM));

		action_group->add(
				Gtk::Action::create("typewriter-words-linear",  _("Words - Linear")),
				sigc::bind(
					sigc::mem_fun(*this, &TypewriterPlugin::split_selected_subtitles), WORDS, LINEAR));
		
		action_group->add(
				Gtk::Action::create("typewriter-words-random",  _("Words - Random")),
				sigc::bind(
					sigc::mem_fun(*this, &TypewriterPlugin::split_selected_subtitles), WORDS, RANDOM));

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

		ui->insert_action_group(action_group);

		Glib::ustring submenu = 
			"<ui>"
			"	<menubar name='menubar'>"
			"		<menu name='menu-extensions' action='menu-extensions'>"
			"			<placeholder name='placeholder'>"
			"					<menu action='typewriter'>"
			"						<menuitem action='typewriter-characters-linear'/>"
			"						<menuitem action='typewriter-characters-random'/>"
			"						<separator/>"
			"						<menuitem action='typewriter-words-linear'/>"
			"						<menuitem action='typewriter-words-random'/>"
			"					</menu>"
			"			</placeholder>"
			"		</menu>"
			"	</menubar>"
			"</ui>";

		ui_id = ui->add_ui_from_string(submenu);
	}

	/*
	 */
	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("typewriter-characters-linear")->set_sensitive(visible);
		action_group->get_action("typewriter-characters-random")->set_sensitive(visible);
		action_group->get_action("typewriter-words-linear")->set_sensitive(visible);
		action_group->get_action("typewriter-words-random")->set_sensitive(visible);
	}

protected:

	enum SPLIT_TYPE
	{
		CHARACTERS,
		WORDS
	};

	enum SPLIT_TIME
	{
		LINEAR,
		RANDOM
	};

	/*
	 */
	void split_selected_subtitles(SPLIT_TYPE split_type, SPLIT_TIME split_time)
	{
		se_debug(SE_DEBUG_PLUGINS);

		Document *doc = get_current_document();

		g_return_if_fail(doc);

		Subtitles subtitles = doc->subtitles();

		std::vector<Subtitle> selection = subtitles.get_selection();
		if(selection.empty())
		{
			doc->flash_message(_("Please select at least one subtitle."));
			return;
		}

		doc->start_command(_("Split subtitles"));
		// To keep subtitles valid, we start change from the end
		for(std::vector<Subtitle>::reverse_iterator it=selection.rbegin(); it != selection.rend(); ++it)
		{
			split(subtitles, *it, split_type, split_time);
		}
		doc->emit_signal("subtitle-time-changed");
		doc->finish_command();
	}

	/*
	 */
	void split(Subtitles& subtitles, Subtitle &sub, SPLIT_TYPE type, SPLIT_TIME time)
	{
		Glib::ustring text = sub.get_text();
		if(text.empty())
			return;
		// Original values
		SubtitleTime ostart = sub.get_start();
		SubtitleTime oduration = sub.get_duration();

		// Array for new subtitles
		std::vector<Subtitle> newsubs;

		// Array for the split text (characters, word ...)
		std::vector<Glib::ustring> vtext;

		if(type == CHARACTERS)
			vtext = split_by_character(text);
		else if(type == WORDS)
			vtext = split_by_word(text);

		// Create really the subtitles
		newsubs = create_subtitles_from_text_array(subtitles, sub, vtext);

		// Setup time
		if(time == LINEAR)
			setup_time_linear(newsubs, ostart, oduration);
		else if(time == RANDOM)
			setup_time_random(newsubs, ostart, oduration);

		// We add to the selection the new subtitles
		subtitles.select(newsubs);
	}

	/*
	 */
	std::vector<Subtitle> create_subtitles_from_text_array(Subtitles &subtitles, Subtitle &original_subtitle, const std::vector<Glib::ustring> &vtext)
	{
		// Array for new subtitles
		std::vector<Subtitle> newsubs;

		// We can add directly the original subtitle, it will be reused
		// we just need to add other lines (size-1)
		newsubs.push_back(original_subtitle);

		// Create subtitle foreach text in the array 
		// start at 1 because we already add the original subtitle in the list
		for(guint c=1; c < vtext.size(); ++c)
		{
			Subtitle next = subtitles.insert_after(newsubs[c-1]);
			original_subtitle.copy_to(next); // Copy all values (style, note...)
			newsubs.push_back(next);
		}
		// Update subtitles text from vtext
		for(guint i=0; i<vtext.size(); ++i)
		{
			newsubs[i].set_text( vtext[i] );
		}
		return newsubs;
	}

	/*
	 */
	std::vector<Glib::ustring> split_by_character(const Glib::ustring &text)
	{
		std::vector<Glib::ustring> characters;
	
		characters.resize(text.size());
		for(guint i=1; i<=text.size(); ++i)
		{
			characters[i-1] = text.substr(0, i);
		}
		return characters;
	}

	/*
	 */
	std::vector<Glib::ustring> split_by_word(const Glib::ustring &text)
	{
		std::vector<Glib::ustring> splitted, words;
		// If you have a better idea, fell free to send a patch.
		splitted = Glib::Regex::split_simple("\\s", text);

		for(guint i=0; i< splitted.size(); ++i)
		{
			Glib::ustring w;
			for(guint j=0; j<=i; ++j)
			{
				if(j>0)
					w+=text.at(w.size());
				w+= splitted[j];
			}
			words.push_back(w);
		}

		return words;
	}

	/*
	 */
	void setup_time_linear(std::vector<Subtitle>& subs, const SubtitleTime &start, const SubtitleTime &duration)
	{
		// We update the time of each subtitles (linear)
		SubtitleTime s = start;
		SubtitleTime d = duration / static_cast<long>(subs.size());

		for(guint i=0; i < subs.size(); ++i)
		{
			subs[i].set_start_and_end(s, s+d);
			s = s + d; // Update the beginning of the next subtitle
		}
	}

	/*
	 */
	void setup_time_random(std::vector<Subtitle>& subs, const SubtitleTime &start, const SubtitleTime &duration)
	{
		std::vector<long> rand_times;
		// Create a random time between 0 and duration
		// and sort the random times
		Glib::Rand rand(start.totalmsecs);
		for(guint i=0; i< subs.size(); ++i)
			rand_times.push_back(rand.get_int_range(0, duration.totalmsecs));
		std::sort(rand_times.begin(), rand_times.end());

		// We update the time of each subtitles (linear)
		SubtitleTime s = start;

		for(guint i=0; i < subs.size(); ++i)
		{
			SubtitleTime e(start.totalmsecs + rand_times[i]);

			subs[i].set_start_and_end(s, e);

			s = e; // Update the beginning of the next subtitle
		}
	}

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

REGISTER_EXTENSION(TypewriterPlugin)

