#include "ossdevice.h"
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <algorithm>
#include <cmath>

OssDevice::OssDevice( Category cat, int cardIdx, const TQString &cardPath,
                      int fd, int channelIdx, const TQString &name, bool stereo,
                      TQObject *parent )
    : TQObject(parent), m_fd(fd), m_cardIdx(cardIdx), m_cardPath(cardPath),
      m_channelIdx(channelIdx), m_name(name), m_category(cat),
      m_stereo(stereo), m_volume(0), m_savedVol(50), m_muted(false), m_pan(0)
{
    syncFromOss();
}

void OssDevice::syncFromOss()
{
    if ( m_fd < 0 ) return;
    int val = 0;
    if ( ::ioctl(m_fd, MIXER_READ(m_channelIdx), &val) < 0 ) return;

    int left  = val & 0xff;
    int right = (val >> 8) & 0xff;
    int mx    = std::max(left, right);

    if ( m_muted ) return; // don't overwrite display while muted

    int newVol = mx;
    int newPan = 0;
    if ( m_stereo && mx > 0 ) {
        if ( left >= right )
            newPan = (int)std::lround( -50.0 * (1.0 - (double)right / left) );
        else
            newPan = (int)std::lround(  50.0 * (1.0 - (double)left / right) );
    }

    if ( newVol != m_volume ) { m_volume = newVol; emit volumeChanged(m_volume); }
    if ( newPan != m_pan   ) { m_pan    = newPan;  emit panChanged(m_pan); }
}

void OssDevice::pushVolume()
{
    if ( m_fd < 0 ) return;
    int vol = m_muted ? 0 : m_volume;
    double lf = m_pan >= 0 ? (50.0 - m_pan) / 50.0 : 1.0;
    double rf = m_pan <= 0 ? (50.0 + m_pan) / 50.0 : 1.0;
    int lv = (int)(vol * lf + 0.5);
    int rv = (int)(vol * rf + 0.5);
    if ( lv > 100 ) lv = 100;
    if ( rv > 100 ) rv = 100;
    int val = lv | (rv << 8);
    ::ioctl(m_fd, MIXER_WRITE(m_channelIdx), &val);
}

void OssDevice::setVolume( int v )
{
    v = v < 0 ? 0 : v > 100 ? 100 : v;
    m_volume = v;
    if ( !m_muted ) m_savedVol = v;
    emit volumeChanged(v);
    pushVolume();
}

void OssDevice::setMuted( bool m )
{
    if ( m == m_muted ) return;
    if ( m ) {
        if ( m_volume > 0 ) m_savedVol = m_volume;
        m_muted = true;
        pushVolume();
    } else {
        m_muted = false;
        m_volume = m_savedVol;
        pushVolume();
        emit volumeChanged(m_volume);
    }
    emit muteChanged(m_muted);
}

void OssDevice::setPan( int p )
{
    p = p < -50 ? -50 : p > 50 ? 50 : p;
    m_pan = p;
    emit panChanged(p);
    pushVolume();
}

#include "ossdevice.moc"
