/*  This file is part of the KDE project
    Copyright (C) 2007 Matthias Kretz <kretz@kde.org>

    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 2 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, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.

*/

#include <kapplication.h>
#include <kmessagebox.h>
#include <kdebug.h>
#include <kdialog.h>
#include <kurl.h>
#include <kcmdlineargs.h>
#include <klocale.h>
#include <kwindowsystem.h>
#include <Phonon/AudioOutput>
#include <Phonon/AudioOutputDevice>
#include <Phonon/AudioOutputDeviceModel>
#include <Phonon/BackendCapabilities>
#include <Phonon/MediaObject>
#include <Phonon/VideoPlayer>
#include <Phonon/VolumeSlider>
#include <Phonon/VideoWidget>
#include <QtCore/QFile>
#include <QtCore/QSocketNotifier>
#include <QtGui/QKeyEvent>
#include <QtGui/QListView>
#include <termios.h>
#include "bufferprogress.h"

class MainWindow : public Phonon::VideoPlayer
{
    Q_OBJECT
    public:
        MainWindow(const KUrl &url)
            : Phonon::VideoPlayer(Phonon::NoCategory)
        {
            this->installEventFilter(this);
            this->resize(this->sizeHint());

            load(url);

            connect(this, SIGNAL(finished()), qApp, SLOT(quit()));

            setWindowTitle(KDialog::makeStandardCaption(url.fileName(), this));
            if (mediaObject()->hasVideo()) {
                show();
            }
            connect(mediaObject(), SIGNAL(hasVideoChanged(bool)), SLOT(setVisible(bool)));
            connect(mediaObject(), SIGNAL(stateChanged(Phonon::State, Phonon::State)), SLOT(handleStateChange(Phonon::State)));

            QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection);

            struct termios term;
            tcgetattr(fileno(stdin), &term);
            term.c_lflag &= ~(ECHO | ECHONL | ICANON);
            tcsetattr(fileno(stdin), TCSANOW, &term);
            m_keyIn.open(stdin, QIODevice::ReadOnly | QIODevice::Unbuffered);

            // readyRead signal doesn't work so use a QSocketNotifier instead which notifies us for
            // every char available (almost)
            QSocketNotifier *n = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read, this);
            connect(n, SIGNAL(activated(int)), SLOT(keyPressed()));
        }

    protected:
        virtual bool eventFilter(QObject *o, QEvent *e)
        {
            if (o == this && e->type() == QEvent::LayoutRequest) {
                //kDebug() << "resize(" << sizeHint() << ");";
                resize(sizeHint());
            }
            return Phonon::VideoPlayer::eventFilter(o, e);
        }

        virtual void keyPressEvent(QKeyEvent *e)
        {
            switch (e->key()) {
            case Qt::Key_Up:
                mediaObject()->seek(mediaObject()->currentTime() + 10000ll);
                break;
            case Qt::Key_Down:
                mediaObject()->seek(qMax(0ll, mediaObject()->currentTime() - 10000ll));
                break;
            case Qt::Key_Right:
                mediaObject()->seek(mediaObject()->currentTime() + 2000ll);
                break;
            case Qt::Key_Left:
                mediaObject()->seek(qMax(0ll, mediaObject()->currentTime() - 2000ll));
                break;
            case Qt::Key_D:
                audioDeviceDialog();
                break;
            case Qt::Key_Q:
                QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
                break;
            case Qt::Key_V:
                volumeDialog();
                break;
            case Qt::Key_F:
                setWindowState(windowState() ^ Qt::WindowFullScreen);
                break;
            }
        }

    private slots:
        void handleStateChange(Phonon::State newstate)
        {
            switch (newstate) {
            case Phonon::ErrorState:
                kError() << mediaObject()->errorString();
                KMessageBox::error(this, mediaObject()->errorString());
                QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
                break;
            case Phonon::BufferingState:
                new BufferProgress(mediaObject());
                break;
            default:
                break;
            }
        }

        void keyPressed()
        {
            char key;
            if (1 == m_keyIn.read(&key, 1)) {
                //kDebug() << (int)key;
                switch (key) {
                    case 27:
                        char sequence[3];
                        sequence[2] = '\0';
                        if (2 == m_keyIn.read(sequence, 2)) {
                            //kDebug() << "escape sequence:" << sequence;
                            switch (sequence[0]) {
                            case '[':
                                switch (sequence[1]) {
                                case 'A': // arrow up
                                    mediaObject()->seek(mediaObject()->currentTime() + 10000ll);
                                    break;
                                case 'B': // arrow down
                                    mediaObject()->seek(qMax(0ll, mediaObject()->currentTime() - 10000ll));
                                    break;
                                case 'C': // arrow right
                                    mediaObject()->seek(mediaObject()->currentTime() + 2000ll);
                                    break;
                                case 'D': // arrow left
                                    mediaObject()->seek(qMax(0ll, mediaObject()->currentTime() - 2000ll));
                                    break;
                                }
                                break;
                            }
                        }
                        break;
                    case 'd':
                    case 'D':
                        audioDeviceDialog();
                        break;
                    case 'q':
                    case 'Q':
                        QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
                        break;
                    case 'V':
                    case 'v':
                        volumeDialog();
                        break;
                }
            }
        }

    private:
        void volumeDialog()
        {
            static KDialog *dialog = 0;
            if (dialog) {
                KWindowSystem::forceActiveWindow(dialog->winId());
                return;
            }
            dialog = new KDialog;
            dialog->setAttribute(Qt::WA_QuitOnClose, false);
            dialog->setCaption(i18n("Change Volume"));
            dialog->setButtons(KDialog::Ok|KDialog::Cancel);

            qreal oldVolume = audioOutput()->volume();
            Phonon::VolumeSlider *volumeSlider = new Phonon::VolumeSlider(dialog);
            volumeSlider->setMuteVisible(false);
            volumeSlider->setOrientation(Qt::Horizontal);
            volumeSlider->setAudioOutput(audioOutput());
            volumeSlider->setFocus(Qt::OtherFocusReason);

            dialog->setMainWidget(volumeSlider);
            dialog->show();
            KWindowSystem::forceActiveWindow(dialog->winId());
            if (QDialog::Rejected == dialog->exec()) {
                audioOutput()->setVolume(oldVolume);
            }
            delete dialog;
            dialog = 0;
        }

        void audioDeviceDialog()
        {
            static KDialog *dialog = 0;
            if (dialog) {
                KWindowSystem::forceActiveWindow(dialog->winId());
                return;
            }
            dialog = new KDialog;
            dialog->setAttribute(Qt::WA_QuitOnClose, false);
            dialog->setCaption(i18n("Select Audio Output Device"));
            dialog->setButtons(KDialog::Ok|KDialog::Cancel);

            const QList<Phonon::AudioOutputDevice> modelData = Phonon::BackendCapabilities::availableAudioOutputDevices();
            Phonon::AudioOutputDeviceModel model;
            model.setModelData(modelData);

            QListView *listView = new QListView(dialog);
            listView->setModel(&model);
            listView->setCurrentIndex(model.index(modelData.indexOf(audioOutput()->outputDevice()), 0));
            listView->setFocus(Qt::OtherFocusReason);
            connect(listView, SIGNAL(activated(const QModelIndex &)), dialog, SLOT(accept()));

            dialog->setMainWidget(listView);
            dialog->show();
            KWindowSystem::forceActiveWindow(dialog->winId());
            if (QDialog::Accepted == dialog->exec()) {
                const QModelIndex index = listView->currentIndex();
                kDebug() << index;
                if (index.isValid()) {
                    Phonon::AudioOutputDevice dev = model.modelData(index);
                    audioOutput()->setOutputDevice(dev);
                }
            }
            delete dialog;
            dialog = 0;
        }

        QFile m_keyIn;
};

int main(int argc, char **argv)
{
    KCmdLineArgs::init(argc, argv, "phononplay", QByteArray(), ki18n("Phonon Play"), "0.1", ki18n("Plays any media file using Phonon"));
    KCmdLineOptions options;
    options.add("+url", ki18n("URL or local file path to the media to play"));
    KCmdLineArgs::addCmdLineOptions(options);
    KApplication app;

    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
    if (args->count() < 1) {
        KCmdLineArgs::usageError(i18n("you need to specify a media to play"));
    }
    KUrl url = args->url(0);
    if (!url.isValid()) {
        KCmdLineArgs::usageError(i18n("invalid URL"));
    }

    MainWindow player(url);
    for (int i = 1; i < args->count(); ++i) {
        url = args->url(i);
        if (url.isValid()) {
            player.mediaObject()->enqueue(url);
        }
    }

    app.exec();
}

#include "main.moc"
