/***************************************************************************
                          v4lradio-configuration.cpp  -  description
                             -------------------
    begin                : Fre Jun 20 2003
    copyright            : (C) 2003 by Martin Witte
    email                : witte@kawo1.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/soundcard.h>

#include <tqspinbox.h>
#include <tqlineedit.h>
#include <tqcombobox.h>
#include <tqlabel.h>
#include <tqfile.h>
#include <tqpushbutton.h>
#include <tqslider.h>
#include <tqcheckbox.h>

#include <tdefiledialog.h>
#include <knuminput.h>
#include <tdelocale.h>
#include <ktabwidget.h>

#include "../../src/include/utils.h"
#include "../../src/include/gui_list_helper.h"
#include "v4lradio-configuration.h"
#include "v4lradio.h"

V4LRadioConfiguration::V4LRadioConfiguration (TQWidget *parent, SoundStreamID ssid)
  : V4LRadioConfigurationUI(parent),
    m_SoundStreamID(ssid),
    m_ignoreGUIChanges(false),
    m_myControlChange(0),
    m_orgTreble(-1),
    m_orgBass(-1),
    m_orgBalance(-2),
    m_orgDeviceVolume(-1),
    m_PlaybackMixerHelper(comboPlaybackMixerDevice, StringListHelper::SORT_BY_DESCR),
    m_CaptureMixerHelper (comboCaptureMixerDevice,  StringListHelper::SORT_BY_DESCR),
    m_PlaybackChannelHelper(comboPlaybackMixerChannel),
    m_CaptureChannelHelper (comboCaptureMixerChannel)
{
    TQObject::connect(buttonSelectRadioDevice, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(selectRadioDevice()));
    editRadioDevice->installEventFilter(this);
    TQObject::connect(editMinFrequency, TQT_SIGNAL(valueChanged(int)),
                     this, TQT_SLOT(guiMinFrequencyChanged(int)));
    TQObject::connect(editMaxFrequency, TQT_SIGNAL(valueChanged(int)),
                     this, TQT_SLOT(guiMaxFrequencyChanged(int)));

    TQObject::connect(editDeviceVolume, TQT_SIGNAL(valueChanged(double)),
                     this, TQT_SLOT(slotDeviceVolumeChanged(double)));
    TQObject::connect(editTreble, TQT_SIGNAL(valueChanged(double)),
                     this, TQT_SLOT(slotTrebleChanged(double)));
    TQObject::connect(editBass, TQT_SIGNAL(valueChanged(double)),
                     this, TQT_SLOT(slotBassChanged(double)));
    TQObject::connect(editBalance, TQT_SIGNAL(valueChanged(double)),
                     this, TQT_SLOT(slotBalanceChanged(double)));

    TQObject::connect(sliderDeviceVolume, TQT_SIGNAL(valueChanged(int)),
                     this, TQT_SLOT(slotDeviceVolumeChanged(int)));
    TQObject::connect(sliderTreble, TQT_SIGNAL(valueChanged(int)),
                     this, TQT_SLOT(slotTrebleChanged(int)));
    TQObject::connect(sliderBass, TQT_SIGNAL(valueChanged(int)),
                     this, TQT_SLOT(slotBassChanged(int)));
    TQObject::connect(sliderBalance, TQT_SIGNAL(valueChanged(int)),
                     this, TQT_SLOT(slotBalanceChanged(int)));

    TQObject::connect(comboPlaybackMixerDevice, TQT_SIGNAL(activated(int)),
                     this, TQT_SLOT(slotComboPlaybackMixerSelected(int)));
    TQObject::connect(comboCaptureMixerDevice, TQT_SIGNAL(activated(int)),
                     this, TQT_SLOT(slotComboCaptureMixerSelected(int)));

    sliderBalance->installEventFilter(this);
}


V4LRadioConfiguration::~V4LRadioConfiguration ()
{
}


bool V4LRadioConfiguration::connectI (Interface *i)
{
    bool a = IV4LCfgClient::connectI(i);
    bool b = IFrequencyRadioClient::connectI(i);
    bool c = IRadioDeviceClient::connectI(i);
    bool d = ISoundStreamClient::connectI(i);
    return a || b || c || d;
}


bool V4LRadioConfiguration::disconnectI (Interface *i)
{
    bool a = IV4LCfgClient::disconnectI(i);
    bool b = IFrequencyRadioClient::disconnectI(i);
    bool c = IRadioDeviceClient::disconnectI(i);
    bool d = ISoundStreamClient::disconnectI(i);
    return a || b || c || d;
}

void V4LRadioConfiguration::noticeConnectedI (ISoundStreamServer *s, bool pointer_valid)
{
    ISoundStreamClient::noticeConnectedI(s, pointer_valid);
    if (s && pointer_valid) {
        s->register4_notifyTrebleChanged(this);
        s->register4_notifyBassChanged(this);
        s->register4_notifyBalanceChanged(this);
        s->register4_notifySignalMinQualityChanged(this);

        s->register4_notifyPlaybackChannelsChanged(this);
        s->register4_notifyCaptureChannelsChanged(this);
        s->register4_notifySoundStreamCreated(this);
    }
}

void V4LRadioConfiguration::noticeConnectedSoundClient(ISoundStreamClient::thisInterface *i, bool pointer_valid)
{
    if (i && pointer_valid && i->supportsPlayback()) {
        const TQString &org_mid     = queryPlaybackMixerID();
        bool           org_present = m_PlaybackMixerHelper.contains(org_mid);
        const TQString &mid         = org_present ? m_PlaybackMixerHelper.getCurrentItem() : org_mid;
        const TQString &org_ch      = queryPlaybackMixerChannel();
        const TQString &ch          = org_present ? m_PlaybackChannelHelper.getCurrentText() : org_ch;
        noticePlaybackMixerChanged(mid, ch);
    }
    if (i && pointer_valid && i->supportsCapture()) {
        const TQString &org_mid     = queryCaptureMixerID();
        bool           org_present = m_CaptureMixerHelper.contains(org_mid);
        const TQString &mid         = org_present ? m_CaptureMixerHelper.getCurrentItem() : org_mid;
        const TQString &org_ch      = queryCaptureMixerChannel();
        const TQString &ch          = org_present ? m_CaptureChannelHelper.getCurrentText() : org_ch;
        noticeCaptureMixerChanged(mid, ch);
    }
}


void V4LRadioConfiguration::noticeDisconnectedSoundClient(ISoundStreamClient::thisInterface *i, bool pointer_valid)
{
    if (i && pointer_valid && i->supportsPlayback()) {
        noticePlaybackMixerChanged(queryPlaybackMixerID(), queryPlaybackMixerChannel());
    }
    if (i && pointer_valid && i->supportsCapture()) {
        noticeCaptureMixerChanged (queryCaptureMixerID(), queryCaptureMixerChannel());
    }
}

// IV4LCfgClient

bool V4LRadioConfiguration::noticeRadioDeviceChanged(const TQString &s)
{
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;

    editRadioDevice->setText(s);

    m_ignoreGUIChanges = old;
    return true;
}


bool V4LRadioConfiguration::noticePlaybackMixerChanged(const TQString &_mixer_id, const TQString &Channel)
{
    TQString mixer_id = _mixer_id;
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;

    m_PlaybackMixerHelper.setData(getPlaybackClientDescriptions());
    m_PlaybackMixerHelper.setCurrentItem(mixer_id);
    mixer_id = m_PlaybackMixerHelper.getCurrentItem();

    ISoundStreamClient *mixer = getSoundStreamClientWithID(mixer_id);
    if (mixer) {
        m_PlaybackChannelHelper.setData(mixer->getPlaybackChannels());
        m_PlaybackChannelHelper.setCurrentText(m_PlaybackChannelHelper.contains(Channel) ? Channel : queryPlaybackMixerChannel());
    }
    labelPlaybackMixerChannel->setEnabled(mixer != NULL);
    comboPlaybackMixerChannel->setEnabled(mixer != NULL);

    m_ignoreGUIChanges = old;
    return true;
}


bool V4LRadioConfiguration::noticeCaptureMixerChanged(const TQString &_mixer_id, const TQString &Channel)
{
    TQString mixer_id = _mixer_id;
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;

    m_CaptureMixerHelper.setData(getCaptureClientDescriptions());
    m_CaptureMixerHelper.setCurrentItem(mixer_id);
    mixer_id = m_CaptureMixerHelper.getCurrentItem();

    ISoundStreamClient *mixer = getSoundStreamClientWithID(mixer_id);
    if (mixer) {
        m_CaptureChannelHelper.setData(mixer->getCaptureChannels());
        m_CaptureChannelHelper.setCurrentText(m_CaptureChannelHelper.contains(Channel) ? Channel : queryCaptureMixerChannel());
    }
    labelCaptureMixerChannel->setEnabled(mixer != NULL);
    comboCaptureMixerChannel->setEnabled(mixer != NULL);

    m_ignoreGUIChanges = old;
    return true;
}


bool V4LRadioConfiguration::noticeDeviceVolumeChanged(float v)
{
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;
    v = v > 1 ? 1 : v;
    v = v < 0 ? 0 : v;

    if (!m_myControlChange)
        m_orgDeviceVolume = v;

    editDeviceVolume  ->setValue(v);
    sliderDeviceVolume->setValue(m_caps.maxVolume - m_caps.intGetVolume(v));
    m_ignoreGUIChanges = old;
    return true;
}


bool V4LRadioConfiguration::noticeCapabilitiesChanged(const V4LCaps &c)
{
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;

    labelDeviceVolume ->setEnabled(c.hasVolume);
    editDeviceVolume  ->setEnabled(c.hasVolume);
    editDeviceVolume  ->setRange(0, 1, c.volumeStep(), false);
    sliderDeviceVolume->setMinValue(0);
    sliderDeviceVolume->setMaxValue(c.maxVolume - c.minVolume);
    sliderDeviceVolume->setEnabled(c.hasVolume);

    labelTreble ->setEnabled(c.hasTreble);
    editTreble  ->setEnabled(c.hasTreble);
    editTreble  ->setRange(0, 1, c.trebleStep(), false);
    sliderTreble->setMinValue(0);
    sliderTreble->setMaxValue(c.maxTreble - c.minTreble);
    sliderTreble->setEnabled(c.hasTreble);

    labelBass ->setEnabled(c.hasBass);
    editBass  ->setEnabled(c.hasBass);
    editBass  ->setRange(0, 1, c.bassStep(), false);
    sliderBass->setMinValue(0);
    sliderBass->setMaxValue(c.maxBass - c.minBass);
    sliderBass->setEnabled(c.hasBass);

    labelBalance ->setEnabled(c.hasBalance);
    editBalance  ->setEnabled(c.hasBalance);
    editBalance  ->setRange(-1, 1, c.balanceStep(), false);
    sliderBalance->setMinValue(0);
    sliderBalance->setMaxValue(c.maxBalance - c.minBalance);
    sliderBalance->setEnabled(c.hasBalance);

    m_caps = c;

    float tmp = 0;
    noticeDeviceVolumeChanged(queryDeviceVolume());

    queryTreble(m_SoundStreamID, tmp);
    noticeTrebleChanged(m_SoundStreamID, tmp);

    queryBass(m_SoundStreamID, tmp);
    noticeBassChanged(m_SoundStreamID, tmp);

    queryBalance(m_SoundStreamID, tmp);
    noticeBalanceChanged(m_SoundStreamID, tmp);

    m_ignoreGUIChanges = old;
    return true;
}

bool V4LRadioConfiguration::noticeActivePlaybackChanged(bool a)
{
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;
    m_checkboxActivePlayback->setChecked(a);
    m_ignoreGUIChanges = old;
    return true;
}

bool V4LRadioConfiguration::noticeMuteOnPowerOffChanged(bool a)
{
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;
    m_checkboxMuteOnPowerOff->setChecked(a);
    m_ignoreGUIChanges = old;
    return true;
}

bool V4LRadioConfiguration::noticeVolumeZeroOnPowerOffChanged(bool a)
{
    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;
    m_checkboxVolumeZeroOnPowerOff->setChecked(a);
    m_ignoreGUIChanges = old;
    return true;
}

// IRadioDeviceClient

bool V4LRadioConfiguration::noticeDescriptionChanged (const TQString &s, const IRadioDevice */*sender*/)
{
    labelDescription->setText(s);
    return true;
}


// IFrequencyRadioClient

bool V4LRadioConfiguration::noticeFrequencyChanged(float /*f*/, const RadioStation */*s*/)
{
    return false;  // we don't care
}


bool V4LRadioConfiguration::noticeMinMaxFrequencyChanged(float min, float max)
{
    editMinFrequency->setValue((int)rint(min*1000));
    editMaxFrequency->setValue((int)rint(max*1000));
    return true;
}


bool V4LRadioConfiguration::noticeDeviceMinMaxFrequencyChanged(float min, float max)
{
    editMinFrequency->setMinValue((int)rint(min*1000));
    editMaxFrequency->setMaxValue((int)rint(max*1000));
    return true;
}


bool V4LRadioConfiguration::noticeScanStepChanged(float s)
{
    editScanStep->setValue((int)rint(s * 1000));
    return true;
}


// IRadioSoundClient

bool V4LRadioConfiguration::noticeTrebleChanged(SoundStreamID id, float t)
{
    if (id != m_SoundStreamID)
        return false;

    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;
    t = t > 1 ? 1 : t;
    t = t < 0 ? 0 : t;

    if (!m_myControlChange)
        m_orgTreble = t;

    editTreble  ->setValue  (t);
    sliderTreble->setValue(m_caps.maxTreble - m_caps.intGetTreble(t));
    m_ignoreGUIChanges = old;
    return true;
}


bool V4LRadioConfiguration::noticeBassChanged(SoundStreamID id, float b)
{
    if (id != m_SoundStreamID)
        return false;

    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;
    b = b > 1 ? 1 : b;
    b = b < 0 ? 0 : b;

    if (!m_myControlChange)
        m_orgBass = b;

    editBass  ->setValue(b);
    sliderBass->setValue(m_caps.maxBass - m_caps.intGetBass(b));
    m_ignoreGUIChanges = old;
    return true;
}


bool V4LRadioConfiguration::noticeBalanceChanged(SoundStreamID id, float b)
{
    if (id != m_SoundStreamID)
        return false;

    bool old = m_ignoreGUIChanges;
    m_ignoreGUIChanges = true;
    b = b >  1 ?  1 : b;
    b = b < -1 ? -1 : b;

    if (!m_myControlChange)
        m_orgBalance = b;

    editBalance  ->setValue(b);
    sliderBalance->setValue(m_caps.intGetBalance(b));
    m_ignoreGUIChanges = old;
    return true;
}


bool V4LRadioConfiguration::noticeSignalMinQualityChanged(SoundStreamID id, float q)
{
    if (id != m_SoundStreamID)
        return false;

    editSignalMinQuality->setValue((int)rint(q * 100));
    return true;
}


bool V4LRadioConfiguration::noticeSoundStreamCreated(SoundStreamID id)
{
    if (id.HasSamePhysicalID(m_SoundStreamID)) {
        m_SoundStreamID = id;
    }
    return true;
}


// GUI Slots


void V4LRadioConfiguration::selectRadioDevice()
{
    KFileDialog fd("/dev/",
                   i18n("any ( * )").ascii(),
                   this,
                   i18n("Radio Device Selection").ascii(),
                   TRUE);
    fd.setMode(KFile::File | KFile::ExistingOnly);
    fd.setCaption (i18n("Select Radio Device"));

    if (fd.exec() == TQDialog::Accepted) {
        editRadioDevice->setText(fd.selectedFile());
    }
}


bool V4LRadioConfiguration::eventFilter(TQObject *o, TQEvent *e)
{
    if (e->type() == TQEvent::FocusOut && TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(editRadioDevice)) {
        slotEditRadioDeviceChanged();
    }
    if (e->type() == TQEvent::MouseButtonDblClick && TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(sliderBalance)) {
        slotBalanceCenter();
    }
    return false;
}


void V4LRadioConfiguration::slotEditRadioDeviceChanged()
{
    if (m_ignoreGUIChanges) return;
    const TQString &s = editRadioDevice->text();
    if (s != queryRadioDevice() || !queryIsPowerOn()) {
        V4LCaps c = queryCapabilities(s);
        noticeDescriptionChanged(c.description);
    } else {
        noticeDescriptionChanged(queryDescription());
    }
}


void V4LRadioConfiguration::slotComboPlaybackMixerSelected(int /*idx*/)
{
    if (m_ignoreGUIChanges) return;
    TQString id = m_PlaybackMixerHelper.getCurrentItem();
    noticePlaybackMixerChanged(id, queryPlaybackMixerChannel());
}


void V4LRadioConfiguration::slotComboCaptureMixerSelected(int /*idx*/)
{
    if (m_ignoreGUIChanges) return;
    TQString id = m_CaptureMixerHelper.getCurrentItem();
    noticeCaptureMixerChanged(id, queryCaptureMixerChannel());
}


void V4LRadioConfiguration::slotOK()
{
    sendMinFrequency(((float)editMinFrequency->value()) / 1000.0);
    sendMaxFrequency(((float)editMaxFrequency->value()) / 1000.0);
    sendSignalMinQuality(m_SoundStreamID, editSignalMinQuality->value() * 0.01);
    sendRadioDevice(editRadioDevice->text());
    sendScanStep(((float)editScanStep->value()) / 1000.0);

    sendCaptureMixer (m_CaptureMixerHelper.getCurrentItem(),
                      m_CaptureChannelHelper.getCurrentText());
    sendPlaybackMixer(m_PlaybackMixerHelper.getCurrentItem(),
                      m_PlaybackChannelHelper.getCurrentText());

    sendActivePlayback(m_checkboxActivePlayback->isChecked());
    sendMuteOnPowerOff(m_checkboxMuteOnPowerOff->isChecked());
    sendVolumeZeroOnPowerOff(m_checkboxVolumeZeroOnPowerOff->isChecked());

    queryTreble (m_SoundStreamID, m_orgTreble);
    queryBass   (m_SoundStreamID, m_orgBass);
    queryBalance(m_SoundStreamID, m_orgBalance);
    m_orgDeviceVolume = queryDeviceVolume();
}


void V4LRadioConfiguration::slotCancel()
{
    noticeRadioDeviceChanged(queryRadioDevice());
    noticePlaybackMixerChanged(queryPlaybackMixerID(), queryPlaybackMixerChannel());
    noticeCaptureMixerChanged (queryCaptureMixerID(),  queryCaptureMixerChannel());
    noticeMinMaxFrequencyChanged(queryMinFrequency(), queryMaxFrequency());
    noticeActivePlaybackChanged(queryActivePlayback());
    noticeMuteOnPowerOffChanged(queryMuteOnPowerOff());
    noticeVolumeZeroOnPowerOffChanged(queryVolumeZeroOnPowerOff());

    float q = 0;
    querySignalMinQuality(m_SoundStreamID, q);
    noticeSignalMinQualityChanged(m_SoundStreamID, q);
    noticeScanStepChanged(queryScanStep());

    sendTreble      (m_SoundStreamID, m_orgTreble);
    sendBass        (m_SoundStreamID, m_orgBass);
    sendBalance     (m_SoundStreamID, m_orgBalance);
    sendDeviceVolume(m_orgDeviceVolume);
}


void V4LRadioConfiguration::guiMinFrequencyChanged(int v)
{
    editMaxFrequency->setMinValue(v);
}


void V4LRadioConfiguration::guiMaxFrequencyChanged(int v)
{
    editMinFrequency->setMaxValue(v);
}

void V4LRadioConfiguration::slotDeviceVolumeChanged (double v) // for KDoubleNumInput, 0.0..1.0
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendDeviceVolume(v);
    --m_myControlChange;
}

void V4LRadioConfiguration::slotTrebleChanged (double t) // for KDoubleNumInput, 0.0..1.0
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendTreble(m_SoundStreamID, t);
    --m_myControlChange;
}

void V4LRadioConfiguration::slotBassChanged   (double b) // for KDoubleNumInput, 0.0..1.0
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendBass(m_SoundStreamID, b);
    --m_myControlChange;
}

void V4LRadioConfiguration::slotBalanceChanged(double b) // for KDoubleNumInput, -1.0..1.0
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendBalance(m_SoundStreamID, b);
    --m_myControlChange;
}


void V4LRadioConfiguration::slotDeviceVolumeChanged (int v)
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendDeviceVolume(m_caps.floatGetVolume(m_caps.maxVolume - v));
    --m_myControlChange;
}

void V4LRadioConfiguration::slotTrebleChanged (int t)
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendTreble(m_SoundStreamID, m_caps.floatGetTreble(m_caps.maxTreble - t));
    --m_myControlChange;
}

void V4LRadioConfiguration::slotBassChanged   (int b)
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendBass(m_SoundStreamID, m_caps.floatGetBass(m_caps.maxBass - b));
    --m_myControlChange;
}

void V4LRadioConfiguration::slotBalanceChanged(int b)
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendBalance(m_SoundStreamID, m_caps.floatGetBalance(b));
    --m_myControlChange;
}


void V4LRadioConfiguration::slotBalanceCenter()
{
    if (m_ignoreGUIChanges) return;
    ++m_myControlChange;
    sendBalance(m_SoundStreamID, 0);
    --m_myControlChange;
}


bool V4LRadioConfiguration::noticePlaybackChannelsChanged(const TQString & client_id, const TQStringList &/*channels*/)
{
    if (m_PlaybackMixerHelper.getCurrentItem() == client_id) {
        noticePlaybackMixerChanged(client_id, m_PlaybackChannelHelper.getCurrentText());
    }
    return true;
}


bool V4LRadioConfiguration::noticeCaptureChannelsChanged (const TQString & client_id, const TQStringList &/*channels*/)
{
    if (m_CaptureMixerHelper.getCurrentItem() == client_id) {
        noticeCaptureMixerChanged(client_id, m_CaptureChannelHelper.getCurrentText());
    }
    return true;
}



#include "v4lradio-configuration.moc"
