/***************************************************************************
 *                                                                         *
 *   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 "frames.h"
#include <kdebug.h>
#include <iostream>
#include <cmath>

#include <X11/Xlib.h>

#include <qpainter.h>
#include <qdatetime.h>
#include <qbitmap.h>
#include <qlayout.h>
#include <qicon.h>


#include <iostream>

// #include <kwin.h>
#include <klocale.h>
// #include <krootpixmap.h>

MainFrame::MainFrame( QWidget *parent) : QFrame(parent)
{
	tooltip = 0L;
	m_rootPixmap = 0L;
	
	m_currentIndex = 0;
	m_rateInLifo.resize(4096);
	m_rateOutLifo.resize(4096);
		
	config = KdevmonConfig::getConfig();
	
	m_graphPixmap = new GraphPixmap();
	
// 	m_rootPixmap = new QWidget( this );
	
	initFromConfig();
	clear();
}

MainFrame::~MainFrame()
{
}

void MainFrame::initFromConfig()
{
	if( config->getMainFrameConfig()->transparent )
	{
// 		m_rootPixmap->start();
		m_rootPixmap->repaint();
		
	}
	else
	{
// 		m_rootPixmap->stop();
		QPixmap p(size());
		p.fill( config->getMainFrameConfig()->bgColor );
		setBackgroundPixmap( p );
	}

	QColor bgColor = config->getMainFrameConfig()->bgColor;
	QPalette p(bgColor,bgColor);
	setPalette( p );
	
	m_graphPixmap->initFromConfig();
}

void MainFrame::updateDisplay( double rateIn, double rateOut )
{
	// store rateIn/rateOut in lifo
	m_currentIndex++;
	m_currentIndex%=4096;
	m_rateInLifo[m_currentIndex] = rateIn;
	m_rateOutLifo[m_currentIndex] = rateOut;
	
	m_graphPixmap->drawGraph( m_rateInLifo, m_rateOutLifo, m_currentIndex );
	
	if( config->getMainFrameConfig()->transparent )
		repaint();
	else
		repaint();
}

void MainFrame::clear() {
	m_rateInLifo.fill(0);
	m_rateOutLifo.fill(0);
	m_currentIndex = 0;

	m_graphPixmap->drawGraph( m_rateInLifo, m_rateOutLifo, m_currentIndex );
	repaint();
}

QString MainFrame::getTooltipText( const QPoint& point ) const
{
	int w = width()-2*BORDERWIDTH;
	int diff = (int) (w - (double)point.x());
	double rIn =
		m_currentIndex-diff>=0 ?
		m_rateInLifo[m_currentIndex-diff]:
		m_rateInLifo[4096+m_currentIndex-diff];
	double rOut =
		m_currentIndex-diff>=0 ?
		m_rateOutLifo[m_currentIndex-diff] :
		m_rateOutLifo[4096+m_currentIndex-diff];
	
	int unit = config->getCommonConfig()->unit;
	int refresh = config->getCommonConfig()->refreshTime;
	
	QString s;
	int diffseconds = (int) ((double)diff * refresh / 1000);
	if( unit == Common::UNIT_BIT )
		s= QString( "%1 \n In: %2bps | Out: %3bps").arg(QTime::currentTime().addSecs(-diffseconds).toString(),Tools::double2bit(rIn), Tools::double2bit(rOut) );
	else if( unit == Common::UNIT_BYTE )
		s = QString("%1 \n In: %2Bps | Out: %3Bps").arg(QTime::currentTime().addSecs(-diffseconds).toString(),Tools::double2byte(rIn), Tools::double2byte(rOut) );
	return s;
}

void MainFrame::resizeEvent( QResizeEvent *e )
{
	QFrame::resizeEvent( e );
// 	std::cout << "mainframe: width:" << width() << ", height: " << height() << std::endl;
	m_graphPixmap->setSize( width()-2*BORDERWIDTH, height()-2*BORDERWIDTH );
	repaint();
}

void MainFrame::paintEvent( QPaintEvent *e )
{
	QFrame::paintEvent( e );
	QPainter p;
	p.begin( this );
	p.drawPixmap( BORDERWIDTH, BORDERWIDTH, m_graphPixmap->getPixmap() );
	p.end();
}

bool MainFrame::event(QEvent *event)
{
	if( config->getMainFrameConfig()->showTooltips )
	{
		if (event->type() == QEvent::ToolTip)
		{
			QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
			if (helpEvent->pos() != QPoint(0,0))
				QToolTip::showText(helpEvent->globalPos(), getTooltipText(helpEvent->globalPos()));
			else
				QToolTip::hideText();
		}
     }
     return QWidget::event(event);
}

DockFrame::DockFrame( QWidget *parent ) : KSystemTrayIcon (parent )
{
	m_currentIndex = 0;
	m_rateInLifo.resize(DOCK_WIDTH);
	m_rateOutLifo.resize(DOCK_WIDTH);
	config = KdevmonConfig::getConfig();
	this->m_graphPixmap = new GraphPixmap();
	initFromConfig();
	clear();
}

DockFrame::~DockFrame() {
}

void DockFrame::initFromConfig()
{
	m_graphPixmap->initFromConfig();

	QPixmap pixmap = m_graphPixmap->getPixmap();
	QPainter painter(&pixmap);
// 	painter.repaint();
}

void DockFrame::clear()
{
	m_rateInLifo.fill(0);
	m_rateOutLifo.fill(0);
	m_currentIndex = 0;

	QPixmap pixmap = m_graphPixmap->getPixmap();
	QPainter painter(&pixmap);

	m_graphPixmap->drawGraph( m_rateInLifo, m_rateOutLifo, m_currentIndex );
// 	painter.repaint();
}

void DockFrame::updateDisplay( double rateIn, double rateOut )
{
	m_currentIndex++;
	m_currentIndex%=DOCK_WIDTH;
	m_rateInLifo[m_currentIndex] = rateIn;
	m_rateOutLifo[m_currentIndex] = rateOut;
	
	if( config->getDockFrameConfig()->showText )
	{
		QString textIn, textOut;
		if( config->getCommonConfig()->unit == Common::UNIT_BIT )
		{
			textIn = Tools::double2bit( rateIn );
			textOut = Tools::double2bit( rateOut );
		}
		else if( config->getCommonConfig()->unit == Common::UNIT_BYTE )
		{
			textIn = Tools::double2byte( rateIn );
			textOut = Tools::double2byte( rateOut );
		}

		QPixmap pm( DOCK_WIDTH, DOCK_WIDTH );
		pm.fill( config->getDockFrameConfig()->bgColor );
		QPainter painter;
		painter.begin( &pm );
		painter.setFont( QFont("arial", config->getDockFrameConfig()->TextSize) );
		painter.setPen( config->getCommonConfig()->colorIn );
		painter.drawText( 0, 22, textIn );
		painter.setPen( config->getCommonConfig()->colorOut );
		painter.drawText( 0, 11, textOut );
		painter.end();
		setIcon(pm);
	}
	else
	{
		m_graphPixmap->setSize(DOCK_WIDTH, DOCK_WIDTH);
		m_graphPixmap->drawGraph(m_rateInLifo, m_rateOutLifo, m_currentIndex );
		setIcon(m_graphPixmap->getPixmap());
	}
}

void DockFrame::resizeEvent( QResizeEvent *e )
{
	m_graphPixmap->setSize( 22, 22 );
	setIcon(m_graphPixmap->getPixmap());
}

void DockFrame::paintEvent( QPaintEvent *e )
{
	paintEvent( e );
	
	if( ! config->getDockFrameConfig()->showText )
	{
// 		std::cout << "pixmap: width:" << m_graphPixmap->getPixmap().width() << ", height: " << m_graphPixmap->getPixmap().height() << std::endl;	s
		QPainter p;
		p.begin((QPaintDevice *) this );
		p.drawPixmap( 0, 0, m_graphPixmap->getPixmap() );
		p.end();
	}
}

GraphPixmap::GraphPixmap() : QObject()
{
	m_graphPixmap = new QPixmap();
	m_mask = new QBitmap();
	
	m_lastIndex = 0;
	m_lastRateInArray = Q3MemArray<double>(0);
	m_lastRateOutArray = Q3MemArray<double>(0);
	
	initFromConfig();
}

void GraphPixmap::initFromConfig()
{
	KdevmonConfig *config = KdevmonConfig::getConfig();
	
	m_colorInArray[0]=config->getCommonConfig()->colorIn;
    for( int i=1; i<COLORS; i++ )
	    m_colorInArray[i]=m_colorInArray[i-1].dark(100+75/COLORS);
	m_colorOutArray[0]=config->getCommonConfig()->colorOut;
    for( int i=1; i<COLORS; i++ )
	  m_colorOutArray[i]=m_colorOutArray[i-1].dark(100+75/COLORS);
	
	drawGraph( m_lastRateInArray, m_lastRateOutArray, m_lastIndex );
}

void GraphPixmap::setSize( int w, int h )
{
// 	std::cout << "setSize: width:" << w << ", height: " << h << std::endl;
	m_graphPixmap->resize( w, h );
	m_mask->resize( w, h );
	
	drawGraph( m_lastRateInArray, m_lastRateOutArray, m_lastIndex );
}

void GraphPixmap::drawGraph( const Q3MemArray<double>& rateInArray, const Q3MemArray<double>& rateOutArray, int currentIndex )
{
	if( (m_graphPixmap->isNull()) || ((int) rateInArray.size() < m_graphPixmap->width()) )
		return;

	KdevmonConfig *conf = KdevmonConfig::getConfig();
	QPainter painter;
	
	static Q3MemArray<int> lengthInArray(0);
	static Q3MemArray<int> lengthOutArray(0);
	int
		w = m_graphPixmap->width(),
		h = m_graphPixmap->height();
	int i;
	if( (int) lengthInArray.size() != w )
	{
		lengthInArray.resize(w);
		lengthOutArray.resize(w);
	}
	lengthInArray.fill(0);
	lengthOutArray.fill(0);
	
	// calculate length of every bar of the graph
	double scaling;
	if( conf->getGraphConfig()->scalingMode == Common::MODE_CBRT )
		scaling = cbrt(conf->getGraphConfig()->scaling / 8.0);
	else
		scaling = conf->getGraphConfig()->scaling / 8.0;

	double rateIn, rateOut;
	for( int i=0; i<w; i++ )
	{
		int index = currentIndex-i<0 ? rateInArray.size()+currentIndex-i
			: currentIndex-i;
		if( conf->getGraphConfig()->scalingMode == Common::MODE_CBRT )
		{
			rateIn = cbrt( rateInArray[index] );
			rateOut = cbrt( rateOutArray[index] );
		}
		else
		{
			rateIn = rateInArray[index];
			rateOut = rateOutArray[index];
		}
		double maxScale = scaling;
		if ( conf->getGraphConfig()->graphPosition == Common::POS_BOTTOM )
		{
			if (rateIn > maxScale) maxScale = rateIn;
			if (rateOut > maxScale) maxScale = rateOut;
		}
		else if ((rateIn + rateOut) > maxScale) maxScale = rateIn + rateOut;

		lengthInArray[w-1-i] = (int) (rateIn / maxScale * h);
		lengthOutArray[w-1-i] = (int) (rateOut / maxScale * h);
	}

	// reset pixmap
	m_graphPixmap->fill( conf->getMainFrameConfig()->bgColor );
	
	// reset mask
	m_mask->fill( Qt::color0 );
	
	// draw grid	
	int space = getGridRowHeight();
	
// 	std::cout <<"painter begin"<< "m_graphPixmap: " << m_graphPixmap << std::endl;
// 	std::cout << "pixmap: width:" << m_graphPixmap->getPixmap().width() << ", height: " << m_graphPixmap->getPixmap().height() << std::endl;	
	painter.begin( m_graphPixmap );
	painter.setPen( conf->getMainFrameConfig()->gridColor );
	for( i = 0; i < h; i+=space )
		painter.drawLine(0, h-i, w, h-i);
	painter.end();
	
	painter.begin( m_mask );
	painter.setPen( Qt::color1 );
	for( i = 0; i < h; i+=space )
		painter.drawLine(0, h-i, w, h-i);
	painter.end();
	
	// draw graph
	painter.begin( m_graphPixmap );
	if( conf->getGraphConfig()->graphStyle == Common::STYLE_LINES )
	{
		painter.setPen( QPen(m_colorInArray[0], 1, Qt::SolidLine) );
		for( i=1; i<w; i++ )
			if( (lengthInArray[i]!=0) || (lengthInArray[i-1]!=0) )
				painter.drawLine(
					i-1,
					h-lengthInArray[i-1],
					i,
					h-lengthInArray[i] );

		painter.setPen( QPen(m_colorOutArray[0], 1, Qt::SolidLine) );
		for( i=1; i<w; i++ )
			if( (lengthOutArray[i]!=0) || (lengthOutArray[i-1]!=0) )
			{
				if( conf->getGraphConfig()->graphPosition ==
					Common::POS_BOTTOM )
					painter.drawLine(
						i-1,
						h-lengthOutArray[i-1],
						i,
						h-lengthOutArray[i] );
				else if ( conf->getGraphConfig()->graphPosition ==
					Common::POS_TOP_BOTTOM )
					painter.drawLine(
						i-1,
						lengthOutArray[i-1],
						i,
						lengthOutArray[i] );
			}
	}
	else if (conf->getGraphConfig()->graphStyle == Common::STYLE_SHADED )
	{
		for( int color = 0; color < COLORS; color++ )
		{
			painter.setPen( m_colorInArray[color] );
			for( i=0; i<w; i++ )
				if( lengthInArray[i] != 0 )
					painter.drawLine(
						i,
						h - lengthInArray[i]*(COLORS-color)/COLORS,
						i,
						h );
		}

		for( int color = 0; color < COLORS; color++ )
		{
			painter.setPen( m_colorOutArray[color] );
			for( i=0; i<w; i++ )
				if( lengthOutArray[i] != 0 )
				{
					if( conf->getGraphConfig()->graphPosition ==
						Common::POS_TOP_BOTTOM )
						painter.drawLine(
							i,
							0,
							i,
							lengthOutArray[i]*(COLORS-color)/COLORS );
					else if ( conf->getGraphConfig()->graphPosition ==
						Common::POS_BOTTOM )
						painter.drawLine(
							i,
							h - lengthOutArray[i]*(COLORS-color)/COLORS,
							i,
							h);
				}
		}

		if ( conf->getGraphConfig()->graphPosition ==
			Common::POS_BOTTOM )
		{
			painter.setPen( QPen(m_colorInArray[0], 1, Qt::SolidLine) );
			for( i=1; i<w; i++ )
				if(((lengthOutArray[i] > lengthInArray[i]) ||
					(lengthOutArray[i - 1] > lengthInArray[i - 1])) &&
					(lengthInArray[i] != 0) )
					painter.drawLine(
						i-1,
						h-lengthInArray[i-1],
						i,
						h-lengthInArray[i] );
		}

	}
	else if ( conf->getGraphConfig()->graphStyle == Common::STYLE_SOLID )
	{
		painter.setPen( m_colorInArray[0] );
		for( i=0; i<w; i++ )
			if( lengthInArray[i] != 0 )
				painter.drawLine(
					i,
					h - lengthInArray[i],
					i,
					h );

		painter.setPen( m_colorOutArray[0] );
		for( i=0; i<w; i++ )
			if( lengthOutArray[i] != 0 )
			{
				if( conf->getGraphConfig()->graphPosition ==
					Common::POS_TOP_BOTTOM )
					painter.drawLine(
						i,
						0,
						i,
						lengthOutArray[i] );
				else if ( conf->getGraphConfig()->graphPosition ==
					Common::POS_BOTTOM )
					painter.drawLine(
						i,
						h - lengthOutArray[i],
						i,
						h);
			}
		if ( conf->getGraphConfig()->graphPosition ==
			Common::POS_BOTTOM )
		{
			painter.setPen( QPen(m_colorInArray[0], 1, Qt::SolidLine) );
			for( i=1; i<w; i++ )
				if(((lengthOutArray[i] > lengthInArray[i]) ||
					(lengthOutArray[i - 1] > lengthInArray[i - 1])) &&
					(lengthInArray[i] != 0) )
					painter.drawLine(
						i-1,
						h-lengthInArray[i-1],
						i,
						h-lengthInArray[i] );
		}
	}
	painter.end();
	
	painter.begin( m_mask );
	painter.setPen( Qt::color1 );
	
	if( conf->getGraphConfig()->graphStyle == Common::STYLE_LINES )
	{
		for( i=1; i<w; i++ )
			if( (lengthInArray[i]!=0) || (lengthInArray[i-1]!=0) )
				painter.drawLine(
					i-1,
					h - lengthInArray[i-1],
					i,
					h - lengthInArray[i] );

		for( i=1; i<w; i++ )
			if( (lengthOutArray[i]!=0) || (lengthOutArray[i-1]!=0) )
			{
				if( conf->getGraphConfig()->graphPosition ==
					Common::POS_BOTTOM )
					painter.drawLine(
						i-1,
						h - lengthOutArray[i-1],
						i,
						h - lengthOutArray[i] );
				else if ( conf->getGraphConfig()->graphPosition ==
					Common::POS_TOP_BOTTOM )
					painter.drawLine(
						i-1,
						lengthOutArray[i-1],
						i,
						lengthOutArray[i] );
			}
	}
	else
	{
		for( i=0; i<w; i++ )
			if( lengthInArray[i]!=0 )
				painter.drawLine(
					i,
					h - lengthInArray[i],
					i,
					h );

		for( i=0; i<w; i++ )
			if( lengthOutArray[i] != 0 )
			{
				if( conf->getGraphConfig()->graphPosition ==
					Common::POS_TOP_BOTTOM )
					painter.drawLine(
						i,
						0,
						i,
						lengthOutArray[i] );
				else if ( conf->getGraphConfig()->graphPosition ==
					Common::POS_BOTTOM )
					painter.drawLine(
						i,
						h - (lengthInArray[i]+lengthOutArray[i]),
						i,
						h - lengthInArray[i] );
			}
	}
	painter.end();
	
	// if frame is not transparent, avoid flickering by not setting a mask.
	// transparent frames still flicker (dont know how I can fix it)
	if( conf->getMainFrameConfig()->transparent )
		m_graphPixmap->setMask( *m_mask );
	else
		m_graphPixmap->setMask( QBitmap() );
}

int GraphPixmap::getGridRowHeight()
{
	KdevmonConfig *conf = KdevmonConfig::getConfig();	

	int h = m_graphPixmap->height() - 1;
	int preferredHeight = h < 100 ? 10 : 20;
	double scaling = conf->getGraphConfig()->scaling / 8.0;

	double tries[6];
	tries[0] = pow(10, (int)log10(scaling)-1);
	tries[1] = 2*tries[0];
	tries[2] = 5*tries[0];
	tries[3] = 10*tries[0];
	tries[4] = 20*tries[0];
	tries[5] = 50*tries[0];
	
	double error = 1000;
	double tmp;
	int found = 0;
	int i;
	for( i = 0; i < 6; i++ )
		if( (tmp=fabs((tries[i]/scaling*h)-preferredHeight)) < error )
		{
			found = i;
			error = tmp;
		}
		
	return (int) fabs(tries[found]/scaling*h);
}


BpsFrame::BpsFrame ( QWidget *parent )
	: QFrame ( parent )
{
	font = QFont( "times" );
	pixmap = new QPixmap( width(), height() );
	pixmapColor = Qt::black;
	pixmap->fill( pixmapColor );
}

BpsFrame::~BpsFrame()
{}

void BpsFrame::resizeEvent( QResizeEvent * )
{
	this->font = QFont( "times", width()/4<height() ? width()/4 : height() );
	pixmap->resize( size() );
	pixmap->fill( pixmapColor );
	repaint();
}

void BpsFrame::setPixmapColor( const QColor& color )
{
	this->pixmapColor = color;
	this->pixmap->fill( color );
}

void BpsFrame::drawText( const QString& text, const QColor& color )
{
	this->text = text;
	this->color = color;
	repaint();
}

void BpsFrame::paintEvent(QPaintEvent *e)
{
	QFrame::paintEvent( e );
	QPainter painter;
	painter.begin( this );
	painter.fillRect( rect(), this->pixmapColor );
	painter.setPen( color );
	painter.setFont( this->font );
	painter.drawText( rect(), Qt::AlignCenter, text );
	painter.end();
}


BarFrame::BarFrame( QWidget *parent )
	: QFrame ( parent)
{
	this->config = KdevmonConfig::getConfig();
	pixmapColor = Qt::black;
	colorIn = Qt::black;
	colorOut = Qt::black;
	this->pixmap = new QPixmap(width(), height());
	this->pixmap->fill();
	this->lastPercentIn = 0;
	this->lastPercentOut = 0;

	initFromConfig();
}

BarFrame::~BarFrame()
{}

void BarFrame::initFromConfig ()
{
	pixmapColor = config->getMainFrameConfig()->bgColor;
	colorIn = config->getCommonConfig()->colorIn;
	colorOut = config->getCommonConfig()->colorOut;
	graphPosition = config->getGraphConfig()->graphPosition;

	if (graphPosition == Common::POS_BOTTOM)
		setFixedWidth (20);
	else
		setFixedWidth (10);

	pixmap->fill(pixmapColor);

	updatePixmap(lastPercentIn, lastPercentOut);
}

void BarFrame::resizeEvent( QResizeEvent * )
{
	pixmap->resize( size() );
	updatePixmap(this->lastPercentIn, this->lastPercentOut);
}

void BarFrame::updatePixmap(double percentIn, double percentOut)
{
	this-> percentIn = percentIn;
	this->percentOut = percentOut;
	repaint();
}

void BarFrame::paintEvent(QPaintEvent *e)
{
	
	QFrame::paintEvent( e );
	int w = graphPosition==Common::POS_BOTTOM?width()/2:width();
	QPainter painter;

	painter.begin(this );
	painter.fillRect(0, 0, width(),height(), this->pixmapColor );
	painter.setPen(this->pixmapColor);
	
	painter.setBrush(this->colorIn);
	for(int i=0; i<pixmap->height()*percentIn; i+=pixmap->height()/10)
		painter.drawRect(0, pixmap->height()-i,
			w, pixmap->height()/10);
	
	painter.setBrush(this->colorOut);
	if (graphPosition == Common::POS_BOTTOM)
		for (int i=0; i<pixmap->height()*percentOut; i+=pixmap->height()/10)
			painter.drawRect (pixmap->width()/2, pixmap->height()-i,
				w, pixmap->height()/10);
	else
		for (int i=0; i<pixmap->height()*percentOut; i+=pixmap->height()/10)
			painter.drawRect (0, i,
				w, pixmap->height()/10);
			
	painter.end();
	
}

void BarFrame::clear()
{
	this->lastPercentIn = 0; this->lastPercentOut = 0;
	this->updatePixmap( 0, 0 );
}

QString Tools::double2bit( double value )
{
	QString s;
	
	value *= 8;

	if( value > 1000000000 ) s.sprintf("%1.3fg", value/1000000000);
	else if( value > 1000000 ) s.sprintf("%2.2fm", value/1000000);
	else if ( value > 1000 ) s.sprintf("%3.1fk", value/1000);
	else s.sprintf("%4.0f", value);

	return s;
}

QString Tools::double2byte( double value )
{
	QString s;
	
	if( value > 1073741824 ) s.sprintf("%1.3fG", value/1073741824);
	else if( value > 1048576 ) s.sprintf("%2.2fM", value/1048576);
	else if ( value > 1024 ) s.sprintf("%3.1fK", value/1024);
	else s.sprintf("%4.0f", value);

	return s;
}
