/*
 * Copyright (C) 2013 Canonical, Ltd.
 *
 * 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; version 3.
 *
 * 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 "mediaplayer.h"

#include "config.h"
#include <QDebug>
#include <QDir>
#include <QGuiApplication>
#include <QLibrary>
#include <QMimeDatabase>
#include <QQmlContext>
#include <QQmlEngine>
#include <QScreen>
#include <QStandardPaths>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <libintl.h>

static void printUsage(const QStringList& arguments)
{
    qDebug() << "usage:"
             << arguments.at(0).toUtf8().constData() << "[options] uri";
    qDebug();
    qDebug() << "options:";
    qDebug() << "\t-w --windowed: start windowed";
}

MediaPlayer::MediaPlayer(int& argc, char** argv)
    : QApplication(argc, argv)
    , m_view(nullptr)
    , m_playUri(QUrl())
    , m_windowed(false)
{
}

bool MediaPlayer::setup()
{
    QStringList args = arguments();
    m_windowed = args.removeAll("-w") + args.removeAll("--windowed") > 0;
    bool testability = args.removeAll("-testability") > 0;

    // The testability driver is only loaded by QApplication but not by
    // QGuiApplication.
    // However, QApplication depends on QWidget which would add some
    // unneeded overhead => Let's load the testability driver on our own.
    if (testability) {
        QLibrary testLib(QLatin1String("qttestability"));
        if (testLib.load()) {
            typedef void (*TasInitialize)(void);
            TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
            if (initFunction) {
                initFunction();
            } else {
                qCritical("Library qttestability resolve failed!");
            }
        } else {
            qCritical("Library qttestability load failed!");
        }
    }

    // TODO: move this to SDK/ShareMenu library
    // qmlRegisterType<ShareFile>("SDKHelper", 1, 0, "ShareFile");

    m_view = new QQuickView();
    // m_view->engine()->addImageProvider("video", new ThumbnailProvider);
    m_view->setColor(QColor("black"));
    m_view->setResizeMode(QQuickView::SizeRootObjectToView);
    m_view->setTitle(gettext("Media Player"));

    // remove non url args
    QStringList newArgs(args);
    Q_FOREACH (const QString& arg, args) {
        if (arg.startsWith("--"))
            newArgs.removeOne(arg);
    }
    args = newArgs;

    if (args.count() >= 2) {
        setPlayUri(args[1]);
    }

    m_view->rootContext()->setContextProperty("mpApplication", this);
    m_view->rootContext()->setContextProperty("screenWidth", m_view->size().width());
    m_view->rootContext()->setContextProperty("screenHeight", m_view->size().height());

    connect(m_view, SIGNAL(widthChanged(int)), SLOT(onWidthChanged(int)));
    connect(m_view, SIGNAL(heightChanged(int)), SLOT(onHeightChanged(int)));
    connect(m_view->engine(), SIGNAL(quit()), SLOT(quit()));

    // Set the orientation changes that this app is interested in being signaled about
    QApplication::primaryScreen()->setOrientationUpdateMask(Qt::PortraitOrientation | Qt::LandscapeOrientation | Qt::InvertedPortraitOrientation | Qt::InvertedLandscapeOrientation);

    QUrl source(mediaPlayerDirectory() + "/qml/player.qml");
    m_view->setSource(source);
    m_view->setWidth(1200);
    m_view->setHeight(675);
    m_view->show();

    return true;
}

void MediaPlayer::setPlayUri(QUrl uri)
{
    QUrl playUri;

    if (uri.scheme() == "video" || uri.scheme() == "audio") {
        uri.setScheme("file");
    }

    if (uri.isRelative()) {
        uri = QUrl::fromLocalFile(QDir::current().absoluteFilePath(uri.toString()));
    }

    // Check if it's a local file
    if (uri.isValid() && uri.isLocalFile()) {
        QFileInfo info(uri.toLocalFile());
        if (info.exists() && info.isFile()) {
            playUri = uri;
        } else {
            qWarning() << "File not found:" << uri << info.exists() << info.isFile();
        }
        // Otherwise see if it's a remote stream
    } else if (uri.isValid()) {
        playUri = uri;
    } else {
        qWarning() << "Invalid uri:" << uri;
    }

    if (playUri != m_playUri) {
        m_playUri = playUri;
        emit playUriChanged();
    }
}

bool MediaPlayer::windowed()
{
    return m_windowed;
}

int MediaPlayer::getStoredPosition()
{
    QString fileName = m_playUri.toString().mid(m_playUri.toString().lastIndexOf("/"));
    return m_settings.value(fileName, -1).toInt();
}

MediaPlayer::~MediaPlayer()
{
    if (m_view) {
        delete m_view;
    }
    m_settings.sync();
}

void MediaPlayer::toggleFullscreen()
{
    QWindow::Visibility newVisibility = m_view->visibility() == QWindow::FullScreen ? QWindow::Windowed : QWindow::FullScreen;
    m_view->setVisibility(newVisibility);
}

void MediaPlayer::leaveFullScreen()
{
    if (m_view->visibility() == QWindow::FullScreen) {
        m_view->setVisibility(QWindow::Windowed);
    }
}

void MediaPlayer::onWidthChanged(int width)
{
    m_view->rootContext()->setContextProperty("screenWidth", width);
}

void MediaPlayer::onHeightChanged(int height)
{
    m_view->rootContext()->setContextProperty("screenHeight", height);
}

QList<QUrl> MediaPlayer::copyFiles(const QList<QUrl>& urls)
{
    static QString moviesDir = QStandardPaths::writableLocation(QStandardPaths::MoviesLocation);

    QList<QUrl> result;

    Q_FOREACH (const QUrl& url, urls) {
        if (!url.isLocalFile()) {
            qWarning() << "Remote files not supported:" << url;
            continue;
        }

        QFileInfo originalFile(url.toLocalFile());

        QString filename = originalFile.fileName();
        QString suffix = originalFile.completeSuffix();
        QString filenameWithoutSuffix;
        if (suffix.isEmpty()) {
            QMimeDatabase mdb;
            QMimeType mt = mdb.mimeTypeForFile(originalFile.absoluteFilePath());

            // If the filename doesn't have an extension add one from the
            // detected mimetype
            if (!mt.preferredSuffix().isEmpty()) {
                suffix = mt.preferredSuffix();
            }
            filenameWithoutSuffix = filename;
        } else {
            filenameWithoutSuffix = originalFile.baseName();
        }

        QFileInfo newFile(moviesDir, QString("%1.%2").arg(filenameWithoutSuffix).arg(suffix));
        if (newFile.exists()) {
            // find a alternative name
            int index = 1;
            do {
                newFile = QFileInfo(moviesDir,
                    QString("%1(%2).%3")
                        .arg(filenameWithoutSuffix)
                        .arg(index)
                        .arg(suffix));
                index++;
            } while (newFile.exists());
        }

        if (QFile::copy(originalFile.absoluteFilePath(), newFile.absoluteFilePath())) {
            result << QUrl::fromLocalFile(newFile.absoluteFilePath());
        } else {
            qWarning() << "Fail to copy file from:" << originalFile.absoluteFilePath() << "to" << newFile.absoluteFilePath();
        }
    }
    return result;
}

void MediaPlayer::storePosition(int duration)
{
    if (m_playUri.isEmpty()) {
        return;
    }

    QString fileName = m_playUri.toString().mid(m_playUri.toString().lastIndexOf("/"));

    if (duration > 0) {
        m_settings.setValue(fileName, duration);
    } else if (duration == -1) {
        m_settings.remove(fileName);
    }
}

bool MediaPlayer::isAudio(const QString& file)
{
    if (file.isEmpty()) {
        return false;
    }
    QMimeDatabase mdb;
    QMimeType mt = mdb.mimeTypeForFile(file);
    if (!mt.isValid()) {
        return false;
    } else {
        return mt.name().startsWith(QLatin1String("audio"));
    }
}
