/*
 *	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 "videoplayer.h"
#include "utility.h"
#include "documentsystem.h"
#include "subtitleeditorwindow.h"
#include "gstplayer.h"

/*
 * Player Controls Widgets
 */
class PlayerControls : public Gtk::HBox
{
public:

	PlayerControls(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
	:Gtk::HBox(cobject)
	{
		m_current_seek = false;
		m_player = NULL;

		builder->get_widget("button-play", m_button_play);
		builder->get_widget("button-pause", m_button_pause);

		builder->get_widget("hscale-seek", m_hscale_seek);

		m_hscale_seek->set_range(0, 1);
		m_hscale_seek->set_value(0);
		m_hscale_seek->set_increments(0.05, 0.05);//get_adjustment()->set_step_increment(0.05);
		//m_hscale_seek->set_update_policy(Gtk::UPDATE_CONTINUOUS);
		m_hscale_seek->set_value_pos(Gtk::POS_RIGHT);
		
		m_hscale_seek->signal_format_value().connect(
				sigc::mem_fun(*this, &PlayerControls::position_to_string));

		m_connection_scale_changed = m_hscale_seek->signal_value_changed().connect(
				sigc::mem_fun(*this, &PlayerControls::on_seek_value_changed));

		m_hscale_seek->signal_event().connect(
				sigc::mem_fun(*this, &PlayerControls::on_seek_event), true);

		// signal play/pause directly connect to the player
		m_button_play->signal_clicked().connect(
			sigc::mem_fun(*this, &PlayerControls::on_play));

		m_button_pause->signal_clicked().connect(
			sigc::mem_fun(*this, &PlayerControls::on_pause));

		show_all();
			
		m_button_pause->hide();

		set_sensitive(false);
	}

	/*
	 */
	void on_seek_value_changed()
	{
		if(m_current_seek)
			return;
		// convert pos (as percentage) to position in the stream
		double pos = m_hscale_seek->get_value();
		m_player->seek(long(m_player->get_duration() * pos));
	}

	/*
	 */
	void on_play()
	{
		if(m_player->is_playing() == false)
		{
			m_player->seek(m_player->get_position());
			m_player->play();
		}
	}

	/*
	 */
	void on_pause()
	{
		m_player->pause();
	}

	/*
	 */
	void init_with_player(Player *player)
	{
		m_player = player;

		m_player->signal_message().connect(
				sigc::mem_fun(*this, &PlayerControls::on_player_message));

		m_player->signal_tick().connect(
				sigc::mem_fun(*this, &PlayerControls::on_player_tick));
	}

	/*
	 */
	void on_player_message(Player::Message msg)
	{
		switch(msg)
		{
		case Player::STATE_NONE:
			{
				m_hscale_seek->set_value(0);
				m_button_play->show();
				m_button_pause->hide();
				set_sensitive(false);
			} break;
		case Player::STATE_PAUSED:
			{
				m_button_play->show();
				m_button_pause->hide();
				set_sensitive(true);
			} break;
		case Player::STATE_PLAYING:
			{
				m_button_play->hide();
				m_button_pause->show();
				set_sensitive(true);
			} break;
		case Player::STREAM_DURATION_CHANGED:
		case Player::STREAM_READY:
			{
				long pos = m_player->get_position();
				long dur = m_player->get_duration();
				double perc = (dur == 0) ? 0 : (double)pos / dur;
				set_seek_position(perc);
			} break;
		default:
			break;
		}
	}

	/*
	 */
	void on_player_tick(long /*current_time*/, long /*stream_length*/, double current_position)
	{
		m_connection_scale_changed.block();
		set_seek_position(current_position);
		m_connection_scale_changed.unblock();
	}

	/*
	 */
	void set_seek_position(double position)
	{
		if(m_current_seek == false && m_hscale_seek->get_value() != position)
		{
			m_hscale_seek->set_value(position);
			m_hscale_seek->queue_draw();
		}
	}

protected:

	/*
	 */
	Gtk::Button* create_button(Gtk::StockID stock)
	{
		Gtk::Button *button = manage(new Gtk::Button);
		button->set_relief(Gtk::RELIEF_NONE);

		Gtk::Image *image = manage(new Gtk::Image(stock, Gtk::ICON_SIZE_MENU));

		button->add(*image);
		return button;
	}

	/*
	 */
	Glib::ustring position_to_string(double value)
	{
		// convert pos (as percentage) to position in the stream
		return SubtitleTime(long(m_player->get_duration() * value)).str();
	}

	/*
	 */
	bool on_seek_event(GdkEvent *ev)
	{
		if(ev->type == GDK_BUTTON_PRESS)
		{
			m_current_seek = true;
		}
		else if(ev->type == GDK_BUTTON_RELEASE)
		{
			// convert pos (as percentage) to position in the stream
			double pos = m_hscale_seek->get_value();
			m_player->seek(long(m_player->get_duration() * pos));

			m_current_seek = false;
		}
		return false;
	}
	
protected:
	// controls
	Gtk::Button* m_button_play;
	Gtk::Button* m_button_pause;
	Gtk::Scale* m_hscale_seek;
	bool m_current_seek;
	sigc::connection m_connection_scale_changed;
	Player* m_player;
};



/*
 * Constructor
 *
 * Create the GStreamer Player, the PlayerControls (play/pause + seek)
 */
VideoPlayer::VideoPlayer(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
:Gtk::VBox(cobject)
{
	m_cfg_display_translated_subtitle = false;

	m_player = manage(new GstPlayer);

	Gtk::Frame* m_framePlayer = NULL;
	PlayerControls* m_playerControls = NULL;

	builder->get_widget("frame-player", m_framePlayer);
	builder->get_widget_derived("player-controls", m_playerControls);

	m_framePlayer->add(*dynamic_cast<Gtk::Widget*>(m_player));
	m_playerControls->init_with_player(m_player);

	load_config();

	Config::getInstance().signal_changed("video-player").connect(
			sigc::mem_fun(*this, &VideoPlayer::on_config_video_player_changed));

	DocumentSystem::getInstance().signal_current_document_changed().connect(
			sigc::mem_fun(*this, &VideoPlayer::on_current_document_changed));

	m_player->signal_tick().connect(
			sigc::mem_fun(*this, &VideoPlayer::on_player_tick));

	m_player->signal_message().connect(
			sigc::mem_fun(*this, &VideoPlayer::on_player_message));
}

/*
 * Destructor
 */
VideoPlayer::~VideoPlayer()
{
	se_debug(SE_DEBUG_VIDEO_PLAYER);
}

/*
 * Load the video player config.
 */
void VideoPlayer::load_config()
{
	Config &cfg = Config::getInstance();

	if(cfg.get_value_bool("video-player", "display"))
		show();
	else
		hide();

	m_player->set_repeat(cfg.get_value_bool("video-player", "repeat"));

	m_cfg_display_translated_subtitle = cfg.get_value_bool("video-player", "display-translated-subtitle");
}

/*
 * Return the gstreamer player.
 */
Player* VideoPlayer::player()
{
	return m_player;
}

/*
 * The player state has changed. 
 * Clear subtitle.
 */
void VideoPlayer::on_player_message(Player::Message msg)
{
	switch(msg)
	{
	case Player::STATE_NONE:
	case Player::STATE_PAUSED:
	case Player::STATE_PLAYING:
		clear_subtitle();
		break;
	default:
		break;
	}
}

/*
 * The config of video player has changed.
 */
void VideoPlayer::on_config_video_player_changed(const Glib::ustring &key, const Glib::ustring &value)
{
	if(key == "display")
	{
		bool state = utility::string_to_bool(value);

		if(state)
			show();
		else
			hide();
	}
	else if(key == "display-translated-subtitle")
	{
		m_cfg_display_translated_subtitle = utility::string_to_bool(value);
	}
}

/*
 * The current document has changed.
 * Clear subtitle (sub and player text) and try to found the good subtitle.
 */
void VideoPlayer::on_current_document_changed(Document *doc)
{
	m_connection_document_changed.disconnect();
	if(doc != NULL)
		m_connection_document_changed = doc->get_signal("document-changed").connect(
				sigc::mem_fun(*this, &VideoPlayer::clear_subtitle));

	clear_subtitle();
	find_subtitle();
}

/*
 * Check or search the good subtitle (find_subtitle).
 */
void VideoPlayer::on_player_tick(long /*current_time*/, long /*stream_length*/, double /*current_position*/)
{
	find_subtitle();
}

/*
 * Initialize the current subtitle and the player text to NULL.
 */
void VideoPlayer::clear_subtitle()
{
	m_subtitle = Subtitle();
	
	m_player->set_subtitle_text("");
}

/*
 * Check if time is in subtitle.
 */
bool VideoPlayer::is_good_subtitle(const Subtitle &sub, long time)
{
	if(sub)
		if(time >= sub.get_start().totalmsecs && time < sub.get_end().totalmsecs)
			return true;
		
	return false;
}

/*
 * Try to found the good subtitle and init the player (text).
 * FIXME: this's call each time
 */
bool VideoPlayer::find_subtitle()
{
	Document *doc = SubtitleEditorWindow::get_instance()->get_current_document();
	if(doc == NULL)
	{
		clear_subtitle();
		return false;
	}

	long position = m_player->get_position();

	SubtitleTime time(position);

	if(!m_subtitle)
	{
		m_subtitle = doc->subtitles().find(time);
		if(m_subtitle)
			show_subtitle_text();
		else
			show_subtitle_null();
	}
	else
	{
		if(is_good_subtitle(m_subtitle, position)) // is good ?
		{
			show_subtitle_text();
		}
		else if(time < m_subtitle.get_start()) // is the next ?
		{
			show_subtitle_null();
		}
		else if(time >= m_subtitle.get_end()) // it's the old, try with the next subtitle
		{
			show_subtitle_null();

			Subtitle next = doc->subtitles().get_next(m_subtitle);
			if(next)
			{
				m_subtitle = next;
				if(is_good_subtitle(m_subtitle, position))
					show_subtitle_text();
			}
			else
				m_subtitle = Subtitle();
		}
	}
	return true;
}

/*
 * Sets the text of the player to NULL.
 */
void VideoPlayer::show_subtitle_null()
{
	m_player->set_subtitle_text("");
}

/*
 * Sets the text of the player with the current subtitle.
 */
void VideoPlayer::show_subtitle_text()
{
	if(!m_subtitle)
		return;
		
	Glib::ustring text;
		
	if(m_cfg_display_translated_subtitle && !m_subtitle.get_translation().empty())
		text = m_subtitle.get_translation();
	else
		text = m_subtitle.get_text();

	m_player->set_subtitle_text(text);
}


