/***************************************************************************
 *                                                                         *
 *   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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <khelpmenu.h>
#include <kpopupmenu.h>
#include <kinstance.h>
#include <kapplication.h>
#include <kwin.h>
#include <kdialogbase.h>
#include <kiconloader.h>
#include <kconfig.h>
#include <kglobal.h>

#include <qframe.h>
#include <qvbox.h>
#include <qhbox.h>
#include <qpoint.h>
#include <qaction.h>
#include <qlineedit.h>
#include <qpainter.h>
#include <qvaluelist.h>
#include <qstringlist.h>
#include <qcursor.h>
#include <qmessagebox.h>

#include "kwirelessmonitor.h"
#include "timerthread.h"
#include "configdialog.h"

QValueList<int> availableBitrateList;
QStringList availableBitrateStrList;
int numAvailableBitrates;

KWirelessMonitor::KWirelessMonitor(QWidget *parent, const char *name)
    : KSystemTray(parent, name)
{
    const KAboutData *about = KApplication::kApplication()->aboutData();
    KHelpMenu* help = new KHelpMenu(this, about, false);
    contextMenu()->insertItem("Help", help->menu());

    mActionEnableSettings = new QAction("Enable Settings...",
                                        "Enable Settings...",
                                        QKeySequence(), this);
    connect(mActionEnableSettings, SIGNAL(activated()),
            this, SLOT(slotEnableSettings()));
    mActionEnableSettings->addTo(contextMenu());

    mActionConfigure = new QAction("Configure...",
                                   "Configure...", QKeySequence(), this);
    connect(mActionConfigure, SIGNAL(activated()),
            this, SLOT(slotConfigure()));
    mActionConfigure->addTo(contextMenu());
    
    mConfigDialog = 0;
    mTimerThread = 0;
    mStatusBox = 0;
    mStatusLabel = 0;
    connect(KApplication::kApplication(), SIGNAL(aboutToQuit()),
            this, SLOT(slotDestroyed()));

    mHelper = NULL;
    mHelperFifo = 0;

    mCurInterface = new QString("");
    //mCurPMMode = -1;
    mCurBRMode = -1;
    mCurBRManualIdx = -1;
}

KWirelessMonitor::~KWirelessMonitor()
{
}

void KWirelessMonitor::setTimerThread(TimerThread *tthread)
{
    mTimerThread = tthread;
}

void KWirelessMonitor::slotDestroyed()
{
    if (mTimerThread) {
        mTimerThread->terminate();
    }
    if (mHelper != NULL) {
        mHelper->kill();
        fprintf(mHelperFifo, "OD\n");
        fflush(mHelperFifo);
        fclose(mHelperFifo);
        unlink(HELPER_FIFO);
    }
}

void KWirelessMonitor::slotEnableSettings()
{
    if (!mConfigDialog) {
        mConfigDialog = new ConfigDialog(this, &mHelper, &mHelperFifo);
        connect(mConfigDialog, SIGNAL(applyClicked()),
                this, SLOT(slotConfigApply()));
        connect(mConfigDialog, SIGNAL(okClicked()),
                this, SLOT(slotConfigApply()));
    }

    mConfigDialog->menuEnableSettings();
}

void KWirelessMonitor::slotConfigure()
{
    if (!mConfigDialog) {
        mConfigDialog = new ConfigDialog(this, &mHelper, &mHelperFifo);
        connect(mConfigDialog, SIGNAL(applyClicked()),
                this, SLOT(slotConfigApply()));
        connect(mConfigDialog, SIGNAL(okClicked()),
                this, SLOT(slotConfigApply()));
    }

    if (mConfigDialog->isShown()) {
        return;
    }

    mConfigDialog->show();

    /* XXX compensate for inaccurate frameGeometry() */
    int w = mConfigDialog->frameGeometry().width() + 15;
    int h = mConfigDialog->frameGeometry().height() + 35;
    QRect deskR = QApplication::desktop()->availableGeometry(
                    QApplication::desktop()->primaryScreen());
    QPoint c = QPoint(deskR.right() - w, deskR.bottom() - h);
    mConfigDialog->move(c);
}

void KWirelessMonitor::slotConfigApply()
{
    QString cIF = mConfigDialog->getInterfaceName();
    //int cPMMode = mConfigDialog->getPMMode();
    int cPMM = mConfigDialog->getPMM();
    int cBRMode = mConfigDialog->getBRMode();
    int cBRManualIdx = mConfigDialog->getBRManualIdx();
    bool cPMBattery = mConfigDialog->batteryPMEnabled();
    bool cPMTransfer = mConfigDialog->transferPMEnabled();

    KConfig *config = KGlobal::config();
    config->setGroup("KWirelessMonitor Settings");
    config->writeEntry("Interface", cIF);
    //config->writeEntry("PM", cPMMode);
    config->writeEntry("PMM", cPMM);
    config->writeEntry("BR", cBRMode);
    config->writeEntry("ABR", cBRManualIdx);
    config->writeEntry("PMBattery", cPMBattery);
    config->writeEntry("PMTransfer", cPMTransfer);
       
    if ((mTimerThread) && (!cIF.isEmpty())) {
        mTimerThread->setInterface(cIF);
    } else {
        return;
    }

    if ((mHelper == 0) || (!mHelperFifo)
        || (!mConfigDialog->settingsEnabled())) {
        return;
    }

    QString newESSID = mConfigDialog->getNewESSID();
    QString currentESSID = getESSID();
    
    //if ((newESSID != currentESSID)
    //        || ((*mCurInterface != "") && (cIF != *mCurInterface))) {
    if ((!cIF.isEmpty()) && (!newESSID.isEmpty())
            && (newESSID != currentESSID)) {
        // XXX DHCP loops if PM on? (Orinoco + Linksys)
        int pmgmt = mTimerThread->getPM();
        setPMModeHelper(PMOff);

        QString com = QString("%1\nNW\n%2\n").arg(cIF).arg(newESSID);
        fprintf(mHelperFifo, "%s", com.ascii());
        fflush(mHelperFifo);
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
        usleep(1000000);
        while (true) {
            struct stat sbuf;
            stat(NET_OUT, &sbuf);
            if (sbuf.st_size > 4) {
                break;
            }
            usleep(500000);
        }
        QApplication::restoreOverrideCursor();
        FILE *netout = fopen(NET_OUT, "r");
        bool err = true;
        if (netout) {
            char buf[16];
            fgets(buf, 16, netout);
            if (strlen(buf) > 6) {
                int ret = strtol(buf + 5, NULL, 10);
                if (ret == 0) {
                    err = false;
                }
            }
        }
        int qual = mTimerThread->getQuality();
        if (err || (0 == qual)) {
            QMessageBox::information(this, "KWirelessMonitor",
                                     "Error connecting to network "
                                     + newESSID);
        }
        setPMModeHelper(pmgmt);
    }
    QString comm("");
    /*
    if ((cPMM == PMManual) &&
            ((cPMMode != mCurPMMode) || (cIF != *mCurInterface))) {
        comm += QString("%1\nPM\n%2\n").arg(cIF).arg(cPMMode);
    }
    */
    if ((!cIF.isEmpty())
            && ((cBRMode != mCurBRMode) || (cBRManualIdx != mCurBRManualIdx)
                || (cIF != *mCurInterface))) {
        int tmp = -1;
        if (cBRMode == BRAuto) {
            tmp = 0;
        } else {
            tmp = cBRManualIdx + 1;
        }
        comm += QString("%1\nBR\n%2\n").arg(cIF).arg(tmp);
    }
    //mCurPMMode = cPMMode;
    mCurBRMode = cBRMode;
    mCurBRManualIdx = cBRManualIdx;
    *mCurInterface = cIF;
    
    if (!comm.isEmpty()) {
        fprintf(mHelperFifo, "%s", comm.ascii());
        fflush(mHelperFifo);
    }
}

void KWirelessMonitor::mousePressEvent(QMouseEvent *e)
{
    if (!rect().contains(e->pos()))
        return;

    switch (e->button()) {
    case LeftButton:
        toggleStatusBox();
        break;
    default:
        KSystemTray::mousePressEvent(e);
        break;
    }
}

void KWirelessMonitor::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    p.drawPixmap(0, 0, *(this->pixmap()));
}

void KWirelessMonitor::updateStats(const QString& stats)
{
    mStatusStr = stats;
    if (mStatusLabel) {
        mStatusLabel->setText(mStatusStr);
    }
}

void KWirelessMonitor::toggleStatusBox()
{
    if (mStatusBox && mStatusBox->isShown()) {
        mStatusBox->hide();
        return;
    }

    if (!mStatusBox) {
        mStatusBox = new QVBox(this, 0,
                               WDestructiveClose
                               | WStyle_Customize | WStyle_StaysOnTop
                               | WStyle_NoBorder | WStyle_Dialog
                               | WX11BypassWM);
        mStatusBox->setFrameStyle(QFrame::PopupPanel | QFrame::Raised);

        mStatusLabel = new QLabel(mStatusBox);
        mStatusLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
        mStatusLabel->setText(mStatusStr);
        mStatusLabel->setAlignment(AlignLeft | AlignVCenter | ExpandTabs);
    }

    int w = mStatusBox->sizeHint().width();
    int h = mStatusBox->sizeHint().height();
    QRect deskR = QApplication::desktop()->availableGeometry(
            QApplication::desktop()->primaryScreen());
    QPoint c = QPoint(deskR.right() - w - 1, deskR.bottom() - h - 1);
    mStatusBox->setFixedSize(mStatusBox->sizeHint());
    mStatusBox->move(c);
    mStatusBox->show();
}

void KWirelessMonitor::setPMModeHelper(int mode)
{
    if ((mHelper == 0) || (!mHelperFifo)
        || (mConfigDialog == 0)
        || (!mConfigDialog->settingsEnabled())) {
        return;
    }

    (*mCurInterface) = mConfigDialog->getInterfaceName();

    // ignore current PM mode
    QString comm("");
    comm += QString("%1\nPM\n%2\n").arg(*mCurInterface).arg(mode);
    //mCurPMMode = mode;

    fprintf(mHelperFifo, "%s", comm.ascii());
    fflush(mHelperFifo);
}

bool KWirelessMonitor::isBatteryPMEnabled()
{
    if (mConfigDialog) {
        return ((mConfigDialog->getPMM() == PMAuto)
                && mConfigDialog->settingsEnabled()
                && mConfigDialog->batteryPMEnabled());
    }
    return false;
}

bool KWirelessMonitor::isTransferPMEnabled()
{
    if (mConfigDialog) {
        return ((mConfigDialog->getPMM() == PMAuto)
                && mConfigDialog->settingsEnabled()
                && mConfigDialog->transferPMEnabled());
    }
    return false;
}

void KWirelessMonitor::disableMenuSettings()
{
    mActionEnableSettings->setEnabled(false);
}

QString KWirelessMonitor::getESSID()
{
    if (!mTimerThread) {
        return "";
    }
    return mTimerThread->getESSID();
}
