/*
 * psipopup.cpp - the Psi passive popup class
 * Copyright (C) 2003  Michail Pishchagin
 *
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "psipopup.h"
#include "common.h"
#include "fancypopup.h"
#include "fancylabel.h"
#include "userlist.h"
#include "alerticon.h"
#include "psievent.h"
#include "im.h"
#include "psicon.h"
#include "psiaccount.h"

#include <qapplication.h>
#include <qlayout.h>

//----------------------------------------------------------------------------
// PsiPopupList
//----------------------------------------------------------------------------

class PsiPopupList : public QObject, public QPtrList<PsiPopup>
{
	Q_OBJECT
private:
	int numPopups;

public:
	PsiPopupList()
	: QObject(qApp)
	{
		setAutoDelete(true);
	}

	PsiPopup *last()
	{
		if ( !count() )
			return 0;
		return QPtrList<PsiPopup>::first();
	}

	void prepend(const PsiPopup *d)
	{
		if ( isEmpty() )
			emit started();

		connect(d, SIGNAL(destroyed(QObject *)), SLOT(popupDestroyed(QObject *)));
		QPtrList<PsiPopup>::prepend(d);
	}

signals:
	void started();
	void finished();

private slots:
	void popupDestroyed(QObject *p)
	{
		setAutoDelete(false);
		remove((PsiPopup *)p);
		setAutoDelete(true);

		if ( isEmpty() )
			emit finished();
	}
};

static PsiPopupList *psiPopupList = 0;

//----------------------------------------------------------------------------
// PsiPopup::Private
//----------------------------------------------------------------------------

class PsiPopup::Private : public QObject
{
	Q_OBJECT
public:
	Private(PsiPopup *p);
	~Private();

	void init(const Icon *titleIcon, QString titleText, PsiAccount *_acc, PopupType type);
	QString clipText(QString);
	QBoxLayout *createContactInfo(const Icon *icon, QString text);

private slots:
	void popupDestroyed();
	void popupClicked(int);
	void eventDestroyed();

public:
	PsiCon *psi;
	PsiAccount *account;
	FancyPopup *popup;
	PsiPopup *psiPopup;
	QString id;
	PopupType popupType;
	Jid jid;
	Status status;
	PsiEvent *event;
	Icon *titleIcon;
	bool display;
};

PsiPopup::Private::Private(PsiPopup *p)
{
	psiPopup = p;
	popup = 0;
	popupType = AlertNone;
	event = 0;
	titleIcon = 0;
}

PsiPopup::Private::~Private()
{
	if ( popup )
		delete popup;
	if ( titleIcon )
		delete titleIcon;
	popup = 0;
}

void PsiPopup::Private::init(const Icon *_titleIcon, QString titleText, PsiAccount *acc, PopupType type)
{
	psi = acc->psi();
	account = acc;
	display = true;

	if ( !psiPopupList )
		psiPopupList = new PsiPopupList();

	FancyPopup *last = 0;
	if ( psiPopupList->last() )
		last = psiPopupList->last()->popup();

	if ( type != AlertNone )
		titleIcon = new AlertIcon(_titleIcon);
	else
		titleIcon = new Icon(*_titleIcon);

	FancyPopup::setHideTimeout( option.ppHideTime );
	FancyPopup::setBorderColor( option.ppBorderColor );

	popup = new FancyPopup(titleText, titleIcon, last, false);
	connect(popup, SIGNAL(clicked(int)), SLOT(popupClicked(int)));
	connect(popup, SIGNAL(destroyed()), SLOT(popupDestroyed()));

	// create id
	if ( _titleIcon )
		id += _titleIcon->name();
	id += titleText;
}

void PsiPopup::Private::popupDestroyed()
{
	popup = 0;
	psiPopup->deleteLater();
}

void PsiPopup::Private::popupClicked(int button)
{
	if ( button == (int)LeftButton ) {
		if ( event )
			psi->processEvent( event );
		else if ( account ) {
			// FIXME: it should work in most cases, but
			// maybe it's better to fix UserList::find()?
			Jid j( jid.userHost() );
			account->actionDefault( j );
		}
	}

	if ( popup )
		popup->hide();
}

void PsiPopup::Private::eventDestroyed()
{
	if ( (popupType == AlertMessage || popupType == AlertHeadline) && !option.popupMsgs )
		delete popup;
	if ( popupType == AlertChat && !option.popupChats /*&& option.alertOpenChats*/ )
		delete popup;
	if ( popupType == AlertHeadline && !option.popupHeadlines)
		delete popup;
	if ( popupType == AlertFile && !option.popupFiles)
		delete popup;

	event = 0;
}

QString PsiPopup::Private::clipText(QString text)
{
	if ( option.ppTextClip > 0 ) {
		// richtext will give us trouble here
		if ( ((int)text.length()) > option.ppTextClip ) {
			text = text.left( option.ppTextClip );

			// delete last unclosed tag
			/*if ( text.find("</") > text.find(">") ) {

				text = text.left( text.find("</") );
			}*/

			text += "...";
		}
	}

	return text;
}

QBoxLayout *PsiPopup::Private::createContactInfo(const Icon *icon, QString text)
{
	QHBoxLayout *dataBox = new QHBoxLayout(0);

	if ( icon ) {
		IconLabel *iconLabel = new IconLabel(popup);
		iconLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
		iconLabel->setIcon(icon);
		dataBox->addWidget(iconLabel);

		dataBox->addSpacing(5);
	}

	IconLabel *textLabel = new IconLabel(popup);
	QFont font;
	font.fromString( option.font[fPopup] );
	textLabel->setFont(font);

	textLabel->setText(QString("<qt>%1</qt>").arg(clipText(text)));
	textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
	dataBox->addWidget(textLabel);

	return dataBox;
}

//----------------------------------------------------------------------------
// PsiPopup
//----------------------------------------------------------------------------

PsiPopup::PsiPopup(const Icon *titleIcon, QString titleText, PsiAccount *acc)
{
	d = new Private(this);
	d->init(titleIcon, titleText, acc, AlertNone);
}

PsiPopup::~PsiPopup()
{
	delete d;
}

PsiPopup::PsiPopup(PopupType type, PsiAccount *acc)
{
	d = new Private(this);

	d->popupType = type;
	Icon *icon = 0;
	QString text = "Psi: ";
	bool doAlertIcon = false;

	switch(type) {
	case AlertOnline:
		text += PsiPopup::tr("Contact online");
		icon = (Icon *)IconsetFactory::iconPtr("status/online");
		break;
	case AlertOffline:
		text += PsiPopup::tr("Contact offline");
		icon = (Icon *)IconsetFactory::iconPtr("status/offline");
		break;
	case AlertStatusChange:
		text += PsiPopup::tr("Status change");
		icon = (Icon *)IconsetFactory::iconPtr("status/online");
		break;
	case AlertMessage:
		text += PsiPopup::tr("Incoming message");
		icon = (Icon *)IconsetFactory::iconPtr("psi/message");
		doAlertIcon = true;
		break;
	case AlertChat:
		text += PsiPopup::tr("Incoming chat message");
		icon= (Icon *)IconsetFactory::iconPtr("psi/chat");
		doAlertIcon = true;
		break;
	case AlertHeadline:
		text += PsiPopup::tr("Headline");
		icon= (Icon *)IconsetFactory::iconPtr("psi/headline");
		doAlertIcon = true;
		break;
	case AlertFile:
		text += PsiPopup::tr("Incoming file");
		icon= (Icon *)IconsetFactory::iconPtr("psi/file");
		doAlertIcon = true;
		break;
	default:
		break;
	}

	d->init(icon, text, acc, doAlertIcon ? type : AlertNone);
}

void PsiPopup::setData(const Icon *icon, QString text)
{
	if ( !d->popup ) {
		deleteLater();
		return;
	}

	d->popup->addLayout( d->createContactInfo(icon, text) );

	// update id
	if ( icon )
		d->id += icon->name();
	d->id += text;

	show();
}

void PsiPopup::setData(const Jid &j, const Resource &r, const UserListItem *u, const PsiEvent *event)
{
	if ( !d->popup ) {
		deleteLater();
		return;
	}

	d->jid    = j;
	d->status = r.status();
	d->event  = (PsiEvent *)event;

	if ( event )
		connect(event, SIGNAL(destroyed()), d, SLOT(eventDestroyed()));

	Icon *icon = is->statusPtr(j, r.status());
	QString text;

	QString jid = j.full();
	if ( option.ppJidClip > 0 && ((int)jid.length()) > option.ppJidClip )
		jid = jid.left( option.ppJidClip ) + "...";

	QString status;
	if ( option.ppStatusClip != 0 )
		status = r.status().status();
	if ( option.ppStatusClip > 0 )
		if ( ((int)status.length()) > option.ppStatusClip )
			status = status.left ( option.ppStatusClip ) + "...";

	QString name;
	if ( u && !u->name().isEmpty() ) {
		if ( !option.ppJidClip )
			name = "<nobr>" + u->name() + "</nobr>";
		else
			name = "<nobr>" + u->name() + " &lt;" + jid + "&gt;" + "</nobr>";
	}
	else
		name = "<nobr>&lt;" + jid + "&gt;</nobr>";

	QString statusString = plain2rich(status);
	if ( option.useEmoticons )
		statusString = emoticonify(statusString);

	if ( !statusString.isEmpty() )
		statusString = "<br>" + statusString;

	QString contactText = "<font size=\"+1\">" + name + "</font>" + statusString;

	// hack for duplicate "Contact Online"/"Status Change" popups
	PsiPopup *pp = psiPopupList->first();
	while ( pp ) {
		if ( d->jid.full() == pp->d->jid.full() && d->status.show() == pp->d->status.show() && d->status.status() == d->status.status() ) {
			if ( d->popupType == AlertStatusChange && pp->d->popupType == AlertOnline ) {
				d->display = false;
				deleteLater();
				break;
			}
		}

		pp = psiPopupList->next();
	}

	// show popup
	if ( d->popupType != AlertHeadline && (d->popupType != AlertFile || !option.popupFiles) )
		setData(icon, contactText);
	else if ( d->popupType == AlertHeadline ) {
		QVBoxLayout *vbox = new QVBoxLayout(0);
		vbox->addLayout( d->createContactInfo(icon, contactText) );

		vbox->addSpacing(5);

		const Message *jmessage = &((MessageEvent *)event)->message();
		QString message;
		if ( !jmessage->subject().isEmpty() )
			message += "<font color=\"red\"><b>" + tr("Subject:") + " " + jmessage->subject() + "</b></font><br>";
		message += plain2rich( jmessage->body() );

		QLabel *messageLabel = new QLabel(d->popup);
		QFont font = messageLabel->font();
		font.setPointSize(option.smallFontSize);
		messageLabel->setFont(font);

		messageLabel->setTextFormat(RichText);
		messageLabel->setText( d->clipText(linkify( message )) );
		messageLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
		vbox->addWidget(messageLabel);

		// update id
		if ( icon )
			d->id += icon->name();
		d->id += contactText;
		d->id += message;

		d->popup->addLayout( vbox );
		show();
	}
}

void PsiPopup::show()
{
	if ( !d->popup || !option.ppIsOn ) {
		deleteLater();
		return;
	}

	if ( !d->id.isEmpty() /*&& option.ppNoDupes*/ ) {
		PsiPopup *pp = psiPopupList->first();
		while ( pp ) {
			if ( d->id == pp->id() && pp->popup() ) {
				pp->popup()->restartHideTimer();

				d->display = false;
				break;
			}

			pp = psiPopupList->next();
		}
	}

	if ( d->display ) {
		psiPopupList->prepend( this );
		d->popup->show();
	}
	else {
		deleteLater();
	}
}

QString PsiPopup::id() const
{
	return d->id;
}

FancyPopup *PsiPopup::popup()
{
	return d->popup;
}

void PsiPopup::deleteAll()
{
	if ( !psiPopupList )
		return;

	psiPopupList->clear();
	delete psiPopupList;
	psiPopupList = 0;
}

#include "psipopup.moc"
