//-*-c++-*-
/***************************************************************************
 *   Copyright (C) 2004 by Fred Schaettgen                                 *
 *   kbluetoothd@schaettgen.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 "scantab.h"
#include "scanjobbase.h"
#include <qcombobox.h>
#include <kapplication.h>
#include <kurllabel.h>
#include <qframe.h>
#include <qgroupbox.h>
#include <qlayout.h>
#include <qlineedit.h>
#include <qradiobutton.h>
#include <qpushbutton.h>
#include <qcheckbox.h>
#include <kdialogbase.h>
#include <qspinbox.h>
#include <qregexp.h>
#include <qdir.h>
#include <kprocess.h>
#include <klistview.h>
#include <qfileinfo.h>
#include <qvalidator.h>
#include <klistview.h>
#include <libkbluetooth/namecache.h>
#include <libkbluetooth/deviceaddress.h>
#include <kmessagebox.h>
#include <klocale.h>
#include "newjobwidget.h"
#include <kdialogbase.h>
#include <qdatetimeedit.h>
#include <kdebug.h>
#include <libkbluetooth/deviceinputwidget.h>
#include "jobtemplatewidget.h"
#include "pageddeviceswidget.h"

using namespace KBluetooth;

ScanTab::ScanTab(QWidget *parent, const char* name) :
    ScanTabBase(parent, name),
    server(KApplication::dcopClient(), "kbluetoothd", "DeviceScanner")
{
    job = new ScanJobBase(jobFrame, "job");
    QHBoxLayout *layout = new QHBoxLayout(jobFrame, 0, 0, "layout");
    layout->addWidget(job);
    connect(jobListView, SIGNAL(selectionChanged()), this, SLOT(slotJobSelected()));
    /*connect(job->useListCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotUseWhiteList()));

    connect(deleteButton, SIGNAL(clicked()), this, SLOT(removeJob()));
    connect(newButton, SIGNAL(clicked()), this, SLOT(addJob()));
    */

    connect(job->removeButton, SIGNAL(clicked()), this, SLOT(removeJobDevice()));
    connect(job->addButton, SIGNAL(clicked()), this, SLOT(addJobDevice()));
    connect(job->executeNow, SIGNAL(leftClickedURL()), this, SLOT(executeJobNow()));
    connect(this->newButton, SIGNAL(clicked()), this, SLOT(addJob()));
    connect(this->deleteButton, SIGNAL(clicked()), this, SLOT(removeJob()));
    connect(this->configureButton, SIGNAL(clicked()), this, SLOT(configureJob()));

    connect(job->intervalNotify, SIGNAL(toggled(bool)),
        job->intervalTimeout, SLOT(setEnabled(bool)));
    connect(searchEnabled, SIGNAL(toggled(bool)),
        searchInterval, SLOT(setEnabled(bool)));

    connect(searchEnabled, SIGNAL(toggled(bool)), this, SIGNAL(dirty()));
    connect(searchInterval, SIGNAL(valueChanged(const QTime&)), this, SIGNAL(dirty()));

    connect(job->whitelistButton, SIGNAL(toggled(bool)), this, SIGNAL(dirty()));
    connect(job->blacklistButton, SIGNAL(toggled(bool)), this, SIGNAL(dirty()));
    connect(job->searchAnyButton, SIGNAL(toggled(bool)), this, SIGNAL(dirty()));

    connect(job->whitelistButton, SIGNAL(toggled(bool)), this, SLOT(updateJobUIfromUI()));
    connect(job->blacklistButton, SIGNAL(toggled(bool)), this, SLOT(updateJobUIfromUI()));
    connect(job->searchAnyButton, SIGNAL(toggled(bool)), this, SLOT(updateJobUIfromUI()));

    connect(job->callTimeout, SIGNAL(valueChanged(const QTime&)), this, SIGNAL(dirty()));
    connect(job->intervalTimeout, SIGNAL(valueChanged(const QTime&)), this, SIGNAL(dirty()));
    connect(job->intervalNotify, SIGNAL(toggled(bool)), this, SIGNAL(dirty()));
    //connect(job->foundNotify, SIGNAL(toggled(bool)), this, SIGNAL(dirty()));
    //connect(job->lostNotify, SIGNAL(toggled(bool)), this, SIGNAL(dirty()));
    connect(jobListView, SIGNAL(clicked(QListViewItem*)), this, SIGNAL(dirty()));

    connect(searchInterval, SIGNAL(valueChanged(const QTime&)), this, SIGNAL(dirty()));

    connect(templateFolderLabel, SIGNAL(leftClickedURL()), this, SLOT(showTemplateFolder()));
    connect(scriptFolderLabel, SIGNAL(leftClickedURL()), this, SLOT(showScriptFolder()));

    pagedDevicesDlg = new KDialogBase(this, "pageddlg", true,
        i18n("Non-Discoverable Devices"), KDialogBase::Close);
    pagedDevicesWidget = new PagedDevicesWidget(pagedDevicesDlg);
    pagedDevicesDlg->setMainWidget(pagedDevicesWidget);
    connect(this->pageLabel, SIGNAL(leftClickedURL()), this, SLOT(showPagedDevices()));
    connect(pagedDevicesWidget, SIGNAL(dirty()), this, SIGNAL(dirty()));

    reloadDiscoverySettings();
}

void ScanTab::slotJobSelected()
{
    updateJobDataFromUI();
    if (jobListView->selectedItem() != NULL) {
        currentJobName = jobListView->selectedItem()->text(0);
    }
    else {
        if (jobListView->childCount() > 0) {
            QListViewItem *firstItem = jobListView->firstChild();
            jobListView->setSelected(firstItem, true);
            currentJobName = firstItem->text(0);
        }
    }
    updateJobUIFromData();
}

void ScanTab::reloadDiscoverySettings()
{
    QString selectedJob;
    if (jobListView->selectedItem() != NULL) {
        selectedJob = jobListView->selectedItem()->text(0);
    }
    jobInfos.clear();
    jobListView->clear();

    if (server.call("getJobs()") == "QStringList")
    {
        QStringList jobList;
        server.ret() >> jobList;
        for (size_t nJob = 0; nJob < jobList.size(); ++nJob) {
            JobInfo jobInfo;
            QString jobName = jobList[nJob];
            jobInfo.name = jobName;
            bool jobEnabled;

            server.args() << jobName;
            if (server.call("getJobEnabled(QString)") == "bool") {
                server.ret() >> jobEnabled;
            }
            else continue;
            kdDebug() << "Job " << jobName << " enabled: " << jobEnabled<< endl;

            server.args() << jobName;
            if (server.call("getUseJobList(QString)") == "bool") {
                server.ret() >> jobInfo.useJobList;
            }
            else continue;

            server.args() << jobName;
            if (server.call("getIsWhitelist(QString)") == "bool") {
                server.ret() >> jobInfo.isWhitelist;
            }
            else continue;

            server.args() << jobName;
            if (server.call("getIntervalNotification(QString)") == "int") {
                server.ret() >> jobInfo.notifyInterval;
            }
            else continue;

            server.args() << jobName;
            if (server.call("getJobMinExecInterval(QString)") == "int") {
                server.ret() >> jobInfo.minExecInterval;
            }
            else continue;

            server.args() << jobName;
            if (server.call("getJobDeviceList(QString)") == "QStringList") {
                server.ret() >> jobInfo.deviceList;
            }
            else continue;

            jobInfos[jobName] = jobInfo;
            QCheckListItem *item = new QCheckListItem(jobListView, jobName,
                QCheckListItem::CheckBox);
            item->setOn(jobEnabled);

            if (jobName == selectedJob) {
                jobListView->setSelected(item, true);
            }
        }
    }

    if (server.call("getInquiryInterval()") == "int") {
        int interval;
        server.ret() >> interval;
        if (interval == 0) {
            searchEnabled->setChecked(false);
            searchInterval->setEnabled(false);
        }
        else {
            searchEnabled->setChecked(true);
            searchInterval->setEnabled(true);
            searchInterval->setTime(QTime().addSecs(interval));
        }
    }

    pagedDevicesWidget->devInfos.clear();
    if (server.call("getPagedDevices()") == "QStringList") {
        QStringList pagedDevices;
        server.ret() >> pagedDevices;
        for (size_t n=0; n<pagedDevices.size(); ++n) {
            server.args() << pagedDevices[n];
            if (server.call("getPageInterval(QString)") == "int") {
                int pageInterval;
                server.ret() >> pageInterval;
                PagedDevicesWidget::DevInfo devInfo;
                devInfo.interval = pageInterval;
                pagedDevicesWidget->devInfos[pagedDevices[n]] = devInfo;
            }
        }
    }
    pagedDevicesWidget->updateInterface();

    slotJobSelected();
}

void ScanTab::updateJobUIFromData()
{
    if (currentJobName == QString()) {
        jobBox->setEnabled(false);
    }
    else {
        jobBox->setEnabled(true);
        JobInfo& jobInfo = jobInfos[currentJobName];
        QString jobname = jobInfo.name;

        if (jobInfo.useJobList) {
            if (jobInfo.isWhitelist) {
                job->whitelistButton->setChecked(true);
            }
            else {
                job->blacklistButton->setChecked(true);
            }
        }
        else job->searchAnyButton->setChecked(true);

        //job->enabledBox->setChecked(jobInfo.enabled);
        //job->foundNotify->setChecked(jobInfo.notifyFound);
        //job->lostNotify->setChecked(jobInfo.notifyLost);
        job->callTimeout->setTime(QTime().addSecs(jobInfo.minExecInterval));

        if (jobInfo.notifyInterval > 0) {
            job->intervalNotify->setChecked(true);
            job->intervalTimeout->setEnabled(true);
            job->intervalTimeout->setTime(QTime().addSecs(jobInfo.notifyInterval));
        }
        else {
            job->intervalNotify->setChecked(false);
            job->intervalTimeout->setEnabled(false);
        }

        job->deviceList->setUpdatesEnabled(false);
        job->deviceList->clear();
        for (size_t nDev = 0; nDev < jobInfo.deviceList.size(); ++nDev) {
            QString devName = jobInfo.deviceList[nDev];
            NameCache::getCachedName(DeviceAddress(jobInfo.deviceList[nDev]), devName);
            job->deviceList->insertItem(devName);

        }
        job->deviceList->setUpdatesEnabled(true);
        job->deviceList->repaint();

        /*job->listView->clear();
        for (size_t n=0; n<inquiryDevices.size(); ++n) {
            QString curDev = inquiryDevices[n];
            if (pagedDevices.contains(curDev)) continue;
            QCheckListItem *listItem = new QCheckListItem(job->listView, "",
                QCheckListItem::CheckBox);
            QString devName = curDev;
            KBluetoothd::NameCache::getCachedName(KBluetooth::DeviceAddress(curDev), devName);
            listItem->setText(NameColumn, devName);
            listItem->setText(AddrColumn, curDev);
        }
        for (size_t n=0; n<pagedDevices.size(); ++n) {
            QString curDev = pagedDevices[n];
            QCheckListItem *listItem = new QCheckListItem(job->listView, "",
                QCheckListItem::CheckBox);
            QString devName = curDev;
            KBluetoothd::NameCache::getCachedName(KBluetooth::DeviceAddress(curDev), devName);
            listItem->setText(NameColumn, devName);
            listItem->setText(AddrColumn, curDev);
            listItem->setOn(true);
        }
        currentJobName = jobname;*/
    }
    updateJobUIfromUI();
}

void ScanTab::updateJobUIfromUI()
{
    // This function grays out widgets based on
    // other widgets.
    bool useJobList = !job->searchAnyButton->isChecked();
    job->addButton->setEnabled(useJobList);
    job->removeButton->setEnabled(useJobList);
    job->deviceList->setEnabled(useJobList);
}

void ScanTab::updateJobDataFromUI()
{
    if (currentJobName == QString()) {
       return;
    }

    JobInfo& jobInfo = jobInfos[currentJobName];

    //jobInfo.enabled = job->enabledBox->isChecked();
    //jobInfo.deviceList =
    jobInfo.useJobList = !job->searchAnyButton->isChecked();
    if (jobInfo.useJobList) {
        jobInfo.isWhitelist = job->whitelistButton->isChecked();
    }
    jobInfo.minExecInterval = int(QTime().secsTo(job->callTimeout->time()));
    if (job->intervalNotify->isChecked()) {
        jobInfo.notifyInterval = int(QTime().secsTo(job->intervalTimeout->time()));
    }
    else {
        jobInfo.notifyInterval = 0;
    }
    //jobInfo.notifyFound = job->foundNotify->isChecked();
    //jobInfo.notifyLost = job->lostNotify->isChecked();
}

void ScanTab::apply()
{
    updateJobDataFromUI();
    std::map<QString,JobInfo>::iterator jobIt;
    QListViewItem* listItem;
    for (listItem = jobListView->firstChild(); listItem; listItem = listItem->nextSibling()) {
        const JobInfo& jobInfo = jobInfos[listItem->text(0)];

        QString jobname = jobInfo.name;

        server.args() << jobname << jobInfo.useJobList;
        server.call("setUseJobList(QString,bool)");

        if (jobInfo.useJobList) {
            server.args() << jobname << jobInfo.isWhitelist;
            server.call("setIsWhitelist(QString,bool)");
        }

        server.args() << jobname << ((QCheckListItem*)listItem)->isOn();
        server.call("setJobEnabled(QString,bool)");

        /*server.args() << jobname << jobInfo.notifyFound;
        server.call("setFoundNotification(QString,bool)");

        server.args() << jobname << jobInfo.notifyLost;
        server.call("setLeaveNotification(QString,bool)");*/

        server.args() << jobname << jobInfo.minExecInterval;
        server.call("setJobMinExecInterval(QString,int)");

        server.args() << jobname << jobInfo.notifyInterval;
        server.call("setIntervalNotification(QString,int)");

        QStringList jobDeviceList;
        server.args() << jobname;
        if (server.call("getJobDeviceList(QString)") == "QStringList") {
            server.ret() >> jobDeviceList;
        }
        for (size_t nDev = 0; nDev < jobDeviceList.size(); ++nDev) {
            server.args() << jobname << jobDeviceList[nDev];
            server.call("removeJobDevice(QString,QString)");
        }
        for (size_t nDev = 0; nDev < jobInfo.deviceList.size(); ++nDev) {
            server.args() << jobname << jobInfo.deviceList[nDev];
            server.call("addJobDevice(QString,QString)");
        }
    }


    if (searchEnabled->isChecked()) {
        server.args() << QTime().secsTo(searchInterval->time());
    }
    else {
        server.args() << 0;
    }
    server.call("setInquiryInterval(int)");

    // Clear old list of paged devices
    QStringList oldPagedDevices;
    if (server.call("getPagedDevices()") == "QStringList") {
        server.ret() >> oldPagedDevices;
        for (size_t n=0; n<oldPagedDevices.size(); ++n) {
            server.args() << oldPagedDevices[n];
            server.call("removePagedDevice(QString)");
        }
    }

    // Add new list of paged devices
    std::map<QString, PagedDevicesWidget::DevInfo>::iterator infoIt;
    for (infoIt = pagedDevicesWidget->devInfos.begin();
         infoIt != pagedDevicesWidget->devInfos.end(); ++infoIt)
    {
        server.args() << infoIt->first << infoIt->second.interval;
        server.call("setPageInterval(QString,int)");
    }
}

void ScanTab::addJobDevice()
{
    if (currentJobName != QString()) {
        JobInfo& jobInfo = jobInfos[currentJobName];
        DeviceAddress addr;
        if (DeviceInputWidget::showSelectionDialog(this, addr, true)) {
            if (jobInfo.deviceList.contains(QString(addr)) == false) {
                jobInfo.deviceList.append(QString(addr));
                updateJobUIFromData();
            }
        }
    }
}

void ScanTab::removeJobDevice()
{
    if (currentJobName != QString()) {
        JobInfo& jobInfo = jobInfos[currentJobName];
        DeviceAddress addr;
        int sel = job->deviceList->currentItem();
        if (sel >= 0) {
            jobInfo.deviceList.remove(jobInfo.deviceList.at(sel));
            updateJobUIFromData();
        }
    }
}

void ScanTab::executeJobNow()
{
    if (currentJobName != QString()) {
        JobInfo& jobInfo = jobInfos[currentJobName];
        server.args() << jobInfo.name;
        server.call("executeJob(QString)");
    }
}

void ScanTab::addJob() {
    KDialogBase dialog(this, "jobtemplatedlg", true,
        i18n("Job Template"), KDialogBase::Ok|KDialogBase::Cancel);
    JobTemplateWidget *widget = new JobTemplateWidget(&dialog);
    dialog.setMainWidget(widget);

    // Add the available templates to the list
    if (server.call("getJobTemplateDir()") != "QString") {
        kdWarning() << "Could not get job template dir." << endl;
        return;
    }
    QString templateDirName;
    server.ret() >> templateDirName;
    QDir templateDir(templateDirName);
    templateDir.setNameFilter("*.template");
    templateDir.setFilter(QDir::Files);
    QStringList templateNames = templateDir.entryList();
    for (size_t n=0; n<templateNames.size(); ++n) {
        QString tempName = templateNames[n];
        widget->jobTemplates->insertItem(tempName.left(
            tempName.length()-QString(".template").length()));
    }

    if (dialog.exec() == QDialog::Accepted) {
        if (widget->jobTemplates->currentItem() >= 0) {
            QString templateName = widget->jobTemplates->text(
                widget->jobTemplates->currentItem());
            QString jobName = widget->jobName->text();
            QRegExp reg("^[A-Za-z0-9_-]+$");
            if (reg.search(jobName) >= 0) {

                // Get the target file name
                if (server.call("getJobDir()") != "QString") {
                    kdWarning() << "Could not get job dir." << endl;
                    return;
                }
                QString jobDirName;
                server.ret() >>jobDirName;
                QString jobPathName = QDir(jobDirName).filePath(jobName);
                if (QFileInfo(jobPathName).exists()) {
                    KMessageBox::information(this,
                        i18n("A job with the called %1 does already exist.").arg(jobName));
                    return;
                }

                // Add the job
                QString templatePathName = QDir(templateDir).filePath(
                    templateNames[widget->jobTemplates->currentItem()]);
                KProcess cpProc;
                cpProc << "cp" << templatePathName << jobPathName;
                cpProc.start(KProcess::Block);
                KProcess chmodProc;
                chmodProc << "chmod" << "u+rx" << jobPathName;
                chmodProc.start(KProcess::Block);
                kdDebug() << "copy " << templatePathName << " to " << jobPathName << endl;

                // Let kbluetooth rescan the list of jobs
                server.call("reloadJobs()");

                reloadDiscoverySettings();
                updateJobUIFromData();
            }
            else {
                KMessageBox::information(this, i18n(
"The job name may only contain the characters A-Z, a-z, 0-9, - and _"));
            }
        }
        else {
            KMessageBox::information(this,
                i18n("You have to select a template for your new job."));
        }
    }
    delete widget;
}

void ScanTab::removeJob()
{
    if (currentJobName != QString()) {
        QString jobName = jobInfos[currentJobName].name;
        if (server.call("getJobDir()") != "QString") {
            kdWarning() << "Could not get job dir." << endl;
            return;
        }
        QString jobDirName;
        server.ret() >> jobDirName;
        if (KMessageBox::warningContinueCancel(this, i18n("<p>Do you really want to delete the job <b>%1</b>?</p>").arg(jobName), i18n("Device Discovery Service"),KGuiItem(i18n("&Delete"),"editdelete")) ==
            KMessageBox::Continue)
        {
            QDir(jobDirName).remove(jobName);

            // Let kbluetooth rescan the list of jobs
            server.call("reloadJobs()");
            currentJobName = QString();
            reloadDiscoverySettings();
            updateJobUIFromData();
        }
    }
}

void ScanTab::configureJob()
{
    if (currentJobName != QString()) {
        JobInfo& jobInfo = jobInfos[currentJobName];
        server.args() << jobInfo.name;
        server.call("configureJob(QString)");
    }
}

void ScanTab::showPagedDevices()
{
    pagedDevicesDlg->exec();
}

void ScanTab::defaults()
{

}

void ScanTab::showScriptFolder()
{
    if (server.call("getJobDir()") != "QString") {
        kdWarning() << "Could not get job dir." << endl;
        return;
    }
    QString jobDirName;
    server.ret() >> jobDirName;

    KApplication::kApplication()->invokeBrowser(jobDirName);
}

void ScanTab::showTemplateFolder()
{
    if (server.call("getJobTemplateDir()") != "QString") {
        kdWarning() << "Could not get job template dir." << endl;
        return;
    }
    QString templateDirName;
    server.ret() >> templateDirName;

    KApplication::kApplication()->invokeBrowser(templateDirName);
}


ScanTab::~ScanTab()
{
}


#include "scantab.moc"
