/***************************************************************************
                          radio-configuration.cpp  -  description
                             -------------------
    begin                : Son Aug 3 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 "../../src/include/radiostation.h"
#include "../../src/include/stationlist.h"
#include "../../src/include/plugins.h"
#include "../../src/include/radiodevice_interfaces.h"
#include "../../src/include/standardscandialog.h"
#include "../../src/include/radiostation-listview.h"
#include "../../src/include/radiostation-config.h"
#include "../../src/include/errorlog-interfaces.h"

#include "radio-configuration.h"

#include <math.h>

#include <tqlistbox.h>
#include <tdelistbox.h>
#include <tqdatetimeedit.h>
#include <tqlineedit.h>
#include <tqlabel.h>
#include <tqspinbox.h>
#include <tqpushbutton.h>
#include <tqpopupmenu.h>
#include <tqtoolbutton.h>
#include <tqwidgetstack.h>
#include <tqimage.h>

#include <tdefiledialog.h>
#include <kstandarddirs.h>
#include <kurllabel.h>
#include <tqregexp.h>
#include <krun.h>
#include <kurlrequester.h>
#include <tdelocale.h>

RadioConfiguration::RadioConfiguration (TQWidget *parent, const IErrorLogClient &logger)
    : RadioConfigurationUI(parent),
      ignoreChanges(false),
      devicePopup(NULL),
      m_logger(logger),
      m_dirty(true)
{
    TQObject::connect(listStations, TQT_SIGNAL(sigCurrentStationChanged(int)),
                     this, TQT_SLOT(slotStationSelectionChanged(int)));
    TQObject::connect(buttonSelectPixmapFile, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotSelectPixmap()));
    TQObject::connect(buttonNewStation, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotNewStation()));
    TQObject::connect(buttonDeleteStation, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotDeleteStation()));
    TQObject::connect(editPixmapFile, TQT_SIGNAL(textChanged(const TQString &)),
                     this, TQT_SLOT(slotPixmapChanged(const TQString &)));
    TQObject::connect(editStationName, TQT_SIGNAL(textChanged(const TQString &)),
                     this, TQT_SLOT(slotStationNameChanged(const TQString &)));
    TQObject::connect(editStationShortName, TQT_SIGNAL(textChanged(const TQString &)),
                     this, TQT_SLOT(slotStationShortNameChanged(const TQString &)));
    TQObject::connect(editVolumePreset, TQT_SIGNAL(valueChanged(int)),
                     this, TQT_SLOT(slotVolumePresetChanged(int)));
    TQObject::connect(buttonStationUp, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotStationUp()));
    TQObject::connect(buttonStationDown, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotStationDown()));
    TQObject::connect(listStations, TQT_SIGNAL(sigStationActivated(int)),
                     this, TQT_SLOT(slotActivateStation( int )));
    TQObject::connect(buttonLoadPresets, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotLoadPresets()));
    TQObject::connect(buttonStorePresets, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotStorePresets()));
    TQObject::connect(buttonLastChangeNow, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotLastChangeNow()));

    connect(editMaintainer, TQT_SIGNAL(textChanged(const TQString &)),    TQT_SLOT(slotSetDirty()));
    connect(editLastChange, TQT_SIGNAL(valueChanged(const TQDateTime &)), TQT_SLOT(slotSetDirty()));
    connect(editCountry,    TQT_SIGNAL(textChanged(const TQString &)),    TQT_SLOT(slotSetDirty()));
    connect(editCity,       TQT_SIGNAL(textChanged(const TQString &)),    TQT_SLOT(slotSetDirty()));
    connect(editMedia,      TQT_SIGNAL(textChanged(const TQString &)),    TQT_SLOT(slotSetDirty()));
    connect(editComment,    TQT_SIGNAL(textChanged(const TQString &)),    TQT_SLOT(slotSetDirty()));
    connect(editPresetFile, TQT_SIGNAL(textChanged(const TQString &)),    TQT_SLOT(slotSetDirty()));

    mailLabel->setText("mailto:witte-presets@kawo1.rwth-aachen.de");
    mailLabel->setURL ("mailto:witte-presets@kawo1.rwth-aachen.de");
    TQObject::connect(mailLabel, TQT_SIGNAL(leftClickedURL(const TQString &)),
                     this, TQT_SLOT(slotSendPresetsByMail(const TQString &)));

    TQObject::connect(buttonSearchStations, TQT_SIGNAL(clicked()),
                     this, TQT_SLOT(slotSearchStations0()));

    devicePopup = new TQPopupMenu(buttonSearchStations);
    buttonSearchStations->setPopup(devicePopup);
    TQObject::connect(devicePopup, TQT_SIGNAL(activated(int)),
                     this, TQT_SLOT(slotSearchStations(int)));
}


RadioConfiguration::~RadioConfiguration ()
{
}


bool RadioConfiguration::connectI (Interface *i)
{
    bool a = IRadioClient::connectI(i);
    bool b = IRadioDevicePoolClient::connectI(i);

    return a || b;
}

bool RadioConfiguration::disconnectI (Interface *i)
{
    bool a = IRadioClient::disconnectI(i);
    bool b = IRadioDevicePoolClient::disconnectI(i);

    return a || b;
}

// IRadioDevicePoolClient

bool RadioConfiguration::noticeDevicesChanged(const TQPtrList<IRadioDevice> &l)
{
    TQPtrListIterator<IRadioDevice> it(l);
    devices.clear();
    devicePopup->clear();
    int id = 0;
    for (; it.current(); ++it) {
        IRadioDevice *d = it.current();
        if (dynamic_cast<ISeekRadio*>(d)) {
            devicePopup->insertItem(d->getDescription(), id++);
            devices.append(d);
        }
    }
    return true;
}


bool RadioConfiguration::noticeDeviceDescriptionChanged(const TQString &)
{
    noticeDevicesChanged(queryDevices());
    return true;
}


// IRadioClient

bool RadioConfiguration::noticeStationsChanged(const StationList &sl)
{
    ignoreChanges = true;

    m_stations = sl;

    listStations->setStations(sl);

    StationListMetaData &info = m_stations.metaData();

    editMaintainer->setText(info.maintainer);
    editLastChange->setDateTime(info.lastChange);
    editCountry->setText(info.country);
    editCity->setText(info.city);
    editMedia->setText(info.media);
    editComment->setText(info.comment);

    ignoreChanges = false;

    slotStationSelectionChanged(listStations->currentStationIndex());

    return true;
}


bool RadioConfiguration::noticePresetFileChanged(const TQString &f)
{
    ignoreChanges = true;
    editPresetFile->setURL(f);
    ignoreChanges = false;
    return true;
}


void RadioConfiguration::slotStationSelectionChanged(int idx)
{
    RadioStation *s = NULL;

    if (idx >= 0 && idx < m_stations.count()) {
        s = &m_stations.at(idx);
    }

    editStationName       ->setDisabled(!s);
    labelStationName      ->setDisabled(!s);
    editPixmapFile        ->setDisabled(!s);
    labelPixmapFile       ->setDisabled(!s);
    editStationShortName  ->setDisabled(!s);
    labelStationShortName ->setDisabled(!s);
    editVolumePreset      ->setDisabled(!s);
    labelVolumePreset     ->setDisabled(!s);
    buttonSelectPixmapFile->setDisabled(!s);
    buttonDeleteStation   ->setDisabled(!s);

    buttonStationUp       ->setDisabled(!s || idx == 0);
    buttonStationDown     ->setDisabled(!s || idx == m_stations.count()-1);

    if (ignoreChanges) return;
    ignoreChanges = true;

    editStationName       ->setText  (s ? s->name() : TQString());
    editStationShortName  ->setText  (s ? s->shortName() : TQString());
    editPixmapFile        ->setText  (s ? s->iconName() : TQString());
    editVolumePreset      ->setValue (s ? (int)rint(s->initialVolume()*100) : -1);

    TQPixmap pixmap(s ? s->iconName() : TQString());
    if (!pixmap.isNull()) {
        pixmapStation->setPixmap(pixmap);
    } else {
        pixmapStation->setText("");
    }


    stackStationEdit->setDisabled(!s);
    if (s) {
        RadioStationConfig *c = stationEditors.find(s->getClassName());
        if (!c) {
            c = s->createEditor();
            if (c) {
                c->reparent(this, TQPoint(0,0), true);
                TQObject::connect(c, TQT_SIGNAL(changed(RadioStationConfig*)),
                                 this, TQT_SLOT(slotStationEditorChanged(RadioStationConfig*)));
                stationEditors.insert(s->getClassName(), c);
                stackStationEdit->addWidget(c);
            }
        }
        if (c) {
            c->setStationData(*s);
            stackStationEdit->raiseWidget(c);
        }
    }

    ignoreChanges = false;
}


void RadioConfiguration::slotNewStation()
{
    slotSetDirty();
    const RadioStation *st = &queryCurrentStation();
    int n = m_stations.count();
    m_stations.all().append(st);
    if (m_stations.count() == n) {
        st = st->copyNewID();
        m_stations.all().append(st);
    }
    if (m_stations.count() > n) {
        listStations->appendStation(*st);
        listStations->setCurrentStation (listStations->count()-1);
        slotStationSelectionChanged(listStations->count()-1);
        listStations->ensureItemVisible(listStations->selectedItem());
    }
}


void RadioConfiguration::slotDeleteStation()
{
    int idx = listStations->currentStationIndex();

    if (idx >= 0 && idx < m_stations.count()) {
        slotSetDirty();
        m_stations.all().remove(idx);
        listStations->removeStation(idx);
    }
}


void RadioConfiguration::slotStationEditorChanged(RadioStationConfig *c)
{
    if (!c) return;
    if (ignoreChanges) return;


    int idx = listStations->currentStationIndex();
    if (idx >= 0 && idx < m_stations.count()) {
        slotSetDirty();
        RadioStation &st = m_stations.at(idx);

        ignoreChanges = true;
        bool o = listStations->signalsBlocked();
        listStations->blockSignals(true);

        c->storeStationData(st);
        listStations->setStation(idx, st);

        listStations->blockSignals(o);
        ignoreChanges = false;
    }
}


void RadioConfiguration::slotStationNameChanged( const TQString & s)
{
    if (ignoreChanges) return;

    int idx = listStations->currentStationIndex();
    if (idx >= 0 && idx < m_stations.count()) {
        slotSetDirty();
        RadioStation &st = m_stations.at(idx);
        st.setName(s);
        ignoreChanges = true;
        bool o = listStations->signalsBlocked();
        listStations->blockSignals(true);
        listStations->setStation(idx, st);
        listStations->blockSignals(o);
        ignoreChanges = false;
    }
}


void RadioConfiguration::slotStationShortNameChanged( const TQString & sn)
{
    if (ignoreChanges) return;

    int idx = listStations->currentStationIndex();
    if (idx >= 0 && idx < m_stations.count()) {
        slotSetDirty();
        RadioStation &st = m_stations.at(idx);
        st.setShortName(sn);
        ignoreChanges = true;
        bool o = listStations->signalsBlocked();
        listStations->blockSignals(true);
        listStations->setStation(idx, st);
        listStations->blockSignals(o);
        ignoreChanges = false;
    }
}


void RadioConfiguration::slotSelectPixmap()
{
    KURL url = KFileDialog::getImageOpenURL(TQString(), this,
                                            i18n("Image Selection"));
    if (!url.isEmpty()) {
        if (url.isLocalFile()) {
            editPixmapFile->setText(url.path());
        } else {
            m_logger.logWarning(i18n("ignoring non-local image"));
        }
    }
}


void RadioConfiguration::slotPixmapChanged( const TQString &s )
{
    if (ignoreChanges) return;

    int idx = listStations->currentStationIndex();
    if (idx >= 0 && idx < m_stations.count()) {
        slotSetDirty();
        RadioStation &st = m_stations.at(idx);
        st.setIconName(s);
        ignoreChanges = true;
        pixmapStation->setPixmap(TQPixmap(s));
        bool o = listStations->signalsBlocked();
        listStations->blockSignals(true);
        listStations->setStation(idx, st);
        listStations->blockSignals(o);
        ignoreChanges = false;
    }
}


void RadioConfiguration::slotVolumePresetChanged(int v)
{
    int idx = listStations->currentStationIndex();
    if (idx >= 0 && idx < m_stations.count()) {
        slotSetDirty();
        RadioStation &s = m_stations.at(idx);
        s.setInitialVolume(0.01 * (double)v);
    }
}



void RadioConfiguration::slotStationUp()
{
    int idx = listStations->currentStationIndex();
    if (idx > 0 && idx < m_stations.count()) {
        slotSetDirty();
        RawStationList &sl = m_stations.all();

        RadioStation *st = sl.take(idx-1);
        sl.insert(idx, st);
        delete st;

        ignoreChanges = true;
//         bool o = listStations->signalsBlocked();
//         listStations->blockSignals(true);
        listStations->setStation(idx-1, *sl.at(idx-1));
        listStations->setStation(idx,   *sl.at(idx));
        listStations->setCurrentStation(idx-1);
//         listStations->blockSignals(o);
        ignoreChanges = false;
    }
}


void RadioConfiguration::slotStationDown()
{
    int idx = listStations->currentStationIndex();
    if (idx >= 0 && idx < m_stations.count() - 1) {
        slotSetDirty();
        RawStationList &sl = m_stations.all();

        RadioStation *st = sl.take(idx);
        sl.insert(idx+1, st);
        delete st;

        ignoreChanges = true;
//         bool o = listStations->signalsBlocked();
//         listStations->blockSignals(true);
        listStations->setStation(idx,   *sl.at(idx));
        listStations->setStation(idx+1, *sl.at(idx+1));
        listStations->setCurrentStation(idx+1);
//         listStations->blockSignals(o);
        ignoreChanges = false;
    }
}


void RadioConfiguration::slotActivateStation(int idx)
{
    if (idx >= 0 && idx < m_stations.count()) {
        sendActivateStation(m_stations.at(idx));
        sendPowerOn();
    }
}

void RadioConfiguration::slotLoadPresets()
{
    KFileDialog fd(locate("data", "tderadio/presets/"),
                   ("*.krp|" + i18n("TDERadio Preset Files")).ascii(),
                   this,
                   i18n("Preset File Selection").ascii(),
                   true);
    fd.setMode(KFile::File | KFile::ExistingOnly);
    fd.setCaption (i18n("Select Preset File"));

    if (fd.exec() == TQDialog::Accepted) {
        slotSetDirty();
        StationList sl;
        if (sl.readXML(fd.selectedURL(), m_logger)) {
            noticeStationsChanged(sl);
        }
    }
}


void RadioConfiguration::slotStorePresets()
{
    KFileDialog fd("",
                   ("*.krp|" + i18n("TDERadio Preset Files")).ascii(),
                   this,
                   i18n("Preset File Selection").ascii(),
                   true);
    fd.setMode(KFile::File);
    fd.setCaption (i18n("Store Preset File"));

    if (fd.exec() == TQDialog::Accepted) {
        editPresetFile->setURL(fd.selectedURL().url());
        m_stations.writeXML(fd.selectedURL(), m_logger);
    }
}


void RadioConfiguration::slotLastChangeNow()
{
    slotSetDirty();
    editLastChange->setDateTime(TQDateTime::currentDateTime());
}


static TQString &urlEscapes(TQString &s)
{
    s.replace(TQRegExp("%"),  "%25");
    s.replace(TQRegExp("\t"),  "%09");
    s.replace(TQRegExp("\n"),  "%0A");
    s.replace(TQRegExp("\n"),  "%0D");
    s.replace(TQRegExp(" "),   "%20");
    s.replace(TQRegExp("\\!"), "%21");
    s.replace(TQRegExp("\""),  "%22");
    s.replace(TQRegExp("#"),   "%23");
    s.replace(TQRegExp("\\$"), "%24");
    s.replace(TQRegExp("\\&"), "%26");
    s.replace(TQRegExp("'"),   "%27");
    s.replace(TQRegExp(","),   "%2C");
    s.replace(TQRegExp(":"),   "%3A");
    s.replace(TQRegExp(";"),   "%3B");
    s.replace(TQRegExp("="),   "%3D");
    s.replace(TQRegExp("\\?"), "%3F");
    return s;
}

void RadioConfiguration::slotSendPresetsByMail( const TQString &url )
{
    TQString presets = m_stations.writeXML(m_logger);

    urlEscapes(presets);

    // documentation says, krun object deletes itself,
    // so we do not need to store the pointer

    TQString country = m_stations.metaData().country;
    TQString city    = m_stations.metaData().city;
    TQString location = city + "/" + country;
    urlEscapes(location);

    TQString cmd = url + "?subject=station preset file for " + location + "&body=";

    cmd += presets;
    new KRun (cmd);
}


void RadioConfiguration::slotSearchStations(int idev)
{
    if (idev >= 0 && (unsigned)idev < devices.count()) {
        IRadioDevice *dev = devices.at(idev);

        StandardScanDialog *x = new StandardScanDialog(NULL);
        x->connectI(dev);                                        // connect device
        x->connectI(IRadioDevicePoolClient::iConnections.at(0)); // connect radio to get verbous station information
        sendActiveDevice(dev);
        x->show();
        x->start();
        if (x->exec() == TQDialog::Accepted) {
            slotSetDirty();
            m_stations.merge(x->getStations());
            noticeStationsChanged(m_stations);
        }
        delete x;
//        logDebug("scan finished");
    }
//    logDebug("scan finished completely");
}


void RadioConfiguration::slotOK()
{
    if (m_dirty) {
        StationListMetaData &i = m_stations.metaData();

        i.maintainer = editMaintainer->text();
        i.lastChange = editLastChange->dateTime();
        i.country    = editCountry->text();
        i.city       = editCity->text();
        i.media      = editMedia->text();
        i.comment    = editComment->text();
    
        sendStations(m_stations);
        sendPresetFile(editPresetFile->url());
        m_dirty = false;
    }
}

void RadioConfiguration::slotCancel()
{
    if (m_dirty) {
        noticeStationsChanged(queryStations());
        noticePresetFileChanged(queryPresetFile());
        m_dirty = false;
    }
}


void RadioConfiguration::slotSetDirty()
{
    if (!ignoreChanges) {
        m_dirty = true;
    }
}


#include "radio-configuration.moc"
