/***************************************************************************
                          glowbutton.cpp  -  description
                             -------------------
    begin                : Thu Sep 6 2001
    copyright            : (C) 2001 by Henning Burchardt
    email                : h_burchardt@gmx.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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <vector>
#include <math.h>
#include <qpixmap.h>
#include <qpixmapcache.h>
#include <qbitmap.h>
#include <qpainter.h>
#include <qimage.h>
#include <qtimer.h>
#include "glowbutton.h"


namespace Glow
{

using namespace KWinInternal;

//-----------------------------------------------------------------------------

// TODO: Switch to qpixmapcache?
//
std::map<const QString, const QPixmap*> PixmapCache::m_pixmapMap;

const QPixmap* PixmapCache::find(const QString& key)
{
	std::map<const QString, const QPixmap*>::const_iterator it = m_pixmapMap.find(key);
	if( it != m_pixmapMap.end() )
		return it->second;
	else
		return 0;
}

void PixmapCache::insert(const QString& key, const QPixmap *pixmap)
{
	m_pixmapMap[key] = pixmap;
}

void PixmapCache::clear()
{
	// delete all pixmaps in the cache
	for( std::map<const QString, const QPixmap*>::const_iterator it=m_pixmapMap.begin();
		it != m_pixmapMap.end(); ++it )
		delete it->second;
	m_pixmapMap.clear();
}

//-----------------------------------------------------------------------------

GlowButton::GlowButton(QWidget *parent, const char *name, const QString& tip)
	: KWinWidgetButton(parent, name, 0, tip)
{
	m_steps = 0;
	m_updateTime = 50;
	m_pixmapName = QString::null;

	m_timer = new QTimer(this);
	connect(m_timer, SIGNAL(timeout()), this, SLOT(slotTimeout()));
	m_pos = 0;
	m_timerStatus = Stop;
}

GlowButton::~GlowButton()
{
}

QString GlowButton::getPixmapName() const
{
	return m_pixmapName;
}

void GlowButton::setPixmapName(const QString& pixmapName)
{
	m_pixmapName = pixmapName;

	const QPixmap *pixmap = PixmapCache::find(pixmapName);
	if( ! pixmap )
		return;

	// set mask
	QBitmap mask(width(), height());
	mask.fill(Qt::color0);
	bitBlt(&mask, 0, 0, pixmap->mask(), 0, 0, width(), height());
	setMask(mask);

	// set steps
	m_steps = pixmap->height()/pixmap->width() - 1;

	repaint(false);
}

void GlowButton::paintEvent( QPaintEvent *e )
{
 	QWidget::paintEvent(e);
	const QPixmap *pixmap = PixmapCache::find(m_pixmapName);
	if( pixmap != 0 )
	{
		int pos = m_pos>=0?m_pos:-m_pos;
		bitBlt(this, 0, 0, pixmap, 0, pos*height(), width(), height());
	}
}

void GlowButton::enterEvent( QEvent *e )
{
	if( m_pos<0 )
		m_pos=-m_pos;
	m_timerStatus = Run;
	if( ! m_timer->isActive() )
		m_timer->start(m_updateTime);
	KWinWidgetButton::enterEvent(e);
}

void GlowButton::leaveEvent( QEvent *e )
{
	m_timerStatus = Stop;
	if( ! m_timer->isActive() )
		m_timer->start(m_updateTime);
	KWinWidgetButton::leaveEvent(e);
}

void GlowButton::mousePressEvent( QMouseEvent *e )
{
	if( m_timer->isActive() )
		m_timer->stop();
	m_pos = m_steps;
	repaint(false);
	KWinWidgetButton::mousePressEvent(e);
}

void GlowButton::mouseReleaseEvent( QMouseEvent *e )
{
	QPoint p = mapToParent(mapFromGlobal(e->globalPos()));
	if( ! m_timer->isActive() ) {
		m_timer->start(m_updateTime);
	}
	if( ! geometry().contains(p) ) {
		m_timerStatus = Stop;
	} else {
		emit clicked();
		emit clicked(e->button());
	}
	KWinWidgetButton::mouseReleaseEvent(e);
}

void GlowButton::slotTimeout()
{
	repaint(false);

	if( m_pos>=m_steps-1 ) {
		m_pos = -m_pos;
	}
	if( m_timerStatus==Stop ) {
		if( m_pos==0 ) {
			m_timer->stop();
			return;
		} else if( m_pos>0 ) {
			m_pos = -m_pos;
		}
	}

	m_pos++;
}

//-----------------------------------------------------------------------------

GlowButtonFactory::GlowButtonFactory()
{
	m_steps = 20;
}

int GlowButtonFactory::getSteps()
{
	return m_steps;
}

void GlowButtonFactory::setSteps(int steps)
{
	m_steps = steps;
}

QPixmap* GlowButtonFactory::createGlowButtonPixmap(
	const QSize& size, const QColor& glowColor,
	const QColorGroup& colorGroup, const QPixmap& fgPixmap )
{
	QPixmap pm(size);
	pm.fill(glowColor);
	return createGlowButtonPixmap(
		size, pm, colorGroup, fgPixmap);
}

QPixmap* GlowButtonFactory::createGlowButtonPixmap(
	const QSize& size, const QPixmap& glowPixmap,
	const QColorGroup& colorGroup, const QPixmap& fgPixmap)
{
	int w = size.width();
	int h = size.height();

	std::vector< std::vector<float> > intensityField;
	intensityField.resize(h);
	for( int y=0; y<h; y++ ) {
		intensityField[y].resize(w);
	}
	for( int y=0; y<h; y++ ) {
		for( int x=0; x<w; x++ ) {
			float distance = (y+x)/(float)(w+h);
//			float distance = sqrt(
//				((w/2.0-x)*(w/2.0-x)+(h/2.0-y)*(h/2.0-y))/(w*w/4.0+h*h/4.0) );
//			float distance = (fabs(w/2.0-x)+fabs(h/2.0-y))/(w/2+h/2);
			intensityField[y][x] = (1-distance);
		}
	}
	QPainter painter;
	QPixmap upPixmap(w, h);
	upPixmap = DrawUtils::drawRoundButton(size, colorGroup);
	painter.begin(&upPixmap);
	painter.drawPixmap(0, 0, fgPixmap);
	painter.end();

	QPixmap downPixmap(w, h);
	downPixmap = DrawUtils::drawRoundButton(size, colorGroup);
	painter.begin(&downPixmap);
	painter.drawPixmap(1, 1, fgPixmap);
	painter.end();

	QPixmap *pm = new QPixmap(w, (m_steps+1)*h);
	QPixmap fadedPixmap;
	for( int i=0; i<m_steps; i++ ) {
		float intensity = (float) i/m_steps;
		fadedPixmap = DrawUtils::fadePixmaps(upPixmap, glowPixmap,
			intensityField, intensity);
		bitBlt(pm, 0, i*h, &fadedPixmap);
	}
	fadedPixmap = DrawUtils::fadePixmaps(downPixmap, glowPixmap,
		intensityField, 1.0f);
	bitBlt(pm, 0, m_steps*h, &fadedPixmap);

	QBitmap simpleMask = DrawUtils::drawRoundButtonMask(size);
	QBitmap mask(w, (m_steps+1)*h);
	mask.fill(Qt::color0);
	for( int i=0; i<m_steps+1; i++ )
		bitBlt(&mask, 0, i*h, &simpleMask);

	pm->setMask(mask);
	return pm;
}

GlowButton* GlowButtonFactory::createGlowButton(
	QWidget *parent, const char* name, const QString& tip)
{
	GlowButton *glowButton = new GlowButton(parent, name, tip);
	return glowButton;
}

//-----------------------------------------------------------------------------

QPixmap DrawUtils::fadePixmaps(
	const QPixmap& bgPixmap, const QPixmap& fgPixmap,
	const std::vector< std::vector<float> >& intensityField, float intensity )
{
	QImage bgImg = bgPixmap.convertToImage();
	QImage fgImg = fgPixmap.convertToImage();
	QImage newImg(bgImg.width(), bgImg.height(), bgImg.depth());
	int w = bgImg.width();
	int h = bgImg.height();
	for( int y=0; y<h; y++ ) {
		uint* bgLine = (uint*) bgImg.scanLine(y);
		uint* fgLine = (uint*) fgImg.scanLine(y);
		uint* newLine = (uint*) newImg.scanLine(y);
		for( int x=0; x<w; x++ ) {
			QRgb bgRgb = *(bgLine+x);
			uint bgRed = qRed(bgRgb);
			uint bgGreen = qGreen(bgRgb);
			uint bgBlue = qBlue(bgRgb);
			QRgb fgRgb = *(fgLine+x);
			uint fgRed = qRed(fgRgb);
			uint fgGreen = qGreen(fgRgb);
			uint fgBlue = qBlue(fgRgb);
			float factor = intensityField[y][x]*intensity;
			uint newRed = (uint) ((1-factor)*bgRed + factor*fgRed);
			uint newGreen = (uint) ((1-factor)*bgGreen + factor*fgGreen);
			uint newBlue = (uint) ((1-factor)*bgBlue + factor*fgBlue);
			*(newLine+x) = qRgb( newRed, newGreen, newBlue );
		}
	}

	return QPixmap(newImg);
}

QImage &DrawUtils::drawLightEffect(
	const QImage& image, const QRect& location, float intensity)
{
	float e_w = location.width()/2.f;
	float e_h = location.height()/2.f;
	float c_x = location.x()+e_w;
	float c_y = location.y()+e_h;

	QImage *img = new QImage(image);
	for( int y=0; y<image.height(); y++ ) {
		float diff_y = QABS(c_y-y)/e_h;
		uint *image_yy = (uint*) image.scanLine(y);
		uint *img_yy = (uint*) img->scanLine(y);
		for( int x=0; x<image.width(); x++ ) {
			float diff_x = QABS(c_x-x)/e_w;
			float dist = QMIN(1.f, sqrt(diff_x*diff_x+diff_y*diff_y));
			uint image_xx = *(image_yy+x);
			QColor col(image_xx);
			int col_h, col_s, col_v;
			col.hsv(&col_h, &col_s, &col_v);
			int diff = (int) (intensity*(col_s+(255-col_v)));
			col_v += (int) ((1-dist)*diff);
			if( col_v > 255 ) {
				col_s -= col_v-255;
				col_v = 255;
			}
			col.setHsv(col_h, col_s, col_v);
			*(img_yy+x) = col.rgb();
		}
	}

	return *img;
}

QPixmap DrawUtils::drawButton(const QSize& size, const QColorGroup& colorGroup)
{
    int w = size.width();
	int h = size.height();
	bool dark = qGray(colorGroup.button().rgb()) < 127;
	QPixmap pm(w,h);
	pm.fill(colorGroup.button());
	QImage img = pm.convertToImage();
	if( dark ) {
		img = drawLightEffect(img, QRect(0,h/2,w,h), 0.5);
		img = drawLightEffect(img, QRect(w/4, 0, w/2, h/4), 0.5);
	} else {
		img = drawLightEffect(img, QRect(0,h/2,w,h), 1.0);
		img = drawLightEffect(img, QRect(w/4, 0, w/2, h/4), 1.0);
	}
	pm.convertFromImage(img);
	return pm;
}

QPixmap DrawUtils::drawRoundButton(const QSize& size, const QColorGroup& colorGroup)
{
	QPixmap pm = drawButton(size, colorGroup);
	QPainter p;
	p.begin(&pm);
	if( qGray(colorGroup.button().rgb()) < 127 ) {
		p.setPen(colorGroup.light());
	} else {
		p.setPen(colorGroup.mid());
	}
	p.drawEllipse(0,0,size.width(),size.height());
	pm.setMask(drawRoundButtonMask(size));
	return pm;
}

QBitmap DrawUtils::drawRoundButtonMask(const QSize& size)
{
	QPainter p;
	QBitmap mask(size);
	mask.fill(Qt::color0);
	p.begin(&mask);
	p.setPen(Qt::color1);
	p.setBrush(Qt::color1);
	p.drawEllipse(0, 0, size.width(), size.height());
	p.end();
	return mask;
}

#if 0
QPixmap DrawUtils::drawRoundRectButton(const QSize& size, const QColorGroup& colorGroup)
{
	QPixmap pm = drawButton(size, colorGroup);
	QPainter p;
	p.begin(&pm);
	if( qGray(colorGroup.button().rgb()) < 127 )
		p.setPen(colorGroup.light());
	else
		p.setPen(colorGroup.mid());
	p.drawRoundRect(0,0,size.width(),size.height());
	pm.setMask(drawRoundRectButtonMask(size));
	return pm;
}

QBitmap DrawUtils::drawRoundRectButtonMask(const QSize& size)
{
	QPainter p;
	QBitmap mask(size);
	mask.fill(Qt::color0);
	p.begin(&mask);
	p.setPen(Qt::color1);
	p.setBrush(Qt::color1);
	p.drawRoundRect(0,0,size.width(), size.height(),50,50);
	p.end();
	return mask;
}
#endif

};

#include "glowbutton.moc"

