//
// SlideWords plugin Copyright 2022 Tomáš Pártl, tomaspartl@centrum.cz.
// This file is a part of subtitleeditor Copyright @ 2005-2022, kitone.
//
// https://kitone.github.io/subtitleeditor/
// https://github.com/kitone/subtitleeditor/
//
// This plugin allows the user to slide words between lines and subtitles.
//
// 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 <debug.h>
#include <extension/action.h>
#include <i18n.h>
#include <utility.h>

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

 	~SlideWordsPlugin()
	{
		deactivate();
	}

	/*
	 */
	void activate()
	{
		se_dbg(SE_DBG_PLUGINS);

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

    action_group->add(Gtk::Action::create(
        "menu-slide-words", _("Slide Words")));

		action_group->add(
				Gtk::Action::create("slide-word-next-line", _("Slide Word To Next Line"),
				_("Slides one word from the end of the first line to the beginning of the second one.")),
					sigc::mem_fun(*this, &SlideWordsPlugin::on_slide_word_next_line));

		action_group->add(
				Gtk::Action::create("slide-word-prev-line", _("Slide Word To Previous Line"),
				_("Slides one word from the beginning of the second line to the end of the first one.")),
					sigc::mem_fun(*this, &SlideWordsPlugin::on_slide_word_prev_line));

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

    ui->insert_action_group(action_group);

    Glib::ustring submenu = R"(
      <ui>
        <menubar name='menubar'>
          <menu name='menu-edit' action='menu-edit'>
            <placeholder name='slide-words'>
              <menu action='menu-slide-words'>
                <menuitem action='slide-word-next-line'/>
                <menuitem action='slide-word-prev-line'/>
              </menu>
            </placeholder>
          </menu>
        </menubar>
      </ui>
    )";

    ui_id = ui->add_ui_from_string(submenu);
	}

	/*
	 */
	void deactivate()
	{
		se_dbg(SE_DBG_PLUGINS);

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

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

	/*
	 */
	void update_ui()
	{
		se_dbg(SE_DBG_PLUGINS);

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

		action_group->get_action("menu-slide-words")->set_sensitive(visible);
	}

protected:

	void on_slide_word_next_line()
	{
		slide_word_x_line( true ); 
	}

	void on_slide_word_prev_line()
	{
		slide_word_x_line( false ); 
	}

	void slide_word_x_line( bool next )
	{
		se_dbg(SE_DBG_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( next ? _("Slide one word to the next line") : _("Slide one word to the previous line"));

		if( selection.size() == 1 ) {
	      Glib::ustring text = selection[0].get_text();
	      if( next ) {
	      	slide_word_next_line( text );
	      } else {
	      	slide_word_prev_line( text );
	      }
	      selection[0].set_text(text);
		} else if( selection.size() == 2 ) {
	    Glib::ustring text1 = selection[0].get_text();
	    Glib::ustring text2 = selection[1].get_text();
	    if( next ) {
	    	slide_word_next_text( text1, text2 );
	    } else {
	    	slide_word_prev_text( text1, text2 );
	    }
	    selection[0].set_text(text1);
	    selection[1].set_text(text2);
		} else {
	    for (auto& subtitle : selection) {
	      Glib::ustring text = subtitle.get_text();
	      if( next ) {
	      	slide_word_next_line( text );
	      } else {
	      	slide_word_prev_line( text );
	      }
	      subtitle.set_text(text);
	    }
		}

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

	void slide_word_next_line( Glib::ustring &text ) {
		size_t end = text.find('\n');
		size_t space = text.rfind(' ', end);
	
		if( space != Glib::ustring::npos ) {
			text.replace( space, 1, "\n" );
		}
		if( end != Glib::ustring::npos ) {
			text.replace( end, 1, " " );
		}
	}

	void slide_word_prev_line( Glib::ustring &text ) {

		size_t end = 0;
		while( end == 0 ) {
			end = text.find('\n');
			if( end == 0 ) {
				text.erase( 0, 1 );
			}
		}

		size_t space = text.find(' ', ( end == Glib::ustring::npos ) ? 0 : end);

		if( space != Glib::ustring::npos ) {
			text.replace( space, 1, "\n" );
		}
		if( end != Glib::ustring::npos ) {
			text.replace( end, 1, " " );
		}
	}

	void slide_word_next_text( Glib::ustring &text1, Glib::ustring &text2 ) {
		if( text1.size() == 0 ) { return; }

		size_t end = text1.rfind('\n');
		while(( end != Glib::ustring::npos )&&( end == text1.size() - 1 )) {
			text1.erase( end, 1 );
			end = text1.rfind('\n');
		}
		size_t space = text1.find_last_of(" \n\t");
		size_t start = space + 1;
		if( space == Glib::ustring::npos ) {
			start = 0;
			space = 0;
		}

		bool addspace = (( text2.size() == 0 )||( text2[0] == ' ' )||( text2[0] == '\n' )) ? false : true;
		if( addspace ) {
			text2.insert(0, " ");
		}
		text2.insert( 0, text1, start, Glib::ustring::npos );
		text1.erase( space );
	}

	void slide_word_prev_text( Glib::ustring &text1, Glib::ustring &text2 ) {
		size_t space = text2.find_first_of(" \n\t");
		while( space == 0 ) {
			text2.erase( 0, 1 );
			space = text2.find(' ');
		}

		if( text2.size() == 0 ) { return; }

		size_t end = space;
		size_t erase = space;
		if( space != Glib::ustring::npos ) {
			erase = space + 1;
		}

		bool addspace = (( text1.size() == 0 )||( text1[ text1.size() - 1 ] == ' ' )||( text2[ text1.size() - 1 ] == '\n' )) ? false : true;
		if( addspace ) {
			text1.append(" ");
		}
		text1.append( text2, 0, end );
		text2.erase( 0, erase );
	}

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

REGISTER_EXTENSION(SlideWordsPlugin)
