/***************************************************************************
                          glowclient.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 <qpixmap.h>
#include <qbitmap.h>
#include <qpainter.h>
#include <qapplication.h>
#include <qlayout.h>
#include <klocale.h>
#include <kpixmapeffect.h>
#include <kpixmap.h>
#include <kconfig.h>
#include <kwin/options.h>
#include <kwin/workspace.h>
#include "bitmaps.h"
#include "glowclient.h"
#include "glowbutton.h"


using namespace KWinInternal;


namespace Glow
{

static const int SIDE_MARGIN = 4;
static const int BOTTOM_MARGIN = 2;
static const int TITLE_MARGIN = 2;
static const int TITLE_SPACING = 1;
static const int RESIZE_HANDLE_WIDTH = 20;
static const int RESIZE_HANDLE_HEIGHT = 3;

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

GlowClientConfig::GlowClientConfig()
{
}

void GlowClientConfig::load()
{
	KConfig conf("kwinglowrc");
	conf.setGroup("General");

	const QColor defaultButtonColor(DEFAULT_BUTTON_COLOR);
	const QColor defaultCloseButtonColor(DEFAULT_CLOSE_BUTTON_COLOR);


	stickyButtonGlowColor = conf.readColorEntry(
		"stickyButtonGlowColor", &defaultButtonColor);

	helpButtonGlowColor = conf.readColorEntry(
		"helpButtonGlowColor", &defaultButtonColor);

	iconifyButtonGlowColor = conf.readColorEntry(
		"iconifyButtonGlowColor", &defaultButtonColor);

	maximizeButtonGlowColor = conf.readColorEntry(
		"maximizeButtonGlowColor", &defaultButtonColor);

	closeButtonGlowColor = conf.readColorEntry(
		"closeButtonGlowColor", &defaultCloseButtonColor);

	showResizeHandle = conf.readBoolEntry("showResizeHandle", true);
	titlebarGradientType = conf.readNumEntry("titlebarGradientType",
		KPixmapEffect::DiagonalGradient);
}

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

GlowClientGlobals *GlowClientGlobals::m_instance = 0;

GlowClientGlobals *GlowClientGlobals::instance()
{
	if( ! m_instance )
		m_instance = new GlowClientGlobals();
	return m_instance;
}

GlowClientGlobals::~GlowClientGlobals()
{
	deletePixmaps();
	m_instance = 0;
}

QString GlowClientGlobals::getPixmapName(PixmapType type, 
		bool isActive, bool isLeft, bool isSmall)
{
	QString s = getPixmapTypeName(static_cast<PixmapType>(type));
	s += "|";
	s += isActive ? "Active" : "NotActive";
	s += "|";
	s += isLeft ? "PosLeft" : "PosRight";
	s += "|";
	s += isSmall ? "SizeSmall" : "SizeNormal";
	return s;
}

GlowClientGlobals::GlowClientGlobals()
	: QObject()
{
	buttonFactory = new GlowButtonFactory();
	readConfig();
	createPixmaps();
}

void GlowClientGlobals::readConfig()
{
	config = new GlowClientConfig();
	config->load();
}

void GlowClientGlobals::reset()
{
	deletePixmaps();
	delete config;
	readConfig();
	createPixmaps();
}

void GlowClientGlobals::createPixmaps()
{	
	for( int type=0; type<=Close; type++ ) {
		createPixmap(static_cast<PixmapType>(type), false, false, false);
		createPixmap(static_cast<PixmapType>(type), false, false, true);
		createPixmap(static_cast<PixmapType>(type), false, true, false);
		createPixmap(static_cast<PixmapType>(type), false, true, true);
		createPixmap(static_cast<PixmapType>(type), true, false, false);
		createPixmap(static_cast<PixmapType>(type), true, false, true);
		createPixmap(static_cast<PixmapType>(type), true, true, false);
		createPixmap(static_cast<PixmapType>(type), true, true, true);
	}
}

void GlowClientGlobals::deletePixmaps()
{
	PixmapCache::clear();
}

const QString GlowClientGlobals::getPixmapTypeName(PixmapType type)
{
	switch(type) {
		case (StickyOn):
			return "StickyOn";
		case(StickyOff ):
			return "StickyOff";
		case(Help):
			return "Help";
		case(Iconify):
			return "Iconify";
		case(MaximizeOn):
			return "MaximizeOn";
		case(MaximizeOff):
			return "MaximizeOff";
		case(Close):
			return "Close";
		default:
			return QString::null;
	}
}

void GlowClientGlobals::createPixmap(PixmapType type, 
		bool isActive, bool isLeft, bool isSmall)
{
	int size;
	if( isSmall )
		size = SMALL_BITMAP_SIZE;
	else
		size = DEFAULT_BITMAP_SIZE;
	QColorGroup g;
	if( isLeft )
		g = options->colorGroup(Options::TitleBar, isActive);
	else
		g = options->colorGroup(Options::ButtonBg, isActive);
	QColor c;
	if( qGray(g.background().rgb()) <= 127 ) // background is dark
		c = Qt::white;
	else // background is light
		c = Qt::black;

	QPixmap pm(size, size);
	pm.fill(c);

	uchar *bits = 0; 
	QColor buttonColor;
	
	switch(type) { 
		case (StickyOn):
		{
			if( isSmall )
				bits = stickyon_small_bits;
			else
				bits = stickyon_bits;
			buttonColor = config->stickyButtonGlowColor;
			break;
		}
		case (StickyOff):
		{
			if( isSmall )
				bits = stickyoff_small_bits;
			else
				bits = stickyoff_bits;
			buttonColor = config->stickyButtonGlowColor;
			break;
		}
		case (Help):
		{
			if( isSmall )
				bits = help_small_bits;
			else
				bits = help_bits;
			buttonColor = config->helpButtonGlowColor;
			break;
		}
		case (Iconify):
		{
			if( isSmall )
				bits = minimize_small_bits;
			else
				bits = minimize_bits;
			buttonColor = config->iconifyButtonGlowColor;
			break;
		}
		case (MaximizeOn):
		{
			if( isSmall )
				bits = maximizeon_small_bits;
			else
				bits = maximizeon_bits;
			buttonColor = config->maximizeButtonGlowColor;
			break;
		}
		case (MaximizeOff):
		{
			if( isSmall )
				bits = maximizeoff_small_bits;
			else
				bits = maximizeoff_bits;
			buttonColor = config->maximizeButtonGlowColor;
			break;
		}
		case (Close):
		{
			if( isSmall )
				bits = close_small_bits;
			else
				bits = close_bits;
			buttonColor = config->closeButtonGlowColor;
			break;
		}
	}

	pm.setMask(QBitmap(size, size, bits, true));
	QPixmap* glowPm = buttonFactory->createGlowButtonPixmap(QSize(size,size), buttonColor, g, pm);
	PixmapCache::insert(getPixmapName(type, isActive, isLeft, isSmall), glowPm);
}

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

GlowClient::GlowClient(KWinInternal::Workspace *ws, WId w, QWidget *parent, const char* name )
	: KWinInternal::Client(ws, w, parent, name),
		m_stickyButton(0), m_helpButton(0), m_minimizeButton(0),
		m_maximizeButton(0), m_closeButton(0),
		m_mainLayout(0), m_leftButtonLayout(0), m_rightButtonLayout(0)
{
	createButtons();
	resetLayout();
	repaint();
}

GlowClient::~GlowClient()
{
}

void GlowClient::resizeEvent( QResizeEvent *e )
{
	Client::resizeEvent(e);
	doShape();
	repaint(false);
}

void GlowClient::paintEvent( QPaintEvent *e )
{
	Client::paintEvent(e);

	GlowClientConfig *conf = GlowClientGlobals::instance()->config;
	QRect r_this = rect();
	QRect r_title = m_titleSpacer->geometry();
	QColorGroup titleCg = options->colorGroup(Options::TitleBar, isActive());
	QColorGroup titleBlendCg = options->colorGroup(Options::TitleBlend, isActive());
	QColorGroup cg = colorGroup();
	QColor titleColor = options->color(Options::TitleBar, isActive());
	QColor titleBlendColor = options->color(Options::TitleBlend, isActive());
	QPainter p;
	QPointArray pArray, pArray2;

	// pixmap for title bar
	QSize tBSize(width(), r_title.height());
	KPixmap gradientPixmap(tBSize-QSize(3,3));
	KPixmapEffect::gradient(gradientPixmap, titleColor, titleBlendColor,
		(KPixmapEffect::GradientType) conf->titlebarGradientType);
	QPixmap titleBuffer(tBSize);
	p.begin(&titleBuffer);
	p.drawPixmap(2, 2, gradientPixmap);
	// draw caption
	p.setFont(options->font(isActive()));
	p.setPen(options->color(Options::Font, isActive()));
	p.drawText(r_title.x(), 0,
		r_title.width(), r_title.height(),
		Qt::AlignLeft | Qt::AlignVCenter | Qt::SingleLine, caption() );
	// draw borders
	pArray = QPointArray(7);
	pArray.setPoint(0, 0, tBSize.height()-1);
	pArray.setPoint(1, 0, 0);
	pArray.setPoint(2, tBSize.width()-1, 0);
	pArray.setPoint(3, tBSize.width()-1, tBSize.height()/2-1);
	pArray.setPoint(4, r_title.x()+r_title.width()-1+tBSize.height()/2,
		tBSize.height()/2-1);
	pArray.setPoint(5, r_title.x()+r_title.width()-1, tBSize.height()-1);
	pArray.setPoint(6, 0, tBSize.height()-1);
	p.setPen(titleCg.mid());
	p.drawPolyline(pArray, 3, 4);
	p.setPen(Qt::black);
	p.drawPolyline(pArray, 0, 4);
	p.setPen(titleCg.light());
	pArray2 = QPointArray(3);
	pArray2.setPoint(0, 1, tBSize.height()-2);
	pArray2.setPoint(1, 1, 1);
	pArray2.setPoint(2, tBSize.width()-2, 1);
	p.drawPolyline(pArray2);
	p.end();

	// set mask
	QBitmap titleBitmap(tBSize);
	titleBitmap.fill(Qt::color0);
	p.begin(&titleBitmap);
	p.setPen(Qt::color1);
	p.setBrush(Qt::color1);
	p.drawPolygon(pArray);
	p.end();
	titleBuffer.setMask(titleBitmap);

	p.begin(this);
	p.drawPixmap(0, 0, titleBuffer);
	p.setPen(Qt::black);
	p.drawLine(0,tBSize.height(),0,r_this.height()-1);
	p.drawLine(0,r_this.height()-1,r_this.width()-1,r_this.height()-1);
	p.drawLine(r_this.width()-1,r_this.height()-1,
		r_this.width()-1,tBSize.height()/2);
	p.end();

	// paint resize handle if necessary
	if( conf->showResizeHandle && !isTool() && isResizable() )
	{
		QSize rh_s(RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT);

		KPixmap gradientPixmap1(rh_s);
		KPixmapEffect::gradient(gradientPixmap1, titleBlendColor, titleColor,
			KPixmapEffect::HorizontalGradient);
		
		KPixmap gradientPixmap2(rh_s);
		KPixmapEffect::gradient(gradientPixmap2, titleColor, titleBlendColor,
			KPixmapEffect::HorizontalGradient);

		p.begin(this);
		p.drawPixmap(1, height() - RESIZE_HANDLE_HEIGHT, gradientPixmap1);
		p.drawPixmap(width() - RESIZE_HANDLE_WIDTH -1, height() - RESIZE_HANDLE_HEIGHT, gradientPixmap2);
		p.setPen(titleColor);
		p.drawLine(1, height() - RESIZE_HANDLE_HEIGHT, 
						RESIZE_HANDLE_WIDTH, height()-RESIZE_HANDLE_HEIGHT);
		p.drawLine(width() - RESIZE_HANDLE_WIDTH - 1, height() - RESIZE_HANDLE_HEIGHT, 
						width() - 2, height() - RESIZE_HANDLE_HEIGHT);
		p.end();
	}
}

void GlowClient::showEvent( QShowEvent *e )
{
	Client::showEvent(e);
	doShape();
	repaint(false);
}

void GlowClient::mouseDoubleClickEvent( QMouseEvent *e )
{
	if( m_titleSpacer->geometry().contains(e->pos()) )
		workspace()->performWindowOperation(
			this, options->operationTitlebarDblClick());
}

void GlowClient::activeChange(bool)
{
	updateButtonPixmaps();
	repaint(false);
}

void GlowClient::iconChange()
{
	// we have no (t yet an) icon button, so do nothing
}

void GlowClient::stickyChange(bool on)
{
	if (on) {
		m_stickyButton->setPixmapName(
			GlowClientGlobals::instance()->getPixmapName(
				GlowClientGlobals::StickyOn, isActive(),
				isLeft(m_stickyButton), isTool()));
		m_stickyButton->setTipText(i18n("Un-Sticky"));
	} else {
		m_stickyButton->setPixmapName(
			GlowClientGlobals::instance()->getPixmapName(
				GlowClientGlobals::StickyOff, isActive(),
				isLeft(m_stickyButton), isTool()));
		m_stickyButton->setTipText(i18n("Sticky"));
	}
}

void GlowClient::maximizeChange(bool on)
{
    if (on) {
		m_maximizeButton->setPixmapName(
			GlowClientGlobals::instance()->getPixmapName(
				GlowClientGlobals::MaximizeOn, isActive(),
				isLeft(m_maximizeButton), isTool()));
		m_maximizeButton->setTipText(i18n("Restore"));
	} else {
		m_maximizeButton->setPixmapName(
			GlowClientGlobals::instance()->getPixmapName(
				GlowClientGlobals::MaximizeOff, isActive(),
				isLeft(m_maximizeButton), isTool()));
		m_maximizeButton->setTipText(i18n("Maximize"));
	}
}

Client::MousePosition GlowClient::mousePosition(const QPoint &pos) const
{
	Client::MousePosition m = Nowhere;
	if( GlowClientGlobals::instance()->config->showResizeHandle && !isTool() ) {
		if( pos.y() >= height()-RESIZE_HANDLE_HEIGHT) {
			if( pos.x() < RESIZE_HANDLE_WIDTH) {
				m = BottomLeft;
			} else if( pos.x() > width() - RESIZE_HANDLE_WIDTH) {
				m = BottomRight;
			} else {
				m = Bottom;
			}
		} else {
			m = Client::mousePosition(pos);
		}
	} else {
		m = Client::mousePosition(pos);
	}
	return m;
}

void GlowClient::createButtons()
{
	GlowClientGlobals *globals = GlowClientGlobals::instance();
	GlowButtonFactory *factory = globals->buttonFactory;
	int s = isTool() ? SMALL_BITMAP_SIZE : DEFAULT_BITMAP_SIZE;
	QSize size(s,s);

	m_stickyButton = factory->createGlowButton(this, "StickyButton", i18n("Sticky"));
	m_stickyButton->setFixedSize(size);
	connect(m_stickyButton, SIGNAL(clicked()), this, SLOT(toggleSticky()));
	m_buttonList.insert(m_buttonList.end(), m_stickyButton);

	m_helpButton = factory->createGlowButton(this, "HelpButton", i18n("Help"));
	m_helpButton->setFixedSize(size);
	connect(m_helpButton, SIGNAL(clicked()), this, SLOT(contextHelp()));
	m_buttonList.insert(m_buttonList.end(), m_helpButton);

	m_minimizeButton = factory->createGlowButton(this, "IconifyButton", i18n("Minimize"));
	m_minimizeButton->setFixedSize(size);
	connect(m_minimizeButton, SIGNAL(clicked()), this, SLOT(iconify()));
	m_buttonList.insert(m_buttonList.end(), m_minimizeButton);

	m_maximizeButton=factory->createGlowButton(this, "MaximizeButton", i18n("Maximize"));
	m_maximizeButton->setFixedSize(size);
	connect(m_maximizeButton, SIGNAL(clicked(int)), this, SLOT(slotMaximize(int)));
	m_buttonList.insert(m_buttonList.end(), m_maximizeButton);

	m_closeButton = factory->createGlowButton(this, "CloseButton", i18n("Close"));
	m_closeButton->setFixedSize(size);
	connect(m_closeButton, SIGNAL(clicked()), this, SLOT(closeWindow()));
	m_buttonList.insert(m_buttonList.end(), m_closeButton);
}

void GlowClient::resetLayout()
{
	if( m_mainLayout )
		delete m_mainLayout;
	m_mainLayout = new QVBoxLayout(this, 0, 0);

	// update button positions and colors
	updateButtonPositions();
	updateButtonPixmaps();

	QBoxLayout *topLayout = new QBoxLayout(m_mainLayout, QBoxLayout::LeftToRight, 0, 0);
	topLayout->setMargin(0);
	topLayout->setSpacing(TITLE_SPACING);
	topLayout->addSpacing(SIDE_MARGIN);
	QVBoxLayout *outerLeftLayout = new QVBoxLayout(topLayout);
	outerLeftLayout->addSpacing(TITLE_MARGIN);
	outerLeftLayout->addItem(m_leftButtonLayout);
	outerLeftLayout->addSpacing(1);
	topLayout->addSpacing(SIDE_MARGIN);

	m_titleSpacer = new QSpacerItem(0, 0,
		QSizePolicy::Expanding, QSizePolicy::Expanding);
	topLayout->addItem(m_titleSpacer);

	topLayout->addSpacing(SIDE_MARGIN);
	QVBoxLayout *outerRightLayout = new QVBoxLayout(topLayout);
	outerRightLayout->addSpacing(TITLE_MARGIN);
	outerRightLayout->addItem(m_rightButtonLayout);
	outerRightLayout->addSpacing(1);
	topLayout->addSpacing(SIDE_MARGIN);

	QBoxLayout *midLayout = new QBoxLayout(m_mainLayout, QBoxLayout::LeftToRight, 0, 0);
	midLayout->addSpacing(SIDE_MARGIN);
	midLayout->addWidget(windowWrapper());
	midLayout->addSpacing(SIDE_MARGIN);

	if( GlowClientGlobals::instance()->config->showResizeHandle
		&& !isTool() && isResizable() ) {
		m_mainLayout->addSpacing(RESIZE_HANDLE_HEIGHT);
	} else {
		m_mainLayout->addSpacing(BOTTOM_MARGIN);
	}
	m_mainLayout->setStretchFactor(topLayout, 0);
	m_mainLayout->setStretchFactor(midLayout, 1);
}

void GlowClient::updateButtonPositions()
{
	QString buttons = options->titleButtonsLeft() + "|"
		+ options->titleButtonsRight();
	bool leftButtons=true;

	// hide all buttons
	for( unsigned int i=0; i<m_buttonList.size(); i++ )
		m_buttonList[i]->hide();

	m_leftButtonList.clear();
	m_rightButtonList.clear();

	// reset left and right button layout
	if(m_leftButtonLayout)
		delete m_leftButtonLayout;
	m_leftButtonLayout = new QBoxLayout(0, QBoxLayout::LeftToRight, 0, 0, 0);
	m_leftButtonLayout->setMargin(0);
	m_leftButtonLayout->setSpacing(TITLE_SPACING);
	if(m_rightButtonLayout)
		delete m_rightButtonLayout;
	m_rightButtonLayout = new QBoxLayout(0, QBoxLayout::LeftToRight, 0, 0, 0);
	m_rightButtonLayout->setMargin(0);
	m_rightButtonLayout->setSpacing(TITLE_SPACING);

	for( unsigned int i=0; i<buttons.length(); i++ )
	{
		char c = buttons[i].latin1();
		GlowButton *button = 0;
		if( c=='S' ) // sticky
			button = m_stickyButton;
		else if( c=='H' && providesContextHelp() ) // help
			button = m_helpButton;
		else if( c=='I' && isMinimizable() ) // iconify
			button = m_minimizeButton;
		else if( c=='A' && isMaximizable() ) // maximize
			button = m_maximizeButton;
		else if( c=='X' && isCloseable() ) // close
			button= m_closeButton;
		else if( c=='_' ) // spacer item
		{
			if(leftButtons)
				m_leftButtonLayout->addSpacing(4);
			else
				m_rightButtonLayout->addSpacing(4);
		}
		else if( c=='|' )
			leftButtons = false;

		if (button) {
			button->show(); // show visible buttons
			if (leftButtons) {
				m_leftButtonList.insert(m_leftButtonList.end(), button);
				m_leftButtonLayout->addWidget(button);
			} else {
				m_rightButtonList.insert(m_rightButtonList.end(), button);
				m_rightButtonLayout->addWidget(button);
			}
		}
	}
}

void GlowClient::updateButtonPixmaps()
{
	GlowClientGlobals *globals = GlowClientGlobals::instance();

	if ( isSticky() ) {
		m_stickyButton->setPixmapName(globals->getPixmapName(
			GlowClientGlobals::StickyOn, isActive(),
			isLeft(m_stickyButton), isTool()));
	} else {
		m_stickyButton->setPixmapName(globals->getPixmapName(
			GlowClientGlobals::StickyOff, isActive(),
			isLeft(m_stickyButton), isTool()));
	}
	m_helpButton->setPixmapName(globals->getPixmapName(
		GlowClientGlobals::Help, isActive(),
		isLeft(m_helpButton), isTool()));

	m_minimizeButton->setPixmapName(globals->getPixmapName(
		GlowClientGlobals::Iconify, isActive(),
		isLeft(m_minimizeButton), isTool()));

	if ( isMaximized() ) {
		m_maximizeButton->setPixmapName(globals->getPixmapName(
			GlowClientGlobals::MaximizeOn, isActive(),
			isLeft(m_maximizeButton), isTool()));
	} else {
		m_maximizeButton->setPixmapName(globals->getPixmapName(
			GlowClientGlobals::MaximizeOff, isActive(),
			isLeft(m_maximizeButton), isTool()));
	}
	m_closeButton->setPixmapName(globals->getPixmapName(
		GlowClientGlobals::Close, isActive(),
		isLeft(m_closeButton), isTool()));
}

void GlowClient::doShape()
{
	QRegion mask(rect());
	// edges

	mask -= QRegion(width()-1,0,1,1);
	mask -= QRegion(0,height()-1,1,1);
	mask -= QRegion(width()-1,height()-1,1,1);
	setMask(mask);
}

bool GlowClient::isLeft(GlowButton *button)
{
	for( unsigned int i=0; i<m_leftButtonList.size(); i++ )
		if( m_leftButtonList[i] == button )
			return true;
	return false;
}

bool GlowClient::isRight(GlowButton *button)
{
	for( unsigned int i=0; i<m_rightButtonList.size(); i++ )
		if( m_rightButtonList[i] == button )
			return true;
	return false;
}

void GlowClient::slotMaximize(int button)
{
	if(button == QMouseEvent::RightButton)
		maximize(MaximizeHorizontal);
	else if(button == QMouseEvent::MidButton)
		maximize(MaximizeVertical);
	else // if(button == QMouseEvent::LeftButton)
		maximize(MaximizeFull);
}

};

extern "C"
{
	Client * allocate(Workspace * ws, WId w)
	{
		return new Glow::GlowClient(ws, w);
	}

	void init()
	{
		Glow::GlowClientGlobals::instance();
	}

	void reset()
	{
		Glow::GlowClientGlobals::instance()->reset();
		Workspace::self()->slotResetAllClientsDelayed();
	}

	void deinit()
	{
		delete Glow::GlowClientGlobals::instance();
	}
}

#include "glowclient.moc"
