/*  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 "mixercontrol.h"
#include "mixercontrol_p.h"
#include "mixertogglecontrol_p.h"
#include "mixerintegercontrol_p.h"
#include "mixerenumcontrol_p.h"
#include <kdebug.h>
#include <klocale.h>
#include <QtCore/QStringList>

namespace Phonon
{
namespace Mixer
{

#define K_GET_CTL \
    AlsaControl *ctl = m_mixer ? m_mixer->ctl() : 0; \
    if (!ctl) return

bool MixerControlSharedData::isValid() const
{
    return m_mixer && m_mixer->isValid();
}

bool MixerToggleControlSharedData::isChecked() const
{
    Q_ASSERT(MixerControlSharedData::m_type == MixerControl::Toggle);
    K_GET_CTL false;
    if (m_isVolatile) {
        ctl->readElementValue(&m_value);
    }
    return static_cast<bool>(m_value.boolean(m_index));
}

void MixerToggleControlSharedData::setChecked(bool checked)
{
    Q_ASSERT(MixerControlSharedData::m_type == MixerControl::Toggle);
    K_GET_CTL;
    m_value.setBoolean(m_index, checked);
    ctl->writeElementValue(m_value);
}

void MixerToggleControlSharedData::valueChangeEvent()
{
    emit toggled(isChecked());
}

int MixerIntegerControlSharedData::value() const
{
    Q_ASSERT(MixerControlSharedData::m_type == MixerControl::Integer);
    K_GET_CTL -1;
    if (m_isVolatile) {
        ctl->readElementValue(&m_value);
    }
    return static_cast<int>(m_value.integer(m_index));
}

void MixerIntegerControlSharedData::setValue(int value)
{
    Q_ASSERT(MixerControlSharedData::m_type == MixerControl::Integer);
    K_GET_CTL;
    ctl->readElementValue(&m_value);
    m_value.setInteger(m_index, value);
    ctl->writeElementValue(m_value);
}

void MixerIntegerControlSharedData::valueChangeEvent()
{
    int newValue = value();
    if (m_lastValue != newValue) {
        m_lastValue = newValue;
        emit valueChanged(value());
    }
}

int MixerEnumControlSharedData::currentIndex() const
{
    Q_ASSERT(MixerControlSharedData::m_type == MixerControl::Enum);
    K_GET_CTL -1;
    if (m_isVolatile) {
        ctl->readElementValue(&m_value);
    }
    return m_value.enumerated(m_index);
}

QString MixerEnumControlSharedData::currentOption() const
{
    const int idx = currentIndex();
    if (idx < 0) {
        return QString();
    }
    return m_options[idx];
}

void MixerEnumControlSharedData::setCurrentIndex(int index)
{
    Q_ASSERT(MixerControlSharedData::m_type == MixerControl::Enum);
    K_GET_CTL;
    m_value.setEnumerated(m_index, index);
    ctl->writeElementValue(m_value);
}

void MixerEnumControlSharedData::setCurrentIndex(const QString &option)
{
    setCurrentIndex(m_options.indexOf(option));
}

void MixerEnumControlSharedData::valueChangeEvent()
{
    const int idx = currentIndex();
    emit currentIndexChanged(idx);
    emit currentIndexChanged(m_options[idx]);
}

MixerControl::Type MixerControl::type() const
{
    kDebug();
    if (!isValid()) {
        return Invalid;
    }
    K_S(const MixerControl);
    return s->m_type;
}

bool MixerControlSharedData::operator<(const MixerControlSharedData &rhs) const
{
    return m_id.numid() < rhs.m_id.numid();
}

MixerControlSharedData::MixerControlSharedData(const MixerSharedData::Ptr &mixer, const AlsaElementId &id)
    : m_mixer(mixer)
{
    id.copy(&m_id);

    K_GET_CTL;
    AlsaElementInfo info;
    info.setId(m_id);
    ctl->readElementInfo(&info);

    m_value.setId(m_id);
    m_index = m_id.index();

    kDebug() << m_id.index() << m_value.index() << info.index();

    switch (info.type()) {
    case SND_CTL_ELEM_TYPE_NONE:
        m_type = MixerControl::Invalid;
        break;
    case SND_CTL_ELEM_TYPE_BOOLEAN:
        m_type = MixerControl::Toggle;
        break;
    case SND_CTL_ELEM_TYPE_INTEGER:
        m_type = MixerControl::Integer;
        break;
    case SND_CTL_ELEM_TYPE_INTEGER64:
        m_type = MixerControl::Invalid;
        break;
    case SND_CTL_ELEM_TYPE_ENUMERATED:
        m_type = MixerControl::Enum;
        break;
    case SND_CTL_ELEM_TYPE_BYTES:
        m_type = MixerControl::Invalid;
        break;
    case SND_CTL_ELEM_TYPE_IEC958:
        m_type = MixerControl::Invalid;
        break;
    }
    kDebug()
        << info.name()
        << "isReadable" << info.isReadable()
        << "isWritable" << info.isWritable()
        << "isVolatile" << info.isVolatile()
        << "isInactive" << info.isInactive()
        << "isLocked" << info.isLocked()
        << "isOwner" << info.isOwner()
        << "isUser" << info.isUser()
        << "owner" << info.owner()
        << "count" << info.count()
        << "dimensions" << info.dimensions()
        //<< "dimension" << info.dimension(unsigned int idx)
        //<< "readId" << info.readId(AlsaElementId *ptr)
        << "numid" << info.numid()
        << "interface" << info.interface()
        << "index" << info.index()
        << "m_index" << m_index;
    switch (m_type) {
    case MixerControl::Invalid:
        kFatal() << "invalid type";
    case MixerControl::Toggle:
        break;
    case MixerControl::Integer:
        kDebug()
            << "min" << info.min()
            << "max" << info.max()
            << "step" << info.step();
        break;
    case MixerControl::Enum:
        kDebug()
            << "items" << info.items()
            << "itemName" << info.itemName();
        break;
    }
}

MixerIntegerControlSharedData::MixerIntegerControlSharedData(const MixerSharedData::Ptr &mixer,
        const AlsaElementId &id, MixerIntegerControl::Category category)
    : MixerControlSharedData(mixer, id),
    m_lastValue(-1),
    m_minimumValue(0),
    m_maximumValue(1),
    m_singleStep(0),
    m_pageStep(0),
    m_category(category)
{
    K_GET_CTL;
    AlsaElementInfo info;
    ctl->readElementInfo(&info, m_id);

    m_minimumValue = info.min();
    m_maximumValue = info.max();
    m_singleStep = info.step();
}

MixerEnumControlSharedData::MixerEnumControlSharedData(const MixerSharedData::Ptr &mixer, const AlsaElementId &id, MixerEnumControl::Category category)
    : MixerControlSharedData(mixer, id),
    m_category(category)
{
    K_GET_CTL;
    AlsaElementInfo info;
    info.setId(m_id);
    ctl->readElementInfo(&info);
    const int entries = info.items();
    for (int i = 0; i < entries; ++i) {
        info.setItem(i);
        ctl->readElementInfo(&info);
        m_options << QString::fromUtf8(info.itemName());
    }
}

void MixerControlSharedData::extraInfoChangeEvent()
{
    K_GET_CTL;
    AlsaElementInfo info;
    info.setId(m_id);
    ctl->readElementInfo(&info);
    if (info.isTlvReadable()) {
        //AlsaTlv tlv = ctl->readTlv(m_id);
        //tlv.parse();
        //m_extraInfo tlv.
    }
}

bool MixerControl::isRemovable() const
{
    K_S(const MixerControl);
    AlsaControl *ctl = s->m_mixer ? s->m_mixer->ctl() : 0;
    if (!ctl) {
        return false;
    }
    AlsaElementInfo info;
    ctl->readElementInfo(&info, s->m_id);
    return info.isUser();
}

} // namespace Mixer
} // namespace Phonon
