/***************************************************************************
 *   Copyright (C) 2008-2009 by Nicolas Dubuit <dubuit@sourceforge.net>    *
 *                                                                         *
 *   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 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 ***************************************************************************/

#include "monitor.h"
#include <QPainter>
#include <QFontMetrics>
#include <QSizeF>
 
#include <plasma/svg.h>
#include <plasma/theme.h>

#include <iostream> 
using namespace std ;

int INTERVAL = 500 ;
int QUEUE_SIZE = 60000/INTERVAL ;

Monitor::Monitor(QObject *parent, const QVariantList &args)
    : Plasma::Applet(parent, args),
	  tm (this)
{
// 	setAspectRatioMode (Plasma::Square) ;
    // this will get us the standard applet background, for free!
    setBackgroundHints(DefaultBackground);
    resize(30, 20);
	
	QObject::connect(&tm, SIGNAL(timeout()), this, SLOT(updateMemValues())) ;
	QObject::connect(&tm, SIGNAL(timeout()), this, SLOT(updateCpuValues())) ;

// Read initial values for CPU usage.
	updateCpuValues() ;
}
 
 
Monitor::~Monitor()
{
    if (hasFailedToLaunch()) {
        // Do some cleanup here
    } else {
        // Save settings
    }
}
 
void Monitor::init()
{
	tm.start(INTERVAL) ;
    resize(30, 20);
    // A small demonstration of the setFailedToLaunch function
/*    if (m_icon.isNull()) {
        setFailedToLaunch(true, "No world to say hello");
    }*/
} 
 
 
void Monitor::paintInterface(QPainter *p,
        const QStyleOptionGraphicsItem *, const QRect &contentsRect)
{
	int i ;
    p->setRenderHint(QPainter::SmoothPixmapTransform);
    p->setRenderHint(QPainter::Antialiasing);

// 	float W = p->window().width() ;
// 	float H = p->window().height() ;

    float w = contentsRect.width() ;
    float h = contentsRect.height() ;


	p->translate (contentsRect.x(), contentsRect.y()) ;
	p->scale (w,-h) ;
 	p->translate (0.,-1.) ;
	p->drawRect(0,0,1,1) ;

// Draw mem.
	if (mem_cache_vals.size()<= 0) 
		return ;
	QPainterPath memPath ;
	QPainterPath cachePath ;
	float x = 1. - float(mem_vals.size())/QUEUE_SIZE ;
	float xStart = x ;
	QPointF memStart (x,mem_cache_vals[0]) ;
	memPath.moveTo(memStart) ;
	QPointF cacheStart (x, mem_cache_vals[0]) ;
	cachePath.moveTo(cacheStart ) ;
	for (i=0 ; i<mem_vals.size() ; i++)
	{
		x += 1./QUEUE_SIZE ;
		memPath.lineTo(x, mem_vals[i]) ;
		cachePath.lineTo(x, mem_vals[i]+mem_cache_vals[i]) ;
	}
 	cachePath.connectPath(memPath.toReversed()) ;
	cachePath.closeSubpath() ;
	memPath.lineTo(1.,0.) ;
	memPath.lineTo(QPointF(xStart, 0.)) ;

	QLinearGradient grad(0.,0.,0.4,0.);
	grad.setColorAt(0.,QColor(60,120,160,0)) ;
	grad.setColorAt(1.,QColor(60,120,160,255)) ;
	p->setBrush(QBrush(grad)) ;
	p->setPen(Qt::NoPen) ;
	p->drawPath(memPath) ;

 	QLinearGradient gradCache(0.,0.,0.4,0.);
 	gradCache.setColorAt(0.,QColor(160,190,60,0)) ;
 	gradCache.setColorAt(1.,QColor(160,190,60,255)) ;
 	p->setBrush(QBrush(gradCache)) ;
 	p->setPen(Qt::NoPen) ;
 	p->drawPath(cachePath) ;

// Draw CPU
	if (cpus_vals.size()<=0)
		return ;
// 	p->setPen(Qt::red) ;
	p->setBrush(Qt::NoBrush) ;
	QPainterPath cpuPath ;
	int icol = 0 ;
	foreach(QQueue<float> cpu_vals, cpus_vals )
	{
		if ( cpu_vals.size()<2 ) continue ;
		float x = 1. - float(cpu_vals.size()-1)/QUEUE_SIZE ;
		cpuPath = QPainterPath() ;
		cpuPath.moveTo(QPointF (x,cpu_vals[1]-cpu_vals[0])) ;
		for (i=1 ; i<cpu_vals.size() ; i++)
		{
			x += 1./QUEUE_SIZE ;
			cpuPath.lineTo(x, cpu_vals[i]-cpu_vals[i-1]) ;
		}
		p->setPen(QPen(QColor(255-icol,0+icol,0))) ;
		icol += 30 ;
		p->drawPath(cpuPath) ;
	}
}



void Monitor::updateCpuValues (void) 
{
	QFile file ("/proc/stat"); 
	file.open(QIODevice::ReadOnly) ;
	QStringList cpustat = QString(file.readAll()).split('\n') ;
	file.close () ;

	// Parse output.
	float user, nice, system, usage ;
	int cpuIndex ;
	QRegExp cpuRe ("^cpu(\\d)\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)") ;
	foreach(QString stat, cpustat) 
	{
		if (cpuRe.indexIn(stat)==0) 
		{
			// New CPU discovered.
			cpuIndex = cpuRe.cap(1).toInt() ;
			if ( cpus_vals.size() < cpuIndex+1 )
				cpus_vals.append(QQueue<float>()) ;
			
			// Read cumulated CPU usage.
			user = cpuRe.cap(2).toInt() ;
			nice = cpuRe.cap(3).toInt() ;
			system = cpuRe.cap(4).toInt() ;
			usage = float(user+system+nice)/100 * 1000/INTERVAL ;
			cpus_vals[cpuIndex].enqueue(usage) ;
			if (cpus_vals[cpuIndex].size()>QUEUE_SIZE+1)
				cpus_vals[cpuIndex].dequeue() ;
		}
	}
}
 


void Monitor::updateMemValues (void) 
{
	QFile memFile ("/proc/meminfo"); 
	memFile.open(QIODevice::ReadOnly) ;
	QStringList meminfo = QString(memFile.readAll()).split('\n') ;
	memFile.close () ;

	// Parse output.
	int memTotal=1, memFree=1, memCache=1 ;
	QRegExp memTotalRe ("^MemTotal:\\s*(\\d*)") ;
	QRegExp memFreeRe ("^MemFree:\\s*(\\d*)") ;
	QRegExp memCacheRe ("^Cached:\\s*(\\d*)") ;
	foreach(QString info, meminfo) 
	{
		if (memTotalRe.indexIn(info)==0) 
			memTotal = memTotalRe.cap(1).toInt() ;
		if (memFreeRe.indexIn(info)==0) 
			memFree = memFreeRe.cap(1).toInt() ;
		if (memCacheRe.indexIn(info)==0) 
			memCache = memCacheRe.cap(1).toInt() ;
	}
	mem_vals.enqueue(float(memTotal-memFree-memCache)/memTotal) ;
	mem_cache_vals.enqueue(float(memCache)/memTotal) ;
	if (mem_vals.size()>QUEUE_SIZE)
	{
		mem_vals.dequeue() ;
		mem_cache_vals.dequeue() ;
	}

	update() ;
}
 
#include "monitor.moc"
