/*
 * newsscroller.cpp
 *
 * Copyright (c) 2000 Frerich Raabe <raabe@kde.org>
 * Copyright (c) 2001 Malte Starostik <malte.starostik@t-online.de>
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <qpainter.h>

#include <kdebug.h>
#include <kstddirs.h>
#include <klocale.h>
#include <kcursor.h>

#include "newsscroller.h"
#include "newssource.h"

class Headline
{
public:
	Headline(NewsScroller *scroller, Article *article)
		: _scroller(scroller),
		 _article(article),
		 _normal(0),
		 _highlighted(0)
	{
	};
	
	virtual ~Headline()
	{
		reset();
	}

	Article *article() const { return _article; }	
	
	int width() { return pixmap()->width(); }
	
	QPixmap *pixmap(bool highlighted = false, bool underlineHighlighted = true)
	{
		QPixmap *result = highlighted ? _highlighted : _normal;
		if (!result)
		{
			const QFontMetrics &metrics = _scroller->fontMetrics();
			result = new QPixmap(metrics.width(_article->headline()), metrics.height());
			result->fill(_scroller->backgroundColor());
			QPainter p(result);
			QFont f = _scroller->font();
			if (highlighted)
				f.setUnderline(underlineHighlighted);
			p.setFont(f);
			p.setPen(highlighted ? _scroller->highlightedColor() : _scroller->foregroundColor());
			p.drawText(0, result->height() - metrics.descent(), _article->headline());
			if (highlighted)
				_highlighted = result;
			else
				_normal = result;
		}
		return result;
	}

	void reset()
	{
		delete _normal;
		delete _highlighted;
	}

private:
	NewsScroller *_scroller;
	Article *_article;
	QPixmap *_normal;
	QPixmap *_highlighted;
};

NewsScroller::NewsScroller(QWidget *parent, uint speed, const char *name)
	: QFrame(parent, name)
{
	setFrameStyle(StyledPanel | Sunken);
	_headlines.setAutoDelete(true);
	setDirection(Left);

	_scrollTimer = new QTimer(this);
	connect(_scrollTimer, SIGNAL(timeout()), SLOT(slotTimeout()));
	setSpeed(speed);
}

QSizePolicy NewsScroller::sizePolicy() const
{
	return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}

void NewsScroller::clear()
{
	_headlines.clear();
	reset();
}

void NewsScroller::addHeadline(Article *article)
{
	_headlines.append(new Headline(this, article));
	reset(true, true);
}

void NewsScroller::setSpeed(uint speed)
{
	if (_speed != speed) {
		_scrollTimer->stop();
		_speed = speed;
		if (_speed > 0) {
			_scrollTimer->start(_speed);
			setAcceptDrops(false);
		}
	}
}

void NewsScroller::setForegroundColor(const QColor &color)
{
	_foregroundColor = color;
	reset();
}

void NewsScroller::setBackgroundColor(const QColor &color)
{
	_backgroundColor = color;
	reset();
}

void NewsScroller::setHighlightedColor(const QColor &color)
{
	_highlightedColor = color;
	reset();
}

void NewsScroller::setFont(const QFont &font)
{
	QFrame::setFont(font);
	reset();
}

void NewsScroller::setEndless(bool endless)
{
	_endless = endless;
	repaint(true);
}

void NewsScroller::setUnderlineHighlighted(bool underlineHighlighted)
{
	_underlineHighlighted = underlineHighlighted;
	repaint(true);
}

void NewsScroller::setDirection(const Direction direction)
{
	reset(false);
	if (_direction != direction)
	{
		_direction = direction;
		switch (_direction)
		{
			case Left:
				_offset = contentsRect().width();
				break;
			case Right:
				_offset = - scrollWidth();
				break;
			case Up:
				_offset = contentsRect().height();
				break;
			case Down:
				_offset = - scrollHeight();
		}
		repaint(false);
	}
}

void NewsScroller::slotScrollText(int distance)
{
	switch (_direction)
	{
		case Left:
			_offset -= distance;
			if (_offset <= - scrollWidth())
				_offset = _endless ? _offset + scrollWidth() - _separator.width() : contentsRect().width();
			break;
		case Right:
			_offset += distance;
			if (_offset >= contentsRect().width())
				_offset = (_endless ? _offset + _separator.width() : 0) - scrollWidth();
			break;
		case Up:
			_offset -= distance;
			if (_offset <= - scrollHeight())
				_offset = _endless ? _offset + scrollHeight() - fontMetrics().height() : contentsRect().height();
			break;
		case Down:
			_offset += distance;
			if (_offset >= contentsRect().height())
				_offset = (_endless ? _offset + fontMetrics().height() : 0) - scrollHeight();
	}
	
	QPoint pt = mapFromGlobal(QCursor::pos());
	
	if (contentsRect().contains(pt))
		updateActive(pt);
	
	repaint(false);
}

void NewsScroller::mousePressEvent(QMouseEvent *e)
{
	if (e->button() == QMouseEvent::LeftButton) {
		_mouseDrag = true;
		if (_direction == Left || _direction == Right)
			_dragStart = e->x();
		else
			_dragStart = e->y();
		_temp = _speed;
		if (_activeHeadline)
			_tempHeadline = _activeHeadline->article()->headline();
		setSpeed(0);
	}
}

void NewsScroller::mouseReleaseEvent(QMouseEvent *e)
{
	if (e->button() == QMouseEvent::LeftButton && _activeHeadline &&
			_activeHeadline->article()->headline() == _tempHeadline) {
		_activeHeadline->article()->open();
		_tempHeadline = "";
	}
	
	if (e->button() == QMouseEvent::RightButton)
		emit(contextMenu());
	
	if (_mouseDrag) {
		_mouseDrag = false;
		setSpeed(_temp);
	}
}

void NewsScroller::mouseMoveEvent(QMouseEvent *e)
{
	if (updateActive(e->pos()))
		repaint(false);

	if (_mouseDrag) {
		if (_direction == Left || _direction == Right) {
			slotScrollText(_dragStart - e->x());
			_dragStart = e->x();
		} else {
			slotScrollText(_dragStart - e->y());
			_dragStart = e->y();
		}
	}
}

void NewsScroller::wheelEvent(QWheelEvent *e)
{
	slotScrollText(-e->delta() / 5);

	// Is this neccessary at all? - Frerich
	QFrame::wheelEvent(e);
}

void NewsScroller::leaveEvent(QEvent *)
{
	if (_activeHeadline) {
		_activeHeadline = 0;
		repaint(false);
	}
}

void NewsScroller::drawContents(QPainter *p)
{
	if (!scrollWidth() || // No news and no "No News Available": uninitialized
	    !_headlines.count()) // Happens when we're currently fetching new news
		return;

	QPixmap buffer(contentsRect().width(), contentsRect().height());
	buffer.fill(_backgroundColor);
	const QFontMetrics &metrics = fontMetrics();
	int pos = _offset;
	
	if (_direction == Left || _direction == Right) {
		while (_endless && pos > 0)
			pos -= scrollWidth() - (_headlines.count() ? 0 : _separator.width());
		do {
			bitBlt(&buffer, pos, (contentsRect().height() - _separator.height()) / 2, &_separator);
			pos += _separator.width();
		} while (!_headlines.count() && pos < contentsRect().width());
	} else {
		while (_endless && pos > 0)
			pos -= scrollHeight() - (_headlines.count() ? metrics.height() : 0);
		do {
			bitBlt(&buffer, (contentsRect().width() - _separator.width()) / 2, pos, &_separator);
			pos += _separator.height();
		} while (!_headlines.count() && pos < contentsRect().height());
	}
	
	do {
		QListIterator<Headline> it(_headlines);
		for(; *it; ++it) {
			if (_direction == Left || _direction == Right) {
				if (pos + (*it)->width() >= 0)
					bitBlt(&buffer, pos, (contentsRect().height() - metrics.height()) / 2, (*it)->pixmap(*it == _activeHeadline, _underlineHighlighted));
				pos += (*it)->width();
				
				if (pos + _separator.width() >= 0)
					bitBlt(&buffer, pos, (contentsRect().height() - _separator.height()) / 2, &_separator);
				pos += _separator.width();
				
				if (pos >= contentsRect().width())
					break;
			} else {
				if (pos + metrics.height() >= 0)
					bitBlt(&buffer, (contentsRect().width() - (*it)->width()) / 2, pos, (*it)->pixmap(*it == _activeHeadline, _underlineHighlighted));
				pos += metrics.height();
				
				if (pos + _separator.height() >= 0)
					bitBlt(&buffer, (contentsRect().width() - _separator.width()) / 2, pos, &_separator);
				pos += _separator.height();
				
				if (pos > contentsRect().height())
					break;
			}
		}
		if (*it)
			break;
	}
	
	while (_endless && (((_direction == Left || _direction == Right) && pos < contentsRect().width()) || pos < contentsRect().height()));
			 
	p->drawPixmap(0, 0, buffer);
}

void NewsScroller::reset(bool bRepaint, bool bSeparatorOnly)
{
	QString sep = _headlines.count() ? QString(" +++ ") : i18n(" +++ No News Available +++");
	_separator.resize(fontMetrics().width(sep), fontMetrics().height());
	_separator.fill(_backgroundColor);
	
	QPainter p(&_separator);
	p.setFont(font());
	p.setPen(_foregroundColor);
	p.drawText(0, _separator.height() - fontMetrics().descent(), sep);
	p.end();
	
	if (!bSeparatorOnly)	
		for (QListIterator<Headline> it(_headlines); *it; ++it)
			(*it)->reset();

	if (bRepaint)
		repaint(!bRepaint);
}

int NewsScroller::scrollWidth() const
{
	int result = (_headlines.count() + 1) * _separator.width();
	
	for (QListIterator<Headline> it(_headlines); *it; ++it)
		result += (*it)->width();
	
	return result;
}

int NewsScroller::scrollHeight() const
{
	return (_headlines.count() * 2 + 1) * fontMetrics().height();
}

bool NewsScroller::updateActive(const QPoint &pt)
{
	int pos = _offset;
	const QFontMetrics &metrics = fontMetrics();
	Headline *headline = 0;
	if (_headlines.count())
	{
		if (_endless)
		{
			while (pos > 0)
				if (_direction == Left || _direction == Right)
					pos -= scrollWidth() - _separator.width();
				else
					pos -= scrollHeight() - metrics.height();
		}
		do
		{
			QListIterator<Headline> it(_headlines);
			for (; (headline = *it); ++it)
			{
				QRect rect;
				if (_direction == Left || _direction == Right)
				{
					pos += _separator.width();
					rect.moveTopLeft(QPoint(pos, (contentsRect().height() - metrics.height()) / 2));
					pos += (*it)->width();
				}
				else
				{
					pos += _separator.height();
					rect.moveTopLeft(QPoint((contentsRect().width() - (*it)->width()) / 2, pos));
					pos += metrics.height();
				}
				rect.setSize(QSize((*it)->width(), metrics.height()));
				if (rect.contains(pt))
					break;
			}
			if (*it)
				break;
		}
		while (_endless && (((_direction == Left || _direction == Right) && pos < contentsRect().width()) || pos < contentsRect().height()));
	}
	if (_activeHeadline == headline)
		return false;
	if ((_activeHeadline = headline))
		setCursor(KCursor::handCursor());
	else
		unsetCursor();
	return true;
}

void NewsScroller::slotTimeout()
{
	slotScrollText();
}

#include "newsscroller.moc"

