/***********************************************************************************
* Mem Usage: Plasmoid to monitor RAM/Swap Usage.
* Copyright (C) 2008  Alexey Tyapkin
*
* 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.
*
* 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 "memusage.h"
#include <QPainter>
#include <QFontMetrics>
#include <QSizeF>

#include <plasma/svg.h>
#include <plasma/theme.h>

memusage::memusage ( QObject *parent, const QVariantList &args )
		: Plasma::Applet ( parent, args ),
//		m_svg ( this ),
		m_icon ( "document" )
{
	iRamUsed=-1;
	iRamUsed1=-1;
	iRamBuffers=-1;
	iRamBuffers1=-1;
	iRamCached=-1;
	iRamCached1=-1;
	iRamTotal=-1;
	iSwapUsed1=-1;
	iSwapUsed=-1;
	iSwapTotal=-1;

	cfg_dialog=NULL;

	setHasConfigurationInterface( true );

	setCacheMode(QGraphicsItem::NoCache);

}


memusage::~memusage()
{
	if ( hasFailedToLaunch() )
	{
		// Do some cleanup here
	}
	else
	{
		// Save settings
	}
}

void memusage::resizePixmaps() {
	resize ( iWidth + 30, iHeight +30 );

        Ram=QPixmap ( QSize ( iWidth+20,iHeight+20 ) );
        dpm=QPixmap ( QSize ( iWidth+1,iHeight+1 ) );
        dpm1=QPixmap ( QSize ( iWidth+1,iHeight+1 ) );
        maskRam=QBitmap ( QSize ( iWidth+1,iHeight+1 ) );
	maskSwap=QBitmap ( QSize ( iWidth+1,iHeight+1 ) );
        Ram.fill ( Qt::transparent );
        dpm.fill ( Qt::transparent );
        dpm1.fill ( Qt::transparent );
        maskRam.fill ( Qt::color0 );
	maskSwap.fill ( Qt::color0 );

	updatePixmaps();
}

QSizeF memusage::contentSizeHint() const
{
	QSizeF sizeHint = QSizeF(iWidth+30,iHeight+30);
	return sizeHint;
}

void memusage::drawGridAndText(QPainter *p, int iiWidth, int iiHeight, QColor cGrid, QFont f, QPen* ppens)
{
	int iWidth2=iiWidth/2;
	int iWidth23=iWidth2+3;
	int iWidth26=iWidth2-6;
	int iFontSize=f.pointSize();
	int iFontSize24=iFontSize*2+4;
	int iFontSize4=iFontSize+4;
	p->setPen(QPen(cGrid, 1));
	p->drawRect(0,0,iiWidth,iiHeight);
	p->drawLine(iWidth2,0,iWidth2,iiHeight);
	p->drawLine(iWidth2,iFontSize4*6+4,iiWidth,iFontSize4*6+4);

	
	// Draw Ram Text
	for (int i=0;i<5;i++) {
		p->setPen(ppens[i]);
		p->drawText(iWidth23, iFontSize4*i+2, iWidth26,iFontSize24, Qt::AlignLeft | Qt::AlignTop, ramInfo[i][0]);
	}

	// Draw Swap Text
	for (int i=0;i<3;i++) {
		p->setPen(ppens[i]);
		p->drawText(iWidth23,  iFontSize4*(i+6)+6, iWidth26,iFontSize24, Qt::AlignLeft | Qt::AlignTop, swapInfo[i][0]);
	}

}

void memusage::updatePixmaps() {
	QPainter p;
	for (int i=0; i < 5; i++) {
		ramRects[i]=QRect(iWidth/2+3, (plasmoidFont.pointSize()+4)*i+2, iWidth/2-6,plasmoidFont.pointSize()+4);
	}

	for (int i=0; i < 3 ; i++) {
		swapRects[i]=QRect(iWidth/2+3,  (plasmoidFont.pointSize()+4)*(i+6)+6, iWidth/2-6,plasmoidFont.pointSize()+4);
	}
	pieRect=QRect(3, 3, iWidth/2-6, iHeight-6);
	
	dpm.fill(Qt::transparent);
	p.begin(&dpm);
		p.setCompositionMode(QPainter::CompositionMode_Destination);
		p.setFont(plasmoidFont);
		drawGridAndText(&p, iWidth,iHeight, cBorder, plasmoidFont, pens);
//		updatePie(&p,iRamUsed, 0, iRamCached, 0, iRamBuffers, 0, iRamTotal, QRect(3, 3, iWidth/2-6,iHeight -6), brushes);
	p.end();

	dpm1.fill(Qt::transparent);

	QPen ppens[5];
	ppens[0]=QPen ( Qt::color1 );
	ppens[1]=QPen ( Qt::color1 );
	ppens[2]=QPen ( Qt::color1 );
	ppens[3]=QPen ( Qt::color1 );
	ppens[4]=QPen ( Qt::color1 );

	maskRam.fill(Qt::color0);
	p.begin(&maskRam);
		drawGridAndText(&p, iWidth,iHeight, Qt::color1, plasmoidFont, ppens);
		p.fillRect(iWidth/2+3,2, iWidth/2-6,(plasmoidFont.pointSize()+4)*5, QBrush(Qt::color1));
		p.fillRect(pieRect, QBrush(Qt::color1));
		p.fillRect(iWidth/2+3,(plasmoidFont.pointSize()+4)*6+6, iWidth/2-6,(plasmoidFont.pointSize()+4)*9, QBrush(Qt::color1));
	p.end();

	maskSwap.fill(Qt::color0);
	p.begin(&maskSwap);
	p.fillRect(iWidth/2+3,(plasmoidFont.pointSize()+4)*6+6, iWidth/2-6,(plasmoidFont.pointSize()+4)*9, QBrush(Qt::color1));
	p.end();

	p.begin(&Ram);
		p.drawPixmap(0,0, dpm);
	p.end();

	dpm.setMask(maskRam);
	dpm1.setMask(maskSwap);
}

void memusage::init()
{
	iWidth=memusage::width;
	iHeight=memusage::height;
	iFontSize=memusage::fontSize;

	ramInfo[0][0]="RAM usage:";
	ramInfo[0][1]="NULL";
	ramInfo[1][0]="Free";
	ramInfo[2][0]="Used";
	ramInfo[3][0]="Buffers";
	ramInfo[4][0]="Cached";

	swapInfo[0][0]="Swap usage:";
	swapInfo[0][1]="NULL";
	swapInfo[1][0]="Free";
	swapInfo[2][0]="Used";

	pens[0]=QPen ( QColor(memusage::cTitle) );
	pens[1]=QPen ( QColor(memusage::cFree) );
	pens[2]=QPen ( QColor(memusage::cUsed) );
	pens[3]=QPen ( QColor(memusage::cBuffers) );
	pens[4]=QPen ( QColor(memusage::cCached) );
	cBorder=QColor(memusage::Border);
	plasmoidFont=QFont(memusage::defaultFontFamily, iFontSize, QFont::Bold);

	readConfig();

	setBackgroundHints(TranslucentBackground);
	

	brushes[1]=QBrush(pens[1].color(), Qt::SolidPattern);
	brushes[2]=QBrush(pens[2].color(), Qt::SolidPattern);
	brushes[3]=QBrush(pens[3].color(), Qt::SolidPattern);
	brushes[4]=QBrush(pens[4].color(), Qt::SolidPattern);

	plasmoidFont.setWeight(QFont::Bold);
	plasmoidFont.setPointSize( iFontSize );

	resize( contentSizeHint());
        prepareGeometryChange();
        updateGeometry();
	resizePixmaps();
	

	Plasma::DataEngine *mem_mon = dataEngine ( "memusage" );
	mem_mon->connectSource ( "RAM", this, 500 );
	mem_mon->connectSource ( "Swap", this, 500 );

        update();
}

void memusage::updatePreview() {
	QPen ppens[5];
	QBrush bbrushes[5];
	QFont pplasmoidFont=QFont(cfg.kfontcombobox->currentFont().family(),cfg.fontSize->value(), QFont::Bold);

	preview=QPixmap ( QSize ( cfg.plasmoidWidth->value()+1,cfg.plasmoidHeight->value()+1 ) );

	ppens[0].setColor(cfg.cTitle->color());
	ppens[1].setColor(cfg.cFree->color());
	ppens[2].setColor(cfg.cUsed->color());
	ppens[3].setColor(cfg.cBuffers->color());
	ppens[4].setColor(cfg.cCached->color());

	bbrushes[1]=QBrush(ppens[1].color(), Qt::SolidPattern);
	bbrushes[2]=QBrush(ppens[2].color(), Qt::SolidPattern);
	bbrushes[3]=QBrush(ppens[3].color(), Qt::SolidPattern);
	bbrushes[4]=QBrush(ppens[4].color(), Qt::SolidPattern);

	QRect rramRects[5];
	QRect sswapRects[3];

	for (int i=0; i < 5; i++) {
		rramRects[i]=QRect(cfg.plasmoidWidth->value()/2+3, (cfg.fontSize->value()+4)*i+2, cfg.plasmoidWidth->value()/2-6,cfg.fontSize->value()+4);
	}

	for (int i=0; i < 3 ; i++) {
		sswapRects[i]=QRect(cfg.plasmoidWidth->value()/2+3,  (cfg.fontSize->value()+4)*(i+6)+6, cfg.plasmoidWidth->value()/2-6,cfg.fontSize->value()+4);
	}

	preview.fill ( Qt::transparent );
	QPainter p;
	p.begin ( &preview );
		p.setFont(pplasmoidFont);
		drawGridAndText(&p, cfg.plasmoidWidth->value(), cfg.plasmoidHeight->value(), cfg.cBorder->color(), pplasmoidFont, ppens);
		p.setPen( Qt::black );
		updatePie(&p,iRamUsed, 0, iRamCached, 0, iRamBuffers, 0, iRamTotal, QRect(3, 3, cfg.plasmoidWidth->value()/2-6, cfg.plasmoidHeight->value()-6), bbrushes);
		drawRamInfo(&p, ppens, rramRects);
		drawSwapInfo(&p, ppens, sswapRects);

	p.end();
	cfg.previewLabel->setPixmap(preview);
	cfg.previewLabel->setFixedSize(preview.size());
	QString tmp;
	cfg.plasmoidSize->setText("Plasmoid size will be "+tmp.sprintf("%d x %d",cfg.plasmoidWidth->value(), cfg.plasmoidHeight->value()));
	cfg.previewLabel->adjustSize();
	cfg_dialog->adjustSize();
}

void memusage::showConfigurationInterface()
{
	if (cfg_dialog == 0) {
		cfg_dialog = new QDialog;

		cfg.setupUi(cfg_dialog);

		connect( cfg.kfontcombobox, SIGNAL(currentFontChanged (const QFont &)), this, SLOT(updatePreview()) );

		connect( cfg.plasmoidWidth, SIGNAL(valueChanged(int)), this, SLOT(updatePreview()) );
		connect( cfg.plasmoidHeight, SIGNAL(valueChanged(int)), this, SLOT(updatePreview()) );
		connect( cfg.fontSize, SIGNAL(valueChanged(int)), this, SLOT(updatePreview()) );


		connect( cfg.cTitle, SIGNAL(changed(const QColor&)), this, SLOT(updatePreview()) );
		connect( cfg.cFree, SIGNAL(changed(const QColor&)), this, SLOT(updatePreview()) );
		connect( cfg.cUsed, SIGNAL(changed(const QColor&)), this, SLOT(updatePreview()) );
		connect( cfg.cBuffers, SIGNAL(changed(const QColor&)), this, SLOT(updatePreview()) );
		connect( cfg.cCached, SIGNAL(changed(const QColor&)), this, SLOT(updatePreview()) );
		connect( cfg.cBorder, SIGNAL(changed(const QColor&)), this, SLOT(updatePreview()) );

		connect( cfg.buttonBox, SIGNAL(accepted()), this, SLOT(cfgToPlasmoid()) );
		connect( cfg.buttonBox, SIGNAL(clicked ( QAbstractButton *)), this, SLOT(clicked ( QAbstractButton *)) );
	}

	plasmoidToCfg();

	cfg_dialog->show();
}

void memusage::defaultsToCfg ()  {
	QFont tmpFont=QFont(memusage::defaultFontFamily, memusage::fontSize, QFont::Bold);

	cfg.kfontcombobox->setCurrentFont(tmpFont);
	cfg.plasmoidWidth->setValue(memusage::width);
	cfg.plasmoidHeight->setValue(memusage::height);
	cfg.fontSize->setValue(memusage::fontSize);
	cfg.cTitle->setColor(QColor(memusage::cTitle));
	cfg.cFree->setColor(QColor(memusage::cFree));
	cfg.cUsed->setColor(QColor(memusage::cUsed));
	cfg.cBuffers->setColor(QColor(memusage::cBuffers));
	cfg.cCached->setColor(QColor(memusage::cCached));
	cfg.cBorder->setColor(QColor(memusage::Border));
}

void memusage::clicked ( QAbstractButton *button) 
{
	if (cfg.buttonBox->buttonRole(button) == QDialogButtonBox::ResetRole) {
		defaultsToCfg();
		updatePreview();
	}
}

void memusage::paintInterface ( QPainter *p,
                                const QStyleOptionGraphicsItem *option, const QRect &contentsRect )
{
	p->setRenderHint ( QPainter::SmoothPixmapTransform );
	p->setRenderHint ( QPainter::Antialiasing );

	p->drawPixmap(15,15,Ram);
}

void memusage::updatePie(QPainter *p, int iiRamUsed, int iiRamUsed1, int iiRamCached, int iiRamCached1, int iiRamBuffers,int iiRamBuffers1, int iiRamTotal, QRect ppieRect, QBrush* bbrushes) {
	if (!(iiRamBuffers1 == iiRamBuffers)) {
		p->setBrush(bbrushes[3]);
		p->drawPie(ppieRect, 0,iUsage.at(iiRamBuffers));
	}
	if (!((iiRamBuffers1 == iiRamBuffers) && (iiRamCached1 == iiRamCached))) {
		p->setBrush(bbrushes[4]);
		p->drawPie(ppieRect, iUsage.at(iiRamBuffers),iUsage.at(iiRamCached));
	}
	if (!((iiRamBuffers1 == iiRamBuffers) && (iiRamCached1 == iiRamCached) && (iiRamUsed1 == iiRamUsed))) {
		p->setBrush(bbrushes[2]);
		p->drawPie(ppieRect, iUsage.at(iiRamBuffers+iiRamCached),iUsage.at(iiRamUsed));
		p->setBrush(bbrushes[1]);
		p->drawPie(ppieRect, iUsage.at(iiRamBuffers+iiRamCached+iiRamUsed), iUsage.at(iiRamTotal-(iiRamBuffers+iiRamCached+iiRamUsed)));
	}
}

void memusage::dataUpdated ( const QString& source, const Plasma::DataEngine::Data &data )
{
    bool changed=false;
//	setUpdatesEnabled(false);
	if(source == "RAM"){
		iRamUsed1=iRamUsed;
                iRamUsed = data["iUsed"].toInt();
		iRamBuffers1=iRamBuffers;
		iRamBuffers=data["iBuffers"].toInt();
		iRamCached1=iRamCached;
		iRamCached=data["iCached"].toInt();
		if (iUsage.isEmpty()) {
			iRamTotal=data["iTotal"].toInt();
			iUsage=QVector<int>(iRamTotal+1);
			for (int i=0; i< iUsage.size();i++) {
				iUsage.replace(i, (int)((i*5760)/iRamTotal));
			}
		}
		if (iRamUsed1!=iRamUsed || iRamBuffers1!=iRamBuffers || iRamCached1!=iRamCached) {
			changed=true;
			ramInfo[1][1]=data["iFree"].toString()+" Mb";
			ramInfo[2][1]=data["iUsed"].toString()+" Mb";
			ramInfo[3][1]=data["iBuffers"].toString()+" Mb";
			ramInfo[4][1]=data["iCached"].toString()+" Mb";

			QPainter p;
			p.begin(&dpm);
			updatePie(&p,iRamUsed, iRamUsed1, iRamCached, iRamCached1, iRamBuffers, iRamBuffers1, iRamTotal, pieRect, brushes);
			p.end();
			
		}

        }else if(source == "Swap"){
		iSwapUsed1=iSwapUsed;
                iSwapUsed = data["iUsed"].toInt();

		if (iSwapUsed1!=iSwapUsed ) {
			changed=true;
			swapInfo[1][1]=data["iFree"].toString()+" Mb";
			swapInfo[2][1]=data["iUsed"].toString()+" Mb";

			QPainter p;
			dpm1.fill(Qt::transparent);
			p.begin(&dpm1);
			p.setFont( plasmoidFont );
			drawSwapInfo(&p, pens, swapRects);
			p.end();
		}
        }
        
        if (changed) {
		Ram.fill(Qt::transparent);
		QPainter p;
		p.begin(&Ram);
		p.setFont( plasmoidFont );
		p.drawPixmap(0,0,dpm);
		drawRamInfo(&p, pens, ramRects);
		p.drawPixmap(0,0,dpm1);
		p.end();
//		setUpdatesEnabled(true);
		update();
        }else {
//	    setUpdatesEnabled(true);
	}

}

void memusage::cfgToPlasmoid() {
	plasmoidFont=cfg.kfontcombobox->currentFont();
	iFontSize=cfg.fontSize->value();
	plasmoidFont.setWeight(QFont::Bold );
	plasmoidFont.setPointSize( iFontSize );

	iWidth=cfg.plasmoidWidth->value();
	iHeight=cfg.plasmoidHeight->value();

	pens[0].setColor(cfg.cTitle->color());
	pens[1].setColor(cfg.cFree->color());
	pens[2].setColor(cfg.cUsed->color());
	pens[3].setColor(cfg.cBuffers->color());
	pens[4].setColor(cfg.cCached->color());
	brushes[1]=QBrush(pens[1].color(), Qt::SolidPattern);
	brushes[2]=QBrush(pens[2].color(), Qt::SolidPattern);
	brushes[3]=QBrush(pens[3].color(), Qt::SolidPattern);
	brushes[4]=QBrush(pens[4].color(), Qt::SolidPattern);

	cBorder=cfg.cBorder->color();

	writeConfig();

	resizePixmaps();

	updatePixmaps();

	QPainter p;
	p.begin(&Ram);
	updatePie(&p,iRamUsed, 0, iRamCached, 0, iRamBuffers, 0, iRamTotal, pieRect, brushes);
	p.setFont( plasmoidFont );
	drawRamInfo(&p, pens, ramRects);
	drawSwapInfo(&p, pens, swapRects);
	p.end();
	resize(contentSizeHint());
        prepareGeometryChange();
        updateGeometry();
        update();


}

void memusage::plasmoidToCfg() {
	cfg.kfontcombobox->setCurrentFont(plasmoidFont);
	cfg.plasmoidWidth->setValue(iWidth);
	cfg.plasmoidHeight->setValue(iHeight);
	cfg.fontSize->setValue(iFontSize);
	cfg.cTitle->setColor(pens[0].color());
	cfg.cFree->setColor(pens[1].color());
	cfg.cUsed->setColor(pens[2].color());
	cfg.cBuffers->setColor(pens[3].color());
	cfg.cCached->setColor(pens[4].color());
	cfg.cBorder->setColor(cBorder);
}

void memusage::drawSwapInfo(QPainter * p, QPen * ppens, QRect * sswapRects)
{
	for (int i=1;i<3;i++) {
		p->setPen(ppens[i]);
		p->drawText(sswapRects[i], Qt::AlignRight, swapInfo[i][1]);
	}
}

void memusage::drawRamInfo(QPainter * p, QPen * ppens, QRect * rramRects)
{
	for (int i=1;i<5;i++) {
		p->setPen(ppens[i]);
		p->drawText(rramRects[i], Qt::AlignRight, ramInfo[i][1]);
	}
}

void memusage::writeConfig() {
        KConfigGroup cg = config();
        cg.writeEntry("iWidth", iWidth);
        cg.writeEntry("iHeight", iHeight);
        cg.writeEntry("iFontSize", iFontSize);
        cg.writeEntry("pens0", pens[0].color().name());
        cg.writeEntry("pens1", pens[1].color().name());
        cg.writeEntry("pens2", pens[2].color().name());
        cg.writeEntry("pens3", pens[3].color().name());
        cg.writeEntry("pens4", pens[4].color().name());
        cg.writeEntry("cBorder", cBorder.name());
        cg.writeEntry("plasmoidFont", plasmoidFont.family());
        emit configNeedsSaving();
}

void memusage::readConfig() {
        KConfigGroup cg = config();
        if (cg.hasKey("iWidth"))
                iWidth=cg.readEntry("iWidth", iWidth);
        if (cg.hasKey("iHeight"))
                iHeight=cg.readEntry("iHeight", iHeight);
        if (cg.hasKey("iFontSize"))
                iFontSize=cg.readEntry("iFontSize", iFontSize);
        if (cg.hasKey("pens0"))
                pens[0].setColor(QColor(cg.readEntry("pens0", pens[0].color().name())));
        if (cg.hasKey("pens1"))
                pens[1].setColor(QColor(cg.readEntry("pens1", pens[1].color().name())));
        if (cg.hasKey("pens2"))
                pens[2].setColor(QColor(cg.readEntry("pens2", pens[2].color().name())));
        if (cg.hasKey("pens3"))
                pens[3].setColor(QColor(cg.readEntry("pens3", pens[3].color().name())));
        if (cg.hasKey("pens4"))
                pens[4].setColor(QColor(cg.readEntry("pens4", pens[4].color().name())));
        if (cg.hasKey("cBorder"))
                cBorder=QColor(cg.readEntry("cBorder", cBorder.name()));
        if (cg.hasKey("plasmoidFont"))
                plasmoidFont=QFont(cg.readEntry("plasmoidFont", plasmoidFont.family()));
}


#include "memusage.moc"
