#include "alsamodel.h"
#include <tqsocketnotifier.h>
#include <tdeconfig.h>
#include <tdeglobal.h>
#include <stdio.h>
#include <stdlib.h>

AlsaModel::AlsaModel( TQObject *parent )
    : TQObject(parent)
{
    m_devices.setAutoDelete(true);
    m_notifiers.setAutoDelete(true);
}

AlsaModel::~AlsaModel()
{
    close();
}

bool AlsaModel::open()
{
    int card = -1;
    while ( snd_card_next(&card) == 0 && card >= 0 )
        openCard(card);
    emit ready();
    return !m_devices.isEmpty();
}

void AlsaModel::openCard( int card )
{
    char hwname[32];
    snprintf(hwname, sizeof(hwname), "hw:%d", card);

    snd_mixer_t *mixer = 0;
    if ( snd_mixer_open(&mixer, 0) < 0 ) return;
    if ( snd_mixer_attach(mixer, hwname) < 0 )        { snd_mixer_close(mixer); return; }
    if ( snd_mixer_selem_register(mixer, 0, 0) < 0 )  { snd_mixer_close(mixer); return; }
    if ( snd_mixer_load(mixer) < 0 )                  { snd_mixer_close(mixer); return; }

    char *cardName = 0;
    snd_card_get_name(card, &cardName);
    TQString qName = cardName ? TQString::fromUtf8(cardName) : TQString("Card %1").arg(card);
    free(cardName);

    AlsaCard ac;
    ac.index = card;
    ac.name  = qName;
    m_cards.append(ac);

    for ( snd_mixer_elem_t *elem = snd_mixer_first_elem(mixer);
          elem; elem = snd_mixer_elem_next(elem) ) {
        if ( !snd_mixer_selem_is_active(elem) ) continue;
        if ( snd_mixer_selem_is_enumerated(elem) )
            addElement(card, qName, elem, AlsaDevice::Neutral);
        else if ( snd_mixer_selem_has_capture_volume(elem) )
            addElement(card, qName, elem, AlsaDevice::Input);
        else if ( snd_mixer_selem_has_playback_volume(elem) )
            addElement(card, qName, elem, AlsaDevice::Output);
        else if ( snd_mixer_selem_has_playback_switch(elem) )
            addElement(card, qName, elem, AlsaDevice::Output);
        else if ( snd_mixer_selem_has_capture_switch(elem) )
            addElement(card, qName, elem, AlsaDevice::Input);
        else if ( snd_mixer_selem_has_common_switch(elem) )
            addElement(card, qName, elem, AlsaDevice::Neutral);
    }

    int count = snd_mixer_poll_descriptors_count(mixer);
    if ( count > 0 ) {
        struct pollfd *fds = new struct pollfd[count];
        snd_mixer_poll_descriptors(mixer, fds, count);
        for ( int i = 0; i < count; i++ ) {
            m_fdToMixer[fds[i].fd] = mixer;
            TQSocketNotifier *sn = new TQSocketNotifier(fds[i].fd, TQSocketNotifier::Read, this);
            connect(sn, TQ_SIGNAL(activated(int)), this, TQ_SLOT(onMixerFd(int)));
            m_notifiers.append(sn);
        }
        delete[] fds;
    }

    m_mixers.append(mixer);
}

void AlsaModel::addElement( int card, const TQString &cardName,
                             snd_mixer_elem_t *elem, AlsaDevice::Category cat )
{
    AlsaDevice *dev = new AlsaDevice(cat, card, elem, cardName, this);
    m_devices.append(dev);
    emit deviceAdded(dev);
}

void AlsaModel::onMixerFd( int fd )
{
    snd_mixer_t *mixer = m_fdToMixer[fd];
    if ( mixer ) snd_mixer_handle_events(mixer);
}

TQPtrList<AlsaDevice> AlsaModel::devicesForCard( int cardIndex ) const
{
    TQPtrList<AlsaDevice> result;
    for ( TQPtrListIterator<AlsaDevice> it(m_devices); *it; ++it )
        if ( (*it)->cardIndex() == cardIndex )
            result.append(*it);
    return result;
}

AlsaDevice *AlsaModel::defaultOutput() const
{
    for ( TQPtrListIterator<AlsaDevice> it(m_devices); *it; ++it )
        if ( (*it)->category() == AlsaDevice::Output )
            return *it;
    return 0;
}

AlsaDevice *AlsaModel::preferredOutput() const
{
    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("Tray");
    int card = cfg->readNumEntry("TrayCard", -1);
    TQString name = cfg->readEntry("TrayDevice", "");
    if ( card >= 0 && !name.isEmpty() ) {
        for ( TQPtrListIterator<AlsaDevice> it(m_devices); *it; ++it )
            if ( (*it)->cardIndex() == card && (*it)->name() == name &&
                 (*it)->category() == AlsaDevice::Output )
                return *it;
    }
    return defaultOutput();
}

void AlsaModel::setPreferredOutput( int cardIndex, const TQString &name )
{
    TDEConfig *cfg = TDEGlobal::config();
    cfg->setGroup("Tray");
    cfg->writeEntry("TrayCard", cardIndex);
    cfg->writeEntry("TrayDevice", name);
    cfg->sync();
    emit preferredOutputChanged( preferredOutput() );
}

void AlsaModel::close()
{
    m_notifiers.clear();
    m_devices.clear();
    m_fdToMixer.clear();
    for ( TQValueListIterator<snd_mixer_t*> it = m_mixers.begin(); it != m_mixers.end(); ++it )
        snd_mixer_close(*it);
    m_mixers.clear();
    m_cards.clear();
}

#include "alsamodel.moc"
