/***************************************************************************
 *                                                                         *
 *   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 <kconfig.h>
#include <kiconloader.h>

#include <qpixmap.h>
#include <qtooltip.h>
#include <qstringlist.h>
#include <qmap.h>
#include <qbitmap.h>

#include "kwirelessmonitor.h"
#include "timerthread.h"
#include "wm_arch.h"

/* note: the last two must be zero and error */
const char *TimerThread::mSignalArray[] = { "kwirelessmonitor_0.png",
                                            "kwirelessmonitor_1.png",
                                            "kwirelessmonitor_2.png",
                                            "kwirelessmonitor_3.png",
                                            "kwirelessmonitor_4.png",
                                            "kwirelessmonitor_zero.png",
                                            "kwirelessmonitor_err.png" };

/* note: the last one must be zero */
const char *TimerThread::mBitrateArray[] = { "kwirelessmonitor_b0.png",
                                             "kwirelessmonitor_b1.png",
                                             "kwirelessmonitor_b2.png",
                                             "kwirelessmonitor_b3.png",
                                             "kwirelessmonitor_bzero.png" };

const int TimerThread::mNumSignal = sizeof(mSignalArray) / sizeof(char *);
const int TimerThread::mNumBitrate = sizeof(mBitrateArray) / sizeof(char *);

TimerThread::TimerThread(KWirelessMonitor *w) : QThread()
{
    mWidget = w;
    mCurrentState = 0;
    mPixmapArray = new QPixmap**[mNumSignal];
    for (int i = 0; i < mNumSignal; i++) {
        /* reserve 0 for no bitrate */
        mPixmapArray[i] = new QPixmap*[mNumBitrate + 1];
    }
    
    KIconLoader *loader = KGlobal::iconLoader();

    /* special case: signal error */
    QPixmap b0 = loader->loadIcon(mBitrateArray[mNumBitrate - 1],
                                  KIcon::User);
    QPixmap s0 = loader->loadIcon(mSignalArray[mNumSignal - 1],
                                  KIcon::User);
    QBitmap b0mask = *(b0.mask());
    QBitmap s0mask = *(s0.mask());
    QBitmap sb00mask(s0.width(), s0.height());
    sb00mask.fill(Qt::color0);
    bitBlt(&sb00mask, 0, 0, &b0mask, 0, 0,
           s0.width(), s0.height(), Qt::CopyROP);
    bitBlt(&sb00mask, 0, 0, &s0mask, 0, 0,
           s0.width(), s0.height(), Qt::XorROP);

    mPixmapArray[mNumSignal - 1][0] = new QPixmap(s0);
    
    mPixmapArray[mNumSignal - 1][1] = new QPixmap(s0.width(), s0.height());
    mPixmapArray[mNumSignal - 1][1]->fill(w, 0, 0);
    bitBlt(mPixmapArray[mNumSignal - 1][1], 0, 0, &s0);
    bitBlt(mPixmapArray[mNumSignal - 1][1], 0, 0, &b0);
    mPixmapArray[mNumSignal - 1][1]->setMask(sb00mask);
    
    /* special case: signal zero */
    QPixmap s1 = loader->loadIcon(mSignalArray[mNumSignal - 2],
                                  KIcon::User);
    QBitmap s1mask = *(s1.mask());
    QBitmap sb10mask(s1.width(), s1.height());
    sb10mask.fill(Qt::color0);
    bitBlt(&sb10mask, 0, 0, &b0mask, 0, 0,
           s1.width(), s1.height(), Qt::CopyROP);
    bitBlt(&sb10mask, 0, 0, &s1mask, 0, 0,
           s1.width(), s1.height(), Qt::XorROP);

    mPixmapArray[mNumSignal - 2][0] = new QPixmap(s1);
    mPixmapArray[mNumSignal - 2][1] = new QPixmap(s1.width(), s1.height());
    mPixmapArray[mNumSignal - 2][1]->fill(w, 0, 0);
    bitBlt(mPixmapArray[mNumSignal - 2][1], 0, 0, &s1);
    bitBlt(mPixmapArray[mNumSignal - 2][1], 0, 0, &b0);
    mPixmapArray[mNumSignal - 2][1]->setMask(sb10mask);

    for (int i = 0; i < (mNumSignal - 2); i++) {
        QPixmap sp = loader->loadIcon(mSignalArray[i], KIcon::User);
        mPixmapArray[i][0] = new QPixmap(sp);
        for (int j = 0; j < mNumBitrate; j++) {
            QPixmap bp = loader->loadIcon(mBitrateArray[j], KIcon::User);
            
            QBitmap bpmask = *(bp.mask());
            QBitmap spmask = *(sp.mask());
            QBitmap sbmask(sp.width(), sp.height());
            sbmask.fill(Qt::color0);
            bitBlt(&sbmask, 0, 0, &bpmask, 0, 0,
                   sp.width(), sp.height(), Qt::CopyROP);
            bitBlt(&sbmask, 0, 0, &spmask, 0, 0,
                   sp.width(), sp.height(), Qt::XorROP);
            
            mPixmapArray[i][j + 1] = new QPixmap(sp.width(), sp.height());
            mPixmapArray[i][j + 1]->fill(w, 0, 0);
            bitBlt(mPixmapArray[i][j + 1], 0, 0, &sp);
            bitBlt(mPixmapArray[i][j + 1], 0, 0, &bp);
            mPixmapArray[i][j + 1]->setMask(sbmask);
        }
    }

    mWidget->setPixmap(*mPixmapArray[0][0]);  // dummy

    KConfig *config = KGlobal::config();
    config->setGroup("KWirelessMonitor Settings");
    QStringList ifList;
    int rv = wm_getWirelessInterfaces(ifList);
    if ((rv < 0) || ifList.empty()) {
        mIFMonitor = new InterfaceMonitor(
                            config->readEntry("Interface", "eth0"));
    } else {
        mIFMonitor = NULL;
        for (QStringList::Iterator it = ifList.begin(); it != ifList.end();
                it += 1) {
            QString str = (*it);
            if (str.compare(config->readEntry("Interface", "eth0")) == 0) {
                mIFMonitor = new InterfaceMonitor(str);
                break;
            }
        }
        if (mIFMonitor == NULL) {
            mIFMonitor = new InterfaceMonitor(*(ifList.begin()));
        }
    }

    for (int i = 0; i < numAvailableBitrates; i++) {
        /* XXX anything higher than idx2 is considered full rate */
        mBitrateIndexMap[availableBitrateList[i]]
            = (i < BITRATE_GRAPH_PARTS) ? i : (BITRATE_GRAPH_PARTS - 1);
    }

    mWasOnACPower = wm_isOnACPower();
}

void TimerThread::setInterface(const QString& ifName)
{
    mIFMonitor->setInterface(ifName);
    for (int i = 0; i < numAvailableBitrates; i++) {
        /* XXX anything higher than idx2 is considered full rate */
        mBitrateIndexMap[availableBitrateList[i]]
            = (i < BITRATE_GRAPH_PARTS) ? i : (BITRATE_GRAPH_PARTS - 1);
    }
}

QString TimerThread::getESSID()
{
    if (!mIFMonitor) {
        return "";
    }
    const struct wirelessStats *stats = mIFMonitor->getStats();
    return stats->ESSID;
}

int TimerThread::getQuality()
{
    if (!mIFMonitor) {
        return 0;
    }
    const struct wirelessStats *stats = mIFMonitor->getStats();
    return stats->quality;
}

int TimerThread::getPM()
{
    if (!mIFMonitor) {
        return 0;
    }
    const struct wirelessStats *stats = mIFMonitor->getStats();
    return stats->powermgmt;
}

void TimerThread::run()
{
    QString tip;
    QString status;
    int ox = -1;
    int oy = -1;
    unsigned long long prevRX = 0;
    unsigned long long prevTX = 0;
    int transfer_timeout = 0;

    while (true) {
        int cx, cy;
        QPixmap *currentPixmap = NULL;
        const struct wirelessStats *stats = mIFMonitor->getStats();
        if (stats->quality < 0) {
            mCurrentState = mNumSignal - 1;
            tip = "Device error";
            status = "Device error";
            currentPixmap = mPixmapArray[mCurrentState][1];
            cx = mCurrentState;
            cy = 1;
        } else if (stats->quality == 0) {
            mCurrentState = mNumSignal - 2;
            tip = QString("%1\nNo signal\nPM %2")
                        .arg(stats->ESSID)
                        .arg((stats->powermgmt == PMOn) ? "On" : "Off");
            status= QString("Network:\t%1\nNo signal\nPower Mgmt %2")
                        .arg(stats->ESSID)
                        .arg((stats->powermgmt == PMOn) ? "On" : "Off");
            currentPixmap = mPixmapArray[mCurrentState][1];
            cx = mCurrentState;
            cy = 1;
        } else {
            mCurrentState = (stats->quality - 1) / 20;
            mCurrentState = (mCurrentState > (mNumSignal - 3))
                            ? (mNumSignal - 3) : mCurrentState;
            float rate = ((float) stats->bitrate) / 1000000.0;
            tip = QString("%1\nSignal %2%\n%3Mbps\nPM %4")
                     .arg(stats->ESSID)
                     .arg(stats->quality)
                     .arg(rate, 0, 'f', 1)
                     .arg((stats->powermgmt == PMOn) ? "On" : "Off");
            status = QString("Network:\t%1\nSignal:\t%2%\nBitrate:\t%3Mbps")
                     .arg(stats->ESSID)
                     .arg(stats->quality)
                     .arg(rate, 0, 'f', 1);
            status += QString("\nPower Mgmt %1")
                        .arg((stats->powermgmt == PMOn) ? "On" : "Off");

            QMap<int, int>::Iterator it
                = mBitrateIndexMap.find(stats->bitrate);
            int currentBitrateIndex = 0;
            if (it != mBitrateIndexMap.end()) {
                currentBitrateIndex = *it;
            } else {
                currentBitrateIndex = BITRATE_GRAPH_PARTS - 1;
            }
            currentPixmap = mPixmapArray[mCurrentState]
                                        [currentBitrateIndex + 1];
            cx = mCurrentState;
            cy = currentBitrateIndex + 1;
        }
        QToolTip::add(mWidget, tip);
        if ((cx != ox) || (cy != oy)) {
            mWidget->setPixmap(*currentPixmap);
            ox = cx;
            oy = cy;
        }
        mWidget->updateStats(status);

        if (mWidget->isBatteryPMEnabled()) {
            bool isOnACPower = wm_isOnACPower();
            if (!mWasOnACPower && isOnACPower) {
                // battery->AC
                mWidget->setPMModeHelper(PMOff);
            } else if (mWasOnACPower && !isOnACPower) {
                // AC->battery
                mWidget->setPMModeHelper(PMOn);
            }
            mWasOnACPower = isOnACPower;
        }
       
        if (mWidget->isTransferPMEnabled()) {
            unsigned long long curRX, curTX;
            mIFMonitor->getTransferBytes(&curRX, &curTX);
            unsigned long long avg = (curTX - prevTX) * 2;
            prevRX = curRX;
            prevTX = curTX;
            if (avg >= DATA_TRANSFER_THRESHOLD) {
                transfer_timeout = TRANSFER_TIMEOUT;
                if (stats->powermgmt == PMOn) {
                    mWidget->setPMModeHelper(PMOff);
                }
            } else {
                if (stats->powermgmt == PMOff) {
                    if (transfer_timeout > 0) {
                        transfer_timeout -= 1;
                    } else {
                        mWidget->setPMModeHelper(PMOn);
                    }
                }
            }
        }

        msleep(500);
    }
}

