// -*- c++ -*-
/***************************************************************************
                           sourcemanager.cpp
                           -----------------
    begin                : Sun Sep 21 2003
    copyright            : (C) 2003, 2004 by Dirk Ziegelmeier
    email                : dziegel@gmx.de
***************************************************************************/

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include <kdebug.h>
#include <klocale.h>
#include <kapplication.h>
#include <kmessagebox.h>

#include <qtimer.h>
#include <QList>

#include "channel.h"
#include "cfgdata.h"
#include "sourcemanager.h"
#include "kdetvsrcplugin.h"
#include "pluginfactory.h"
#include "channelpropertiesdialogimpl.h"

#define AUDIOMODE_UPTDATE_INTERVAL 3000

SourceManager::SourceManager(PluginFactory* pf, QWidget* screen)
    : _dev(QString::null),
      _src(QString::null),
      _enc(QString::null),
      _vsrc(0L),
      _lastViewModeHint(Windowed),
      _screen(screen),
      _pf(pf)
{
    scanPlugins();

    _audioModeTimer = new QTimer(this);
    _audioModeTimer->setObjectName("AudioModeTimer");
    connect( _audioModeTimer, SIGNAL( timeout() ),
             this, SLOT( checkAudioMode() ));
    _audioModeTimer->start(AUDIOMODE_UPTDATE_INTERVAL);
}

SourceManager::~SourceManager()
{
    delete _audioModeTimer;
    stopDevice();
}

void SourceManager::setScreen(QWidget* screen)
{
    _screen = screen;
    scanPlugins();
}

void SourceManager::scanPlugins()
{
    kDebug();

    if (!_screen) {
        //        kDebug() << "Sourcemanager: Info: No screenwidget set. Cannot scan video plugins.";
        return;
    }

    stopDevice();

    _devices.clear();
    _devicePluginMap.clear();
    _deviceSourcesMap.clear();
    _deviceEncodingsMap.clear();
    _deviceTunerMap.clear();

    QList<PluginDesc*>& sourcePlugins(_pf->videoPlugins());
    for (QList<PluginDesc*>::iterator plug = sourcePlugins.begin();
         plug != sourcePlugins.end(); ++plug) {
        kDebug() << "Sourcemanager: Found a plugin: " << (*plug)->name;

        KdetvSourcePlugin* vsrc = _pf->getSourcePlugin((*plug), _screen);
        if (vsrc == 0) continue;
        vsrc->probeDevices();

        for (QStringList::ConstIterator it = vsrc->deviceList().constBegin();
             it != vsrc->deviceList().constEnd();
             ++it) {
            kDebug() << "Found device '" << *it << "' in plugin '" << (*plug)->name << "'";
            kDebug() << "Device contains " << vsrc->sourceList(*it).count() << " sources.";
            kDebug() << "Device is a tuner: " << vsrc->isTuner(*it);

            _devices.append(*it);
            _devicePluginMap[*it]    = (*plug);
            _deviceSourcesMap[*it]   = vsrc->sourceList(*it);
            _deviceEncodingsMap[*it] = vsrc->encodingList(*it);
            _deviceTunerMap[*it]     = vsrc->isTuner(*it);
        }

        vsrc->destroy();
    }
    emit devicesChanged();
}

bool SourceManager::setDevice( const QString& dev )
{
    kDebug() << "Set device: " << dev;

    if (!_screen) {
        kWarning() << "No screenwidget set! Cannot create video plugin!";
        return false;
    }

    if (!_vsrc || (_devicePluginMap[dev] != _vsrc->pluginDescription())) {
        stopDevice();
        _vsrc = _pf->getSourcePlugin(_devicePluginMap[dev], _screen);
        if (_vsrc) {
            _vsrc->probeDevices();
        }
    }

    if (_vsrc) {
        emit aboutToChangeDevice();

        connect(_vsrc, SIGNAL( errorMessage(const QString&) ),
                this,  SLOT(   errorMessage(const QString&) ));

        _dev = dev;
        _vsrc->setDevice(dev);

        // init source and encoding to valid values
        if (source().isEmpty()) {
            setSource(sourcesFor(_dev).first());
        }
        if (encoding().isEmpty()) {
            setEncoding(encodingsFor(_dev).first());
        }

        setAudioMode( QString::null );
        emit deviceChanged(dev);
        emit colourKeyChanged(_vsrc->colourKey());
        return true;
    }
    return false;
}

bool SourceManager::setChannel( const Channel& channel )
{
    kDebug() << "Set channel: " << channel.name();

    if(hasDevice()) {
        emit aboutToChangeSource();
        emit aboutToChangeEncoding();
        _src = channel.getChannelProperty("source").toString();
        _enc = channel.getChannelProperty("encoding").toString();
        _vsrc->setChannelProperties(channel.channelProperties());
        setAudioMode( QString::null );
        emit sourceChanged(_src);
        emit encodingChanged(_enc);
        emit frequencyChanged(channel.getChannelProperty("frequency").toULongLong());
        return true;
    }
    return false;
}

bool SourceManager::setSource( const QString& src )
{
    kDebug() << "Set source: " << src;

    if (hasDevice() && sourcesFor(_dev).contains(src)) {
        emit aboutToChangeSource();
        _src = src;
        _vsrc->setSource(src);
        setAudioMode( QString::null );
        emit sourceChanged(src);
        return true;
    }
    return false;
}

bool SourceManager::setEncoding( const QString& enc )
{
    kDebug() << "Set encoding: " << enc;

    if (hasDevice() && encodingsFor(_dev).contains(enc)) {
        emit aboutToChangeEncoding();
        _enc = enc;
        _vsrc->setEncoding(enc);
        setAudioMode( QString::null );
        emit encodingChanged(enc);
        return true;
    }
    return false;
}

bool SourceManager::setVideoDesktop( bool on )
{
    kDebug() << "Set video desktop: " << on;

    if (!hasDevice())
        return false;

    QByteArray qba;
    QDataStream arg(&qba, QIODevice::WriteOnly);

    arg << (bool)true;
    // TODO: replace dcop with dbus stuff
    bool rc = false; // kapp->dcopClient()->send("kdesktop", "KDesktopIface", "setVRoot(bool)", qba);

    kDebug() << "kdesktop setVRoot returned " << (rc ? "true" : "false");
    if (_vsrc->canVideoDesktop())
        _vsrc->setVideoDesktop(on);

    // TODO: same here => switch to dbus
    // FIXME: this is an ugly hack really.  It won't work with other
    //        desktops (vroots), and it's not the right place for
    //        this anyways.
    /*if (!on) {
        rc = kapp->dcopClient()->send("kdesktop", "KDesktopIface", "refresh()", qba);
        kDebug() << "kdesktop refresh returned " << (rc ? "true" : "false");
    }*/

    return true;
}

bool SourceManager::startVideo()
{
    kDebug();

    if (hasDevice()) {
        _vsrc->startVideo();
        emit playbackChanged(true);
        return true;
    }
    return false;
}

bool SourceManager::stopVideo()
{
    kDebug();

    if (hasDevice()) {
        _vsrc->stopVideo();
        emit playbackChanged(false);
        return true;
    }
    return false;
}

void SourceManager::viewModeHint(ViewModeHint hint)
{

    if (hasDevice() && (hint != _lastViewModeHint)) {
        if(hint == Fullscreen) {
            _vsrc->setFullscreen(true);
        } else {
            _vsrc->setFullscreen(false);
        }
    }

    _lastViewModeHint = hint;
}

void SourceManager::stopDevice()
{
    kDebug();

    if (hasDevice()) {
        stopVideo();

        emit aboutToChangeDevice();
        emit aboutToChangeSource();
        emit aboutToChangeEncoding();

        _dev = QString::null;
        _src = QString::null;
        _enc = QString::null;
        _vsrc->destroy();
        _vsrc = 0L;

        emit deviceChanged( _dev );
        emit sourceChanged( _src );
        emit encodingChanged( _enc );
    }
}

bool SourceManager::setFrequency(qulonglong freq)
{
    if (hasDevice()) {
        _vsrc->setFrequency(freq);
        setAudioMode( QString::null );
        emit frequencyChanged(freq);
        return true;
    }
    return false;
}

qulonglong SourceManager::frequency()
{
    if (hasDevice()) {
        return _vsrc->frequency();
    }
    return 0;
}

bool SourceManager::snapshot(QImage& grabPix)
{
    if ( !hasDevice() ) return false;
    return (_vsrc->grabStill(&grabPix));
}

const Control::ControlList& SourceManager::controls()
{
    if (hasDevice()) {
        return _vsrc->controls();
    }

    static Control::ControlList none;
    return none;
}

bool SourceManager::setMuted(bool mute)
{
    if (hasDevice()) {
        _vsrc->setMuted(mute);
        return true;
    }
    return false;
}

bool SourceManager::setVolume(int left, int right)
{
    if (hasDevice()) {
        return _vsrc->setVolume((int)((float)left * 655.35), (int)((float)right * 655.35));
    }
    return false;
}

bool SourceManager::setAudioMode( const QString& audioMode )
{
    if (hasDevice()) {
        _audioModeTimer->stop();
        _audioModeTimer->start(AUDIOMODE_UPTDATE_INTERVAL);

        if (_audioMode == audioMode) {
            return true;
        }

        _audioMode = audioMode;
        emit audioModeChanged(_audioMode);
        return ( _vsrc->setAudioMode(audioMode) == 0 );
    }
    _audioMode = QString::null;
    return false;
}

QColor SourceManager::colourKey()
{
    if (_vsrc) {
        return _vsrc->colourKey();
    }
    return QColor();
}

int SourceManager::signal()
{
    if (hasDevice()) {
        return _vsrc->signal();
    }
    return -1;
}

const QString SourceManager::audioMode() const
{
    if (hasDevice()) {
        return _audioMode;
    }
    return QString::null;
}

const QStringList& SourceManager::audioModes() const
{
    static QStringList empty;

    if (hasDevice()) {
        return _vsrc->audioModes();
    }
    return empty;
}

bool SourceManager::canVideoDesktop() const
{
    if (hasDevice()) {
        return _vsrc->canVideoDesktop();
    }
    return false;
}

bool SourceManager::canGrabStill() const
{
    if (hasDevice()) {
        return _vsrc->canGrabStill();
    }
    return false;
}

bool SourceManager::supportsImageFiltering() const
{
    if (hasDevice()) {
        return _vsrc->supportsImageFiltering();
    }
    return false;
}

void SourceManager::checkAudioMode()
{
    if (hasDevice()) {
        const QStringList& modes = _vsrc->broadcastedAudioModes();

        if ( !_audioMode.isEmpty() && modes.contains(_audioMode) ) {
            setAudioMode(_audioMode);
        } else {
            setAudioMode(_vsrc->defaultAudioMode());
        }
    }
}

Channel* SourceManager::createChannel(QObject* parent)
{
    // FIXME: This has to be pushed into the plugins (DVB <-> analog) 

    Channel* c = new Channel(parent);

    c->setName(i18n("Unnamed"));

    c->setChannelProperty("frequency", 1000ull);
    c->setChannelProperty("source",    source());
    c->setChannelProperty("encoding",  encoding());
    c->setNumber(0);

    return c;
}

QDialog* SourceManager::channelPropertiesDialog(Channel* channel, QWidget* parent)
{
    // FIXME: This has to be pushed into the plugins (DVB <-> analog) 

    ChannelPropertiesDialogImpl* d;
    d = new ChannelPropertiesDialogImpl(channel, this, parent, "ChannelProperties",
                                        true, Qt::WDestructiveClose);
    return d;
}

void SourceManager::errorMessage(const QString& msg)
{
    KMessageBox::error(0L,
                       msg,
                       i18n("Video Plugin Error"));
}

#include "sourcemanager.moc"
