/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2009  Georg Rudoy
 *
 * 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 "playerwidget.h"
#include <QToolBar>
#include <QStylePainter>
#include <QBitmap>
#include <QTimer>
#include <QDialog>
#include <plugininterface/util.h>
#include "phonon.h"
#include "videosettings.h"
#include "xmlsettingsmanager.h"
#include "core.h"

using namespace Phonon;

namespace LeechCraft
{
	namespace Plugins
	{
		namespace LMP
		{
			PlayerWidget::PlayerWidget (QWidget *parent)
			: QWidget (parent)
			, MediaObject_ (new MediaObject)
			{
				setObjectName ("LeechCraft::Plugins::LMP::PlayerWidget");
				MediaObject_->setTickInterval (700);
				connect (MediaObject_.get (),
						SIGNAL (tick (qint64)),
						this,
						SLOT (updateState ()));
				connect (MediaObject_.get (),
						SIGNAL (tick (qint64)),
						this,
						SLOT (updateOSD ()));
				connect (MediaObject_.get (),
						SIGNAL (stateChanged (Phonon::State,
								Phonon::State)),
						this,
						SLOT (updateState ()));
				connect (MediaObject_.get (),
						SIGNAL (hasVideoChanged (bool)),
						this,
						SLOT (handleHasVideoChanged (bool)));
				Ui_.setupUi (this);
				Ui_.VideoWidget_->installEventFilter (this);
				Ui_.ControlsLayout_->insertWidget (0, SetupToolbar ());

				Ui_.SeekSlider_->setMediaObject (MediaObject_.get ());

				AudioOutput_.reset (new AudioOutput (MusicCategory, this));

				connect (MediaObject_.get (),
						SIGNAL (finished ()),
						Ui_.VideoWidget_,
						SLOT (exitFullScreen ()));

				SetupContextMenu ();

				QBitmap bm (32, 32);
				bm.clear ();
				TransparentCursor_ = QCursor (bm, bm);
			}

			void PlayerWidget::Play ()
			{
				play ();
			}

			void PlayerWidget::Pause ()
			{
				pause ();
			}

			void PlayerWidget::Stop ()
			{
				MediaObject_->stop ();
			}

			void PlayerWidget::Clear ()
			{
				MediaObject_->clear ();
			}

			void PlayerWidget::Enqueue (const QUrl& url)
			{
				Enqueue (MediaSource (url));
			}

			void PlayerWidget::Enqueue (QIODevice *data)
			{
				Enqueue (MediaSource (data));
			}

			QWidget* PlayerWidget::Widget ()
			{
				return this;
			}

			void PlayerWidget::Enqueue (const MediaSource& source)
			{
				bool shouldStop = GetState () == Phonon::StoppedState;
				MediaObject_->enqueue (source);
				if (shouldStop)
					MediaObject_->stop ();
				Ui_.VideoWidget_->setVisible (MediaObject_->hasVideo ());
			}

			void PlayerWidget::Forward (SkipAmount a)
			{
				if (MediaObject_.get ())
					MediaObject_->seek (MediaObject_->currentTime () + a * 1000);

				updateState ();
			}

			void PlayerWidget::Rewind (SkipAmount a)
			{
				if (MediaObject_.get ())
					MediaObject_->seek (MediaObject_->currentTime () - a * 1000);

				updateState ();
			}

			State PlayerWidget::GetState () const
			{
				return MediaObject_->state ();
			}

			MediaObject* PlayerWidget::GetMediaObject () const
			{
				return MediaObject_.get ();
			}

			void PlayerWidget::play ()
			{
				if (MediaObject_.get ())
				{
					if (!VideoPath_.isValid ())
						VideoPath_.reconnect (MediaObject_.get (), Ui_.VideoWidget_);
					if (!AudioPath_.isValid ())
					{
						AudioPath_.reconnect (MediaObject_.get (), AudioOutput_.get ());
						Ui_.VolumeSlider_->setAudioOutput (AudioOutput_.get ());
					}

					MediaObject_->play ();
				}
			}

			void PlayerWidget::pause ()
			{
				if (MediaObject_.get ())
					MediaObject_->pause ();
			}

			void PlayerWidget::toggleFullScreen ()
			{
				if (!Ui_.VideoWidget_->isFullScreen ())
					QTimer::singleShot (1500,
							this,
							SLOT (hideCursor ()));
				Ui_.VideoWidget_->setFullScreen (!Ui_.VideoWidget_->isFullScreen ());
			}

			void PlayerWidget::togglePause ()
			{
				if (MediaObject_->state () == PausedState)
					Play ();
				else
					Pause ();
			}

			void PlayerWidget::incrementVolume ()
			{
				qreal nv = AudioOutput_->volume ();
				nv += 0.1;
				if (nv > 1)
					nv = 1;
				AudioOutput_->setVolume (nv);
			}

			void PlayerWidget::decrementVolume ()
			{
				qreal nv = AudioOutput_->volume ();
				nv -= 0.1;
				if (nv < 0)
					nv = 0;
				AudioOutput_->setVolume (nv);
			}

			QToolBar* PlayerWidget::SetupToolbar ()
			{
				QToolBar *bar = new QToolBar (this);

				Play_ = new QAction (tr ("Play"),
						this);
				Play_->setObjectName ("Play_");
				Play_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_play"));
				connect (Play_,
						SIGNAL (triggered ()),
						this,
						SLOT (play ()));

				Pause_ = new QAction (tr ("Pause"),
						this);
				Pause_->setObjectName ("Pause_");
				Pause_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_pause"));
				connect (Pause_,
						SIGNAL (triggered ()),
						this,
						SLOT (pause ()));

				ViewerSettings_ = new QAction (tr ("Viewer settings"),
						this);
				ViewerSettings_->setObjectName ("ViewerSettings_");
				ViewerSettings_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_viewersettings"));
				connect (ViewerSettings_,
						SIGNAL (triggered ()),
						this,
						SLOT (changeViewerSettings ()));

				ApplyVideoSettings (XmlSettingsManager::Instance ()->
							Property ("Brightness", 0).value<qreal> (),
						XmlSettingsManager::Instance ()->Property ("Contrast", 0).value<qreal> (),
						XmlSettingsManager::Instance ()->Property ("Hue", 0).value<qreal> (),
						XmlSettingsManager::Instance ()->Property ("Saturation", 0).value <qreal> ());

				bar->addAction (Play_);
				bar->addAction (Pause_);
				bar->addSeparator ();
				bar->addAction (ViewerSettings_);

				return bar;
			}

			void PlayerWidget::SetupContextMenu ()
			{
				FullScreen_ = new QAction (tr ("Toggle full screen"),
						this);
				FullScreen_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_togglefullscreen"));
				QList<QKeySequence> fullscreens;
				fullscreens << tr ("F");
				FullScreen_->setShortcuts (fullscreens);
				connect (FullScreen_,
						SIGNAL (triggered ()),
						this,
						SLOT (toggleFullScreen ()));

				TogglePause_ = new QAction (tr ("Pause/resume"),
						this);
				TogglePause_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_togglepause"));
				QList<QKeySequence> togglePauses;
				togglePauses << Qt::Key_Space
					<< tr ("P")
					<< tr ("R");
				TogglePause_->setShortcuts (togglePauses);
				connect (TogglePause_,
						SIGNAL (triggered ()),
						this,
						SLOT (togglePause ()));

				VolumeUp_ = new QAction (tr ("Volume up"),
						this);
				VolumeUp_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_volumeup"));
				QList<QKeySequence> volumeUps;
				volumeUps << QString ("=")
					<< QString ("+")
					<< QString ("*");
				VolumeUp_->setShortcuts (volumeUps);
				connect (VolumeUp_,
						SIGNAL (triggered ()),
						this,
						SLOT (incrementVolume ()));

				VolumeDown_ = new QAction (tr ("Volume down"),
						this);
				VolumeDown_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_volumedown"));
				QList<QKeySequence> volumeDowns;
				volumeDowns << QString ("-")
					<< QString ("/");;
				VolumeDown_->setShortcuts (volumeDowns);
				connect (VolumeDown_,
						SIGNAL (triggered ()),
						this,
						SLOT (decrementVolume ()));

				OSD_ = new QAction (tr ("On-screen display"),
						this);
				OSD_->setIcon (Core::Instance ()
						.GetCoreProxy ()->GetIcon ("lmp_osd"));
				QList<QKeySequence> osds;
				osds << QString ("o");
				OSD_->setShortcuts (osds);
				OSD_->setCheckable (true);
				connect (OSD_,
						SIGNAL (toggled (bool)),
						this,
						SLOT (updateOSD ()));

				Ui_.VideoWidget_->addAction (FullScreen_);
				Ui_.VideoWidget_->addAction (TogglePause_);
				Ui_.VideoWidget_->addAction (VolumeUp_);
				Ui_.VideoWidget_->addAction (VolumeDown_);
				Ui_.VideoWidget_->addAction (OSD_);
			}

			void PlayerWidget::ApplyVideoSettings (qreal b, qreal c, qreal h, qreal s)
			{
				Ui_.VideoWidget_->setBrightness (b);
				Ui_.VideoWidget_->setContrast (c);
				Ui_.VideoWidget_->setHue (h);
				Ui_.VideoWidget_->setSaturation (s);
			}

			bool PlayerWidget::eventFilter (QObject *o, QEvent *e)
			{
				if (OSD_->isChecked () &&
						e->type () == QEvent::Paint &&
						o == Ui_.VideoWidget_)
				{
					/*
					QPainter p (Ui_.VideoWidget_);
					p.setPen (QColor (Qt::white));
					p.drawText (200, 200, "Test");
					*/
				}
				else if (e->type () == QEvent::MouseMove &&
						o == Ui_.VideoWidget_)
				{
					if (Ui_.VideoWidget_->isFullScreen ())
					{
						Ui_.VideoWidget_->setCursor (QCursor ());
						QTimer::singleShot (1500,
								this,
								SLOT (hideCursor ()));
					}
				}
				return QObject::eventFilter (o, e);
			}

			void PlayerWidget::handleHasVideoChanged (bool has)
			{
				Ui_.VideoWidget_->setVisible (has);
				updateOSD ();
			}

			void PlayerWidget::updateOSD ()
			{
				if (!MediaObject_->hasVideo () &&
						!OSD_->isChecked ())
					return;

				Ui_.VideoWidget_->update ();
			}

			void PlayerWidget::updateState ()
			{
				QString result;
				switch (MediaObject_->state ())
				{
					case LoadingState:
						result = tr ("Initializing");
						break;
					case StoppedState:
						result = tr ("Stopped");
						break;
					case PlayingState:
						result = tr ("Playing");
						break;
					case BufferingState:
						result = tr ("Buffering");
						break;
					case PausedState:
						result = tr ("Paused");
						break;
					case ErrorState:
						result = tr ("Error");
						break;
				}
				if (MediaObject_->state () == ErrorState)
					result += QString (" (%1)").arg (MediaObject_->errorString ());
				result += tr (" [");
				result += QString::number (static_cast<double> (MediaObject_->
							currentTime ())/1000., 'f', 1);
				int totalTime = MediaObject_->totalTime ();
				if (totalTime)
				{
					result += tr ("/");
					result += QString::number (static_cast<double> (totalTime)/1000.,
							'f', 1);
				}
				result += tr ("]");

				result += tr (" from ");
				MediaSource source = MediaObject_->currentSource ();
				switch (source.type ())
				{
					case MediaSource::Invalid:
#if PHONON_VERSION >= PHONON_VERSION_CHECK (4, 3, 0)
					case MediaSource::Empty:
#endif
						result += tr ("nowhere");
						break;
					case MediaSource::LocalFile:
						result += source.fileName ();
						break;
					case MediaSource::Url:
						result += source.url ().toString ();
						break;
					case MediaSource::Disc:
						result += source.deviceName ();
						switch (source.discType ())
						{
							case Cd:
								result += tr (" (CD)");
								break;
							case Dvd:
								result += tr (" (DVD)");
								break;
							case Vcd:
								result += tr (" (VCD)");
								break;
							default:
								result += tr (" (Unknown disc type)");
								break;
						}
						break;
					case MediaSource::Stream:
						result += tr ("stream");
						break;
				}

				if (MediaObject_->state () == ErrorState)
					emit gotEntity (Util::MakeNotification ("LMP", result, PCritical_));
				else
					emit stateUpdated (result);
			}

			void PlayerWidget::changeViewerSettings ()
			{
				std::auto_ptr<VideoSettings> settings (new VideoSettings (Ui_.VideoWidget_->brightness (),
						Ui_.VideoWidget_->contrast (),
						Ui_.VideoWidget_->hue (),
						Ui_.VideoWidget_->saturation (),
						this));
				if (settings->exec () == QDialog::Rejected)
					return;

				qreal b = settings->Brightness (),
					  c = settings->Contrast (),
					  h = settings->Hue (),
					  s = settings->Saturation ();

				ApplyVideoSettings (b, c, h, s);

				XmlSettingsManager::Instance ()->setProperty ("Brightness", b);
				XmlSettingsManager::Instance ()->setProperty ("Contrast", c);
				XmlSettingsManager::Instance ()->setProperty ("Hue", h);
				XmlSettingsManager::Instance ()->setProperty ("Saturation", s);
			}

			void PlayerWidget::handleStateUpdated (const QString& state)
			{
				setToolTip (state);
			}

			void PlayerWidget::hideCursor ()
			{
				Ui_.VideoWidget_->setCursor (TransparentCursor_);
			}
		};
	};
};

