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

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    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 Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include "mixer.h"
#include "mixercontrolset.h"
#include "mixercontrolset_p.h"
#include "mixer_p.h"
#include <kdebug.h>
#include <kglobal.h>
#include <QtCore/QList>
#include <QtGui/QIcon>
#include "defs_p.h"
#include "mixertogglecontrol_p.h"
#include "mixerintegercontrol_p.h"
#include "mixerenumcontrol_p.h"

namespace Phonon
{
namespace Mixer
{

const QObject *Mixer::notifier() const
{
    K_S(const Mixer);
    return s;
}

Mixer::Mixer()
    : d_ptr(new MixerPrivate)
{
    d_ptr->q_ptr = this;
}

Mixer::Mixer(const Phonon::AudioOutputDevice &device)
    : d_ptr(new MixerPrivate)
{
    d_ptr->q_ptr = this;
    QVariant mixerDeviceId = device.property("mixerDeviceId");
    if (!mixerDeviceId.isValid() || !MixerPrivate::isValidDeviceId(mixerDeviceId)) {
        return;
    }
    d_ptr->s_ptr = MixerPrivate::getSharedData(mixerDeviceId);
}

Mixer::Mixer(Type type)
    : d_ptr(new MixerPrivate)
{
    d_ptr->q_ptr = this;
    switch (type) {
    case Invalid:
        return;
    case PhononControls:
        break;
    }
    d_ptr->s_ptr = MixerPrivate::getSharedData(type);
}

Mixer::Mixer(const QString deviceId)
    : d_ptr(new MixerPrivate)
{
    d_ptr->q_ptr = this;
    if (!MixerPrivate::isValidDeviceId(deviceId)) {
        return;
    }
    d_ptr->s_ptr = MixerPrivate::getSharedData(deviceId);
}

Mixer::Mixer(const Mixer &rhs)
    : d_ptr(new MixerPrivate)
{
    d_ptr->q_ptr = this;
    d_ptr->s_ptr = rhs.d_ptr->s_ptr;
}

Mixer::~Mixer()
{
    delete d_ptr;
}

Mixer &Mixer::operator=(const Mixer &rhs)
{
    d_ptr->s_ptr = rhs.d_ptr->s_ptr;
    return *this;
}

bool Mixer::operator==(const Mixer &rhs) const
{
    return d_ptr->s_ptr == rhs.d_ptr->s_ptr;
}

bool Mixer::isValid() const
{
    K_S(const Mixer);
    if (s) {
        return s->isValid();
    }
    return false;
}

QString Mixer::name() const
{
    K_S(const Mixer);
    Q_ASSERT(s);
    return s->name();
}

QList<MixerControl> Mixer::allControls() const
{
    K_S(const Mixer);
    if (s->m_controls.isEmpty()) {
        kDebug() << "m_controls is empty: update";
        const_cast<MixerSharedData *>(s)->updateControls();
    }
    return s->m_controls;
}

QList<MixerToggleControl> Mixer::toggleControls() const
{
    K_S(const Mixer);
    if (s->m_controls.isEmpty()) {
        kDebug() << "m_controls is empty: update";
        const_cast<MixerSharedData *>(s)->updateControls();
    }
    QList<MixerToggleControl> r;
    foreach (const MixerControl &c, s->m_controls) {
        MixerToggleControl tc = mixercontrol_cast<MixerToggleControl>(c);
        if (tc.isValid()) {
            r << tc;
        }
    }
    return r;
}

QList<MixerIntegerControl> Mixer::integerControls() const
{
    K_S(const Mixer);
    if (s->m_controls.isEmpty()) {
        kDebug() << "m_controls is empty: update";
        const_cast<MixerSharedData *>(s)->updateControls();
    }
    QList<MixerIntegerControl> r;
    foreach (const MixerControl &c, s->m_controls) {
        MixerIntegerControl tc = mixercontrol_cast<MixerIntegerControl>(c);
        if (tc.isValid()) {
            r << tc;
        }
    }
    return r;
}

QList<MixerEnumControl> Mixer::enumControls() const
{
    K_S(const Mixer);
    if (s->m_controls.isEmpty()) {
        kDebug() << "m_controls is empty: update";
        const_cast<MixerSharedData *>(s)->updateControls();
    }
    QList<MixerEnumControl> r;
    foreach (const MixerControl &c, s->m_controls) {
        MixerEnumControl tc = mixercontrol_cast<MixerEnumControl>(c);
        if (tc.isValid()) {
            r << tc;
        }
    }
    return r;
}

QList<MixerControlSet> Mixer::controlSets() const
{
    K_S(const Mixer);
    if (s->m_controlSets.isEmpty()) {
        kDebug() << "m_controls is empty: update";
        const_cast<MixerSharedData *>(s)->updateControls();
    }
    return s->m_controlSets.values();
}

MixerToggleControl Mixer::addControl(MixerToggleControl::Category category, MixerControlSet &set)
{
    Q_ASSERT(isValid());
    K_S(Mixer);
    AlsaElementId id;
    //id.setNumid(?);
    //id.setInterface(?);
    id.setDevice(0);
    id.setSubdevice(0);
    const QByteArray name = set.name().toUtf8();
    id.setName(name);
    id.setIndex(0);
    s->m_ctl->addBoolElement(id, 0);
    MixerToggleControlSharedData::Ptr ts(new MixerToggleControlSharedData(MixerSharedData::Ptr(s), id, category));
    set.d_func()->addControl(MixerControlSharedData::Ptr(ts.data()));
    MixerToggleControl tc(*new MixerToggleControlPrivate(ts.data()));
    return tc;
}


MixerIntegerControl Mixer::addControl(MixerIntegerControl::Category category, int min, int max, int singleStep, int pageStep, MixerControlSet &set)
{
    Q_ASSERT(isValid());
    K_S(Mixer);
    AlsaElementId id;
    //id.setNumid(?);
    //id.setInterface(?);
    id.setDevice(0);
    id.setSubdevice(0);
    const QByteArray name = set.name().toUtf8();
    id.setName(name);
    id.setIndex(0);
    s->m_ctl->addIntElement(id, 0, min, max, singleStep);
    MixerIntegerControlSharedData::Ptr ts(new MixerIntegerControlSharedData(MixerSharedData::Ptr(s), id, category));
    set.d_func()->addControl(MixerControlSharedData::Ptr(ts.data()));
    MixerIntegerControl tc(*new MixerIntegerControlPrivate(ts.data()));
    return tc;
}

MixerEnumControl Mixer::addControl(MixerEnumControl::Category category, const QStringList &options, MixerControlSet &set)
{
    Q_ASSERT(isValid());
    return MixerEnumControl(*new MixerEnumControlPrivate(0)); // ALSA doesn't support this
}

K_GLOBAL_STATIC(QList<MixerSharedData *>, mixerList)

MixerSharedData::~MixerSharedData()
{
    kDebug() << name();
    if (!mixerList.isDestroyed()) {
        mixerList->removeAll(this);
    }
    cleanup();
}

MixerSharedData::Ptr MixerPrivate::getSharedData(const QVariant &mixerDeviceId)
{
    foreach (MixerSharedData *sd, *mixerList) {
        if (*sd == mixerDeviceId) {
            return MixerSharedData::Ptr(sd);
        }
    }
    MixerSharedData::Ptr sd(new MixerSharedData(mixerDeviceId));
    *mixerList << sd.data();
    return sd;
}

MixerSharedData::Ptr MixerPrivate::getSharedData(Mixer::Type type)
{
    foreach (MixerSharedData *sd, *mixerList) {
        if (*sd == type) {
            return MixerSharedData::Ptr(sd);
        }
    }
    MixerSharedData::Ptr sd(new MixerSharedData(type));
    *mixerList << sd.data();
    return sd;
}

MixerControlSet MixerSharedData::controlSetFor(const QString &_name)
{
    QString name(_name);
    if (name.endsWith(QLatin1String(" Volume"))) {
        name = name.left(name.length() - 7);
    } else if (name.endsWith(QLatin1String(" Switch"))) {
        name = name.left(name.length() - 7);
    }
    if (m_controlSets.contains(name)) {
        return m_controlSets.value(name);
    }
    MixerControlSet set;
    MixerControlSetPrivate *setPrivate = set.d_func();
    setPrivate->s_ptr = new MixerControlSetSharedData(name, QIcon());
    setPrivate->s_ptr->m_mixer = this;
    Q_ASSERT(set.isValid());
    m_controlSets.insert(name, set);
    return set;
}

} // namespace Mixer
} // namespace Phonon

uint qHash(const Phonon::Mixer::Mixer &m)
{
    return qHash(m.d_func()->s_func());
}
