/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwmpplayercontrol.h"

#include "qwmpevents.h"
#include "qwmpglobal.h"
#include "qwmpmetadata.h"
#include "qwmpplaylist.h"

#include <qmediaplayer.h>
#include <qmediaplaylist.h>

#include <QtCore/qcoreapplication.h>
#include <QtCore/qcoreevent.h>
#include <QtCore/qurl.h>
#include <QtCore/qvariant.h>

QWmpPlayerControl::QWmpPlayerControl(IWMPCore3 *player, QWmpEvents *events, QObject *parent)
    : QMediaPlayerControl(parent)
    , m_player(player)
    , m_controls(0)
    , m_settings(0)
    , m_state(QMediaPlayer::StoppedState)
    , m_changes(0)
    , m_buffering(false)
    , m_audioAvailable(false)
    , m_videoAvailable(false)
{
    m_player->get_controls(&m_controls);
    m_player->get_settings(&m_settings);
    m_player->get_network(&m_network);

    if (m_settings)
        m_settings->put_autoStart(FALSE);

    WMPPlayState state = wmppsUndefined;
    if (m_player->get_playState(&state) == S_OK)
        playStateChangeEvent(state);

    connect(events, SIGNAL(Buffering(VARIANT_BOOL)), this, SLOT(bufferingEvent(VARIANT_BOOL)));
    connect(events, SIGNAL(PositionChange(double,double)),
            this, SLOT(positionChangeEvent(double,double)));
    connect(events, SIGNAL(PlayStateChange(long)), this, SLOT(playStateChangeEvent(long)));
    connect(events, SIGNAL(CurrentItemChange(IDispatch*)),
            this, SLOT(currentItemChangeEvent(IDispatch*)));
    connect(events, SIGNAL(MediaChange(IDispatch*)), this, SLOT(mediaChangeEvent(IDispatch*)));
}

QWmpPlayerControl::~QWmpPlayerControl()
{
    if (m_controls) m_controls->Release();
    if (m_settings) m_settings->Release();
    if (m_network) m_network->Release();
}

QMediaPlayer::State QWmpPlayerControl::state() const
{
    return m_state;
}

QMediaPlayer::MediaStatus QWmpPlayerControl::mediaStatus() const
{
    return m_status;
}

qint64 QWmpPlayerControl::duration() const
{
    double duration = 0.;

    IWMPMedia *media = 0;
    if (m_controls && m_controls->get_currentItem(&media) == S_OK) {
        media->get_duration(&duration);
        media->Release();
    }

    return duration * 1000;
}

qint64 QWmpPlayerControl::position() const
{
    double position = 0.0;

    if (m_controls)
        m_controls->get_currentPosition(&position);

    return position * 1000;
}

void QWmpPlayerControl::setPosition(qint64 position)
{
    if (m_controls)
        m_controls->put_currentPosition(double(position) / 1000.);
}

int QWmpPlayerControl::volume() const
{
    long volume = 0;

    if (m_settings)
        m_settings->get_volume(&volume);

    return volume;
}

void QWmpPlayerControl::setVolume(int volume)
{
    if (m_settings && m_settings->put_volume(volume) == S_OK)
        emit volumeChanged(volume);
}

bool QWmpPlayerControl::isMuted() const
{
    VARIANT_BOOL mute = FALSE;

    if (m_settings)
        m_settings->get_mute(&mute);

    return mute;
}

void QWmpPlayerControl::setMuted(bool muted)
{
    if (m_settings && m_settings->put_mute(muted ? TRUE : FALSE) == S_OK)
        emit mutedChanged(muted);

}

int QWmpPlayerControl::bufferStatus() const
{
    long progress = 0;

    if (m_network)
        m_network->get_bufferingProgress(&progress);

    return progress;
}

bool QWmpPlayerControl::isVideoAvailable() const
{
    return m_videoAvailable;
}

bool QWmpPlayerControl::isAudioAvailable() const
{
    return m_audioAvailable;
}

void QWmpPlayerControl::setAudioAvailable(bool available)
{
    if (m_audioAvailable != available)
        emit audioAvailableChanged(m_audioAvailable = available);
}

void QWmpPlayerControl::setVideoAvailable(bool available)
{
    if (m_videoAvailable != available)
        emit videoAvailableChanged(m_videoAvailable = available);
}

bool QWmpPlayerControl::isSeekable() const
{
    return true;
}

QMediaTimeRange QWmpPlayerControl::availablePlaybackRanges() const
{
    QMediaTimeRange ranges;

    IWMPMedia *media = 0;
    if (m_controls && m_controls->get_currentItem(&media) == S_OK) {
        double duration = 0;
        media->get_duration(&duration);
        media->Release();

        if(duration > 0)
            ranges.addInterval(0, duration * 1000);
    }

    return ranges;
}

qreal QWmpPlayerControl::playbackRate() const
{
    double rate = 0.;

    if (m_settings)
        m_settings->get_rate(&rate);

    return rate;
}

void QWmpPlayerControl::setPlaybackRate(qreal rate)
{
    if (m_settings)
        m_settings->put_rate(rate);
}

void QWmpPlayerControl::play()
{
    if (m_controls)
        m_controls->play();
}

void QWmpPlayerControl::pause()
{
    if (m_controls)
        m_controls->pause();
}

void QWmpPlayerControl::stop()
{
    if (m_controls)
        m_controls->stop();
}

QMediaContent QWmpPlayerControl::media() const
{
    QMediaResourceList resources;

    QUrl tmpUrl = url();

    if (!tmpUrl.isEmpty())
        resources << QMediaResource(tmpUrl);

    return resources;
}

const QIODevice *QWmpPlayerControl::mediaStream() const
{
    return 0;
}

void QWmpPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream)
{
    if (!content.isNull() && !stream)
        setUrl(content.canonicalUrl());
    else
        setUrl(QUrl());
}

bool QWmpPlayerControl::event(QEvent *event)
{
    if (event->type() == QEvent::UpdateRequest) {
        const int changes = m_changes;
        m_changes = 0;

        if (changes & DurationChanged)
            emit durationChanged(duration());
        if (changes & PositionChanged)
            emit positionChanged(position());
        if (changes & StatusChanged)
            emit mediaStatusChanged(m_status);
        if (changes & StateChanged)
            emit stateChanged(m_state);

        return true;
    } else {
        return QMediaPlayerControl::event(event);
    }
}

void QWmpPlayerControl::scheduleUpdate(int change)
{
    if (m_changes == 0)
        QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));

    m_changes |= change;
}

void QWmpPlayerControl::bufferingEvent(VARIANT_BOOL buffering)
{
    if (m_state != QMediaPlayer::StoppedState) {
        m_status = buffering
                ? QMediaPlayer::BufferingMedia
                : QMediaPlayer::BufferedMedia;

        scheduleUpdate(StatusChanged);
    }
}

void QWmpPlayerControl::currentItemChangeEvent(IDispatch *)
{
    scheduleUpdate(DurationChanged);
}

void QWmpPlayerControl::mediaChangeEvent(IDispatch *dispatch)
{
    IWMPMedia *media = 0;
    if (dispatch &&  dispatch->QueryInterface(
            __uuidof(IWMPMedia), reinterpret_cast<void **>(&media)) == S_OK) {
        IWMPMedia *currentMedia = 0;
        if (m_controls && m_controls->get_currentItem(&currentMedia) == S_OK) {
            VARIANT_BOOL isEqual = VARIANT_FALSE;
            if (media->get_isIdentical(currentMedia, &isEqual) == S_OK && isEqual)
                scheduleUpdate(DurationChanged);

            currentMedia->Release();
        }
        media->Release();
    }
}

void QWmpPlayerControl::positionChangeEvent(double , double)
{
    scheduleUpdate(PositionChanged);
}

void QWmpPlayerControl::playStateChangeEvent(long state)
{
    switch (state) {
    case wmppsUndefined:
        m_state = QMediaPlayer::StoppedState;
        m_status = QMediaPlayer::UnknownMediaStatus;
        scheduleUpdate(StatusChanged | StateChanged);
        break;
    case wmppsStopped:
        if (m_state != QMediaPlayer::StoppedState) {
            m_state = QMediaPlayer::StoppedState;
            scheduleUpdate(StateChanged);

            if (m_status != QMediaPlayer::EndOfMedia) {
                m_status = QMediaPlayer::LoadedMedia;
                scheduleUpdate(StatusChanged);
            }
        }
        break;
    case wmppsPaused:
        if (m_state != QMediaPlayer::PausedState && m_status != QMediaPlayer::BufferedMedia) {
            m_state = QMediaPlayer::PausedState;
            m_status = QMediaPlayer::BufferedMedia;
            scheduleUpdate(StatusChanged | StateChanged);
        } else if (m_state != QMediaPlayer::PausedState) {
            m_state = QMediaPlayer::PausedState;
            scheduleUpdate(StateChanged);
        } else if (m_status != QMediaPlayer::BufferedMedia) {
            m_status = QMediaPlayer::BufferedMedia;

            scheduleUpdate(StatusChanged);
        }
        break;
    case wmppsPlaying:
    case wmppsScanForward:
    case wmppsScanReverse:
        if (m_state != QMediaPlayer::PlayingState && m_status != QMediaPlayer::BufferedMedia) {
            m_state = QMediaPlayer::PlayingState;
            m_status = QMediaPlayer::BufferedMedia;
            scheduleUpdate(StatusChanged | StateChanged);
        } else if (m_state != QMediaPlayer::PlayingState) {
            m_state = QMediaPlayer::PlayingState;
            scheduleUpdate(StateChanged);
        } else if (m_status != QMediaPlayer::BufferedMedia) {
            m_status = QMediaPlayer::BufferedMedia;
            scheduleUpdate(StatusChanged);
        }

        if (m_state != QMediaPlayer::PlayingState) {
            m_state = QMediaPlayer::PlayingState;
            scheduleUpdate(StateChanged);
        }
        if (m_status != QMediaPlayer::BufferedMedia) {
            m_status = QMediaPlayer::BufferedMedia;
            scheduleUpdate(StatusChanged);
        }
        break;
    case wmppsBuffering:
    case wmppsWaiting:
        if (m_status != QMediaPlayer::StalledMedia && m_state != QMediaPlayer::StoppedState) {
            m_status = QMediaPlayer::StalledMedia;
            scheduleUpdate(StatusChanged);
        }
        break;
    case wmppsMediaEnded:
        if (m_status != QMediaPlayer::EndOfMedia && m_state != QMediaPlayer::StoppedState) {
            m_state = QMediaPlayer::StoppedState;
            m_status = QMediaPlayer::EndOfMedia;
            scheduleUpdate(StatusChanged | StateChanged);
        }
        break;
    case wmppsTransitioning:
        break;
    case wmppsReady:
        if (m_status != QMediaPlayer::LoadedMedia) {
            m_status = QMediaPlayer::LoadedMedia;
            scheduleUpdate(StatusChanged);
        }

        if (m_state != QMediaPlayer::StoppedState) {
            m_state = QMediaPlayer::StoppedState;
            scheduleUpdate(StateChanged);
        }
        break;
    case wmppsReconnecting:
        if (m_status != QMediaPlayer::StalledMedia && m_state != QMediaPlayer::StoppedState) {
            m_status = QMediaPlayer::StalledMedia;
            scheduleUpdate(StatusChanged);
        }
        break;
    default:
        break;
    }
}

QUrl QWmpPlayerControl::url() const
{
    BSTR string;
    if (m_player && m_player->get_URL(&string) == S_OK) {
        QUrl url(QString::fromWCharArray(string, SysStringLen(string)), QUrl::StrictMode);

        SysFreeString(string);

        return url;
    } else {
        return QUrl();
    }
}

void QWmpPlayerControl::setUrl(const QUrl &url)
{
    if (url != QWmpPlayerControl::url() && m_player) {
        BSTR string = SysAllocString(reinterpret_cast<const wchar_t *>(url.toString().unicode()));

        m_player->put_URL(string);

        SysFreeString(string);
    }
}
