/*
 * eventdlg.cpp - dialog for sending / receiving messages and events
 * Copyright (C) 2001, 2002  Justin Karneges
 *
 * 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"eventdlg.h"

#include<qlabel.h>
#include<qcombobox.h>
#include<qlayout.h>
#include<qpushbutton.h>
#include<qdragobject.h>
#include<qdropsite.h>
#include<qmessagebox.h>
#include<qstringlist.h>
#include<qtimer.h>
#include<qcursor.h>
#include<qpopupmenu.h>
#include<qiconset.h>
#include<qdatetime.h>
#include<qtooltip.h>
#include<qheader.h>
#include<qapplication.h>
#include<qclipboard.h>
#include<qlcdnumber.h>
#include"psievent.h"
#include"psicon.h"
#include"psiaccount.h"
#include"msgmle.h"
#include"common.h"
#include"userlist.h"
#include"iconwidget.h"
#include"fancylabel.h"
#include"iconselect.h"
#include"iconwidget.h"

static QString findJid(const QString &s, int x, int *p1, int *p2)
{
	// scan backward for the beginning of a Jid
	int n1 = x;
	if(n1 >= (int)s.length())
		n1 = s.length()-1;
	for(; n1 >= 0; --n1) {
		if(s.at(n1) == ',') {
			++n1;
			break;
		}
	}
	if(n1 < 0)
		n1 = 0;
	// go forward, skipping whitespace
	for(; n1 < (int)s.length(); ++n1) {
		if(!s.at(n1).isSpace())
			break;
	}

	// now find the end of the Jid
	int n2 = n1;
	for(; n2 < (int)s.length(); ++n2) {
		if(s.at(n2) == ',')
			break;
	}
	--n2;
	// scan backwards from the end, skipping whitespace
	for(; n2 > n1; --n2) {
		if(!s.at(n2).isSpace())
			break;
	}
	++n2;

	*p1 = n1;
	*p2 = n2;

	return s.mid(n1, n2-n1);
}

//----------------------------------------------------------------------------
// ELineEdit - a line edit that handles advanced Jid entry
//----------------------------------------------------------------------------
// hack hack hack hack
struct QLineEditPrivate
{
	// qt 3.3.1
   /*QLineEdit *q;
   QString text;
   int cursor;
   int cursorTimer;
   QPoint tripleClick;
   int tripleClickTimer;
   uint frame : 1;
   uint cursorVisible : 1;
   uint separator : 1;
   uint readOnly : 1;
   uint modified : 1;
   uint direction : 5;
   uint dragEnabled : 1;
   uint alignment : 3;
   uint echoMode : 2;
   uint textDirty : 1;
   uint selDirty : 1;
   uint validInput : 1;
   int ascent;
   int maxLength;
   int menuId;
   int hscroll;*/

	char pad[sizeof(QLineEdit *) + sizeof(QString) + (sizeof(int)*2) + sizeof(QPoint) + sizeof(int) + 3 + (sizeof(int)*3)];
	int hscroll;
};

ELineEdit::ELineEdit(EventDlg *parent, const char *name)
:QLineEdit(parent, name)
{
	setAcceptDrops(TRUE);
}

void ELineEdit::dragEnterEvent(QDragEnterEvent *e)
{
	e->accept(QTextDrag::canDecode(e));
}

void ELineEdit::dropEvent(QDropEvent *e)
{
	QString str;

	if(QTextDrag::decode(e, str)) {
		Jid jid(str);
		if(!jid.isValid())
			setText(str);
		else {
			EventDlg *e = (EventDlg *)parent();
			QString name = e->jidToString(jid);

			bool hasComma = false, hasText = false;
			int len = text().length();
			while ( --len >= 0 ) {
				QChar c = text().at( len );
				if ( c == ',' ) {
					hasComma = true;
					break;
				}
				else if ( !c.isSpace() ) {
					hasText = true;
					break;
				}
			}

			if ( hasComma || !hasText )
				setText(text() + ' ' + name);
			else
				setText(text() + ", " + name);
		}
		setCursorPosition(text().length());

		repaint();
	}
}

void ELineEdit::keyPressEvent(QKeyEvent *e)
{
	QLineEdit::keyPressEvent(e);
	if(e->ascii() >= 32 && e->ascii() < 127)
		tryComplete();
}

QPopupMenu *ELineEdit::createPopupMenu()
{
	EventDlg *e = (EventDlg *)parent();
	int xoff = mapFromGlobal(QCursor::pos()).x();
	int x = characterAt(d->hscroll + xoff, 0);
	QString str = text();
	int p1, p2;
	QString j = findJid(str, x, &p1, &p2);
	if(j.isEmpty())
		return QLineEdit::createPopupMenu();

	UserResourceList list = e->getResources(j);

	setCursorPosition(p1);
	setSelection(p1, p2-p1);

	url = list;
	url.sort();

	int n = 100;
	QPopupMenu *rm = new QPopupMenu(this); //= new QPopupMenu(pm);
	connect(rm, SIGNAL(activated(int)), SLOT(resourceMenuActivated(int)));

	rm->insertItem(tr("Recipient Default"), n++);

	if(!list.isEmpty()) {
		rm->insertSeparator();

		for(UserResourceList::ConstIterator it = url.begin(); it != url.end(); ++it) {
			const UserResource &r = *it;
			QString name;
			if(r.name().isEmpty())
				name = QObject::tr("[blank]");
			else
				name = r.name();

			rm->insertItem(is->status(r.status()), name, n++);
		}
	}

	//pm->insertItem("Change Resource", rm, -1, 0);
	//pm->insertSeparator(1);

	return rm;
}

void ELineEdit::resourceMenuActivated(int x)
{
	if(x < 100)
		return;

	QString name;
	if(x == 100)
		name = "";
	else {
		int n = 101;
		for(UserResourceList::ConstIterator it = url.begin(); it != url.end(); ++it) {
			if(n == x) {
				name = (*it).name();
				break;
			}
			++n;
		}
	}
	url.clear();

	changeResource(name);
}


//----------------------------------------------------------------------------
// AttachView
//----------------------------------------------------------------------------
class AttachViewItem : public QListViewItem
{
public:
	AttachViewItem(const QString &_url, const QString &_desc, AttachView *par)
	:QListViewItem(par)
	{
		type = 0;
		url = _url;
		desc = _desc;

		setPixmap(0, IconsetFactory::icon("psi/www"));
		setText(0, url + " (" + desc + ')');
		setMultiLinesEnabled(true);
	}

	AttachViewItem(const QString &_gc, AttachView *par)
	:QListViewItem(par)
	{
		type = 1;
		gc = _gc;

		setPixmap(0, IconsetFactory::icon("psi/groupChat"));
		setText(0, gc);
		setMultiLinesEnabled(true);
	}

	int rtti() const
	{
		return 9100;
	}

	QString url, desc;
	QString gc;
	int type;
};

AttachView::AttachView(QWidget *parent, const char *name)
:QListView(parent, name)
{
	v_readOnly = false;
	addColumn(tr("Attachments"));
	setResizeMode(QListView::AllColumns);

	connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(qlv_context(QListViewItem *, const QPoint &, int)));
	connect(this, SIGNAL(doubleClicked(QListViewItem *)), SLOT(qlv_doubleClicked(QListViewItem *)));
};

AttachView::~AttachView()
{
}

void AttachView::setReadOnly(bool b)
{
	v_readOnly = b;
}

void AttachView::urlAdd(const QString &url, const QString &desc)
{
	new AttachViewItem(url, desc, this);
	childCountChanged();
}

void AttachView::gcAdd(const QString &gc)
{
	new AttachViewItem(gc, this);
	childCountChanged();
}

void AttachView::qlv_context(QListViewItem *lvi, const QPoint &pos, int)
{
	AttachViewItem *i = (AttachViewItem *)lvi;
	if(!i)
		return;

	QPopupMenu pm(this);
	if(i->type == 0) {
		pm.insertItem(tr("Go to &URL..."), 0);
		pm.insertItem(tr("Copy location"), 1);
	}
	else
		pm.insertItem(tr("Join &Groupchat..."), 0);
	pm.insertSeparator();
	pm.insertItem(tr("Remove"), 2);

	if(v_readOnly)
		pm.setItemEnabled(2, false);

	int n = pm.exec(pos);
	if(n == -1)
		return;

	if(n == 0) {
		if(i->type == 0)
			goURL(i->url);
		else
			actionGCJoin(i->gc);
	}
	else if(n == 1) {
		QApplication::clipboard()->setText(i->url, QClipboard::Clipboard);
		if(QApplication::clipboard()->supportsSelection())
			QApplication::clipboard()->setText(i->url, QClipboard::Selection);
	}
	else if(n == 2) {
		delete i;
		childCountChanged();
	}
}

void AttachView::qlv_doubleClicked(QListViewItem *lvi)
{
	AttachViewItem *i = (AttachViewItem *)lvi;
	if(!i)
		return;

	if(i->type == 0)
		goURL(i->url);
	else
		actionGCJoin(i->gc);
}

void AttachView::goURL(const QString &_url)
{
	if(_url.isEmpty())
		return;

	QString url = _url;
	if(url.find("://") == -1)
		url.insert(0, "http://");

	openURL(url);
}

UrlList AttachView::urlList() const
{
	UrlList list;

	for(AttachViewItem *i = (AttachViewItem *)firstChild(); i; i = (AttachViewItem *)i->nextSibling())
		list += Url(i->url, i->desc);

	return list;
}

void AttachView::addUrlList(const UrlList &list)
{
	for(QValueList<Url>::ConstIterator it = list.begin(); it != list.end(); ++it) {
		const Url &u = *it;
		urlAdd(u.url(), u.desc());
	}
}


//----------------------------------------------------------------------------
// AddUrlDlg
//----------------------------------------------------------------------------
AddUrlDlg::AddUrlDlg(QWidget *parent, const char *name)
:AddUrlUI(parent, name, true)
{
#ifndef Q_WS_MAC
	setIcon(IconsetFactory::icon("psi/www"));
#endif

	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
	connect(pb_ok, SIGNAL(clicked()), SLOT(accept()));
}

AddUrlDlg::~AddUrlDlg()
{
}


//----------------------------------------------------------------------------
// EventDlg - a window to read and write events
//----------------------------------------------------------------------------
class EventDlg::Private : public QObject
{
	Q_OBJECT
public:
	Private(EventDlg *d) {
		dlg = d;
	}

	EventDlg *dlg;
	PsiCon *psi;
	PsiAccount *pa;

	bool composing;

	AccountsComboBox *cb_ident;
	QComboBox *cb_type;
	QLabel *lb_ident, *lb_time;
	IconLabel *lb_status;
	ELineEdit *le_to;
	QLineEdit *le_from, *le_subj;
	QLCDNumber *lcd_count;
	IconToolButton *tb_url, *tb_info, *tb_history, *tb_pgp, *tb_icon;
	IconLabel *lb_pgp;
	bool enc;
	int transid;
	IconButton *pb_next;
	IconButton *pb_close, *pb_quote, *pb_deny, *pb_send, *pb_reply, *pb_chat, *pb_auth;
	ChatView *mle;
	AttachView *attachView;
	QTimer *whois;
	QString lastWhois;
	Jid jid, realJid;
	QString thread;
	QStringList completionList;
	Icon *anim, *nextAnim;
	int nextAmount;

	bool urlOnShow;

	Message m;
	QStringList sendLeft;

public slots:
	void addEmoticon(const Icon *icon) {
		if ( !dlg->isActiveWindow() )
		     return;

		QString text;

		QDict<QString> itext = icon->text();
		QDictIterator<QString> it ( itext );
		for ( ; it.current(); ++it) {
			if ( it.current() && !it.current()->isEmpty() ) {
				text = *(it.current()) + " ";
				break;
			}
		}

		if ( !text.isEmpty() )
			mle->insert( text );
	}

	void addEmoticon(QString text) {
		if ( !dlg->isActiveWindow() )
		     return;

		mle->insert( text + " " );
	}

	void updateCounter() {
		lcd_count->display((int)mle->text().length());
	}
};

EventDlg::EventDlg(const QString &to, PsiCon *psi, PsiAccount *pa)
:AdvancedWidget<QWidget>(0, 0, WDestructiveClose | WGroupLeader)
{
	d = new Private(this);
	d->composing = true;
	d->psi = psi;
	d->pa = 0;
	d->psi->dialogRegister(this);

	d->anim = 0;
	d->nextAnim = 0;
	d->nextAmount = 0;
	d->urlOnShow = false;

	setAccount(pa);

	d->whois = new QTimer;
	connect(d->whois, SIGNAL(timeout()), SLOT(doWhois()));

	init();

	d->cb_ident->setAccount(pa);

	d->pb_send->show();
	d->le_to->setText(expandAddresses(to, false));
	d->le_to->setCursorPosition(0);

	if(option.grabUrls) {
		// url in clipboard?
		QClipboard *cb = QApplication::clipboard();
		QCString subtype("plain");
		bool oldmode = cb->selectionModeEnabled();
		cb->setSelectionMode(false);
		QString text = cb->text(subtype);
		if(!text && cb->supportsSelection()) {
			cb->setSelectionMode(true);
			text = cb->text(subtype);
		}
		if(text) {
			if(text.left(7) == "http://") {
				d->attachView->urlAdd(text, "");
				cb->setSelectionMode(false);
				cb->clear();
				cb->setSelectionMode(true);
				cb->clear();
			}
		}
		cb->setSelectionMode(oldmode);
	}

	updateIdentity(pa);

	X11WM_CLASS("event");

	if(d->le_to->text().isEmpty())
		d->le_to->setFocus();
	else
		d->mle->setFocus();

	if(d->tb_pgp) {
		UserListItem *u = d->pa->findFirstRelavent(d->jid);
		if(u && u->isSecure(d->jid.resource()))
			d->tb_pgp->setOn(true);
	}
}

EventDlg::EventDlg(const Jid &j, PsiAccount *pa, bool unique)
:AdvancedWidget<QWidget>(0, 0, WDestructiveClose | WGroupLeader)
{
	d = new Private(this);
	d->composing = false;
	d->psi = pa->psi();
	d->pa = pa;
	d->jid = j;
	d->realJid = j;

	if(unique)
		d->pa->dialogRegister(this, j);
	else
		d->pa->dialogRegister(this);

	d->anim = 0;
	d->nextAnim = 0;
	d->nextAmount = 0;
	d->urlOnShow = false;

	init();

	d->le_from->setText(expandAddresses(d->jid.full(), false));
	d->le_from->setCursorPosition(0);

	doWhois();

	d->pb_next->show();

	d->pb_close->setFocus();

	X11WM_CLASS("event");

	setTime(QDateTime::currentDateTime(), true);
}

EventDlg::~EventDlg()
{
	if(d->composing) {
		delete d->whois;
		d->psi->dialogUnregister(this);
	}
	else {
		d->pa->dialogUnregister(this);
	}
	delete d;
}

void EventDlg::init()
{
	QVBoxLayout *vb1 = new QVBoxLayout(this, 4);
	QLabel *l;

	// first row
	QHBoxLayout *hb1 = new QHBoxLayout(vb1);
	l = new QLabel(tr("Identity:"), this);
	hb1->addWidget(l);

	d->enc = false;
	d->transid = -1;

	if(d->composing) {
		d->cb_ident = d->psi->accountsComboBox(this);
		connect(d->cb_ident, SIGNAL(activated(PsiAccount *)), SLOT(updateIdentity(PsiAccount *)));
		hb1->addWidget(d->cb_ident);
	}
	else {
		d->lb_ident = d->pa->accountLabel(this);
		d->lb_ident->setSizePolicy(QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ));
		hb1->addWidget(d->lb_ident);
	}

	if(d->composing) {
		l = new QLabel(tr("Type:"), this);
		hb1->addWidget(l);
		d->cb_type = new QComboBox(this);
		d->cb_type->insertItem(tr("Normal"));
		d->cb_type->insertItem(tr("Chat"));
		hb1->addWidget(d->cb_type);
	}
	else {
		l = new QLabel(tr("Time:"), this);
		hb1->addWidget(l);
		d->lb_time = new QLabel(this);
		d->lb_time->setFrameStyle( QFrame::Panel | QFrame::Sunken );
		hb1->addWidget(d->lb_time);
	}

	// second row
	QHBoxLayout *hb2 = new QHBoxLayout(vb1);

	d->lb_status = new IconLabel(this);
	d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));

	if(d->composing) {
		l = new QLabel(tr("To:"), this);
		hb2->addWidget(l);
		hb2->addWidget(d->lb_status);
		d->le_to = new ELineEdit(this);
		connect(d->le_to, SIGNAL(textChanged(const QString &)), SLOT(to_textChanged(const QString &)));
		connect(d->le_to, SIGNAL(changeResource(const QString &)), SLOT(to_changeResource(const QString &)));
		connect(d->le_to, SIGNAL(tryComplete()), SLOT(to_tryComplete()));
		hb2->addWidget(d->le_to);
	}
	else {
		l = new QLabel(tr("From:"), this);
		hb2->addWidget(l);
		hb2->addWidget(d->lb_status);
		d->le_from = new QLineEdit(this);
		d->le_from->setReadOnly(true);
		hb2->addWidget(d->le_from);
	}

	// icon select
	//connect(d->psi->iconSelectPopup(), SIGNAL(iconSelected(const Icon *)), d, SLOT(addEmoticon(const Icon *)));
	connect(d->psi->iconSelectPopup(), SIGNAL(textSelected(QString)), d, SLOT(addEmoticon(QString)));

	d->tb_icon = new IconToolButton(this);
	d->tb_icon->setIcon(IconsetFactory::iconPtr("psi/smile"));
	d->tb_icon->setPopup(d->psi->iconSelectPopup());
	d->tb_icon->setPopupDelay (1);
	QToolTip::add(d->tb_icon, tr("Select icon"));
	if ( !d->composing )
		d->tb_icon->setEnabled(false);

	// message length counter
	d->le_subj = new QLineEdit(this);
	d->lcd_count = new QLCDNumber(this);
	QToolTip::add(d->lcd_count, tr("Message length"));
	d->lcd_count->setFixedWidth(50);

	if(d->composing) {
		d->tb_pgp = new IconToolButton(this);
		d->tb_pgp->setToggleButton(true);
		QToolTip::add(d->tb_pgp, tr("Toggle encryption"));
		d->lb_pgp = 0;
	}
	else {
		d->lb_pgp = new IconLabel(this);
		d->lb_pgp->setIcon(IconsetFactory::iconPtr("psi/cryptoNo"));
		d->tb_pgp = 0;
	}

	d->tb_url = new IconToolButton(this);
	connect(d->tb_url, SIGNAL(clicked()), SLOT(addUrl()));
	QToolTip::add(d->tb_url, tr("Add URL"));
	d->tb_info = new IconToolButton(this);
	connect(d->tb_info, SIGNAL(clicked()), SLOT(doInfo()));
	QToolTip::add(d->tb_info, tr("User info"));
	d->tb_history = new IconToolButton(this);
	connect(d->tb_history, SIGNAL(clicked()), SLOT(doHistory()));
	QToolTip::add(d->tb_history, tr("Message history"));

	QHBoxLayout *hb3 = new QHBoxLayout(vb1);

//	if(d->composing /* && config->showsubject */) {
	if(option.showSubjects) {
		// third row
		l = new QLabel(tr("Subject:"), this);
		hb3->addWidget(l);
		hb3->addWidget(d->le_subj);
		hb3->addWidget(d->lcd_count);
		hb3->addWidget(d->tb_icon);
		hb3->addWidget(d->tb_url);
		hb3->addWidget(d->tb_info);
		hb3->addWidget(d->tb_history);

		if(!d->composing) {
			d->le_subj->setReadOnly(true);
			d->tb_url->setEnabled(false);
			hb3->addWidget(d->lb_pgp);
		} else
			hb3->addWidget(d->tb_pgp);

	} else {
		d->le_subj->hide();
		hb2->addWidget(d->lcd_count);
		hb2->addWidget(d->tb_icon);
		hb2->addWidget(d->tb_url);
		hb2->addWidget(d->tb_info);
		hb2->addWidget(d->tb_history);
		if(d->composing)
			hb2->addWidget(d->tb_pgp);
		else
			hb2->addWidget(d->lb_pgp);
	}

	// text area
	d->mle = new ChatView(this);
	d->mle->setReadOnly(false);
	d->mle->setUndoRedoEnabled(true);
	d->mle->setTextFormat(PlainText);
	d->mle->setMinimumHeight(50);
	vb1->addWidget(d->mle);

	connect(d->mle, SIGNAL(textChanged()), d, SLOT(updateCounter()));

	d->mle->setTextFormat(QTextEdit::PlainText);
	if(!d->composing) {
		d->mle->setReadOnly(true);
		d->mle->setUndoRedoEnabled(false);
	}

	// attachment view
	d->attachView = new AttachView(this);
	d->attachView->setFixedHeight(80);
	d->attachView->hide();
	connect(d->attachView, SIGNAL(childCountChanged()), SLOT(showHideAttachView()));
	connect(d->attachView, SIGNAL(actionGCJoin(const QString &)), SLOT(actionGCJoin(const QString &)));
	vb1->addWidget(d->attachView);

	if(!d->composing)
		d->attachView->setReadOnly(true);

	// bottom row
	QHBoxLayout *hb4 = new QHBoxLayout(vb1);
	d->pb_close = new IconButton(this);
	d->pb_close->setText(tr("&Close"));
	connect(d->pb_close, SIGNAL(clicked()), SLOT(close()));
	d->pb_close->setMinimumWidth(80);
	hb4->addWidget(d->pb_close);
	hb4->addStretch(1);
	d->pb_next = new IconButton(this);
	connect(d->pb_next, SIGNAL(clicked()), SLOT(doReadNext()));
	d->pb_next->setText(tr("&Next"));
	d->pb_next->hide();
	d->pb_next->setMinimumWidth(96);
	d->pb_next->setEnabled(false);
	hb4->addWidget(d->pb_next);
	d->pb_quote = new IconButton(this);
	d->pb_quote->setText(tr("&Quote"));
	connect(d->pb_quote, SIGNAL(clicked()), SLOT(doQuote()));
	d->pb_quote->hide();
	d->pb_quote->setMinimumWidth(96);
	hb4->addWidget(d->pb_quote);
	d->pb_deny = new IconButton(this);
	d->pb_deny->setText(tr("&Deny"));
	connect(d->pb_deny, SIGNAL(clicked()), SLOT(doDeny()));
	d->pb_deny->hide();
	d->pb_deny->setMinimumWidth(96);
	hb4->addWidget(d->pb_deny);
	d->pb_auth = new IconButton(this);
	d->pb_auth->setText(tr("&Add/Auth"));
	connect(d->pb_auth, SIGNAL(clicked()), SLOT(doAuth()));
	d->pb_auth->setIcon(IconsetFactory::iconPtr("psi/addContact"));
	d->pb_auth->hide();
	d->pb_auth->setMinimumWidth(96);
	hb4->addWidget(d->pb_auth);
	d->pb_send = new IconButton(this);
	d->pb_send->setText(tr("&Send"));
	connect(d->pb_send, SIGNAL(clicked()), SLOT(doSend()));
	d->pb_send->hide();
	d->pb_send->setMinimumWidth(96);
	hb4->addWidget(d->pb_send);
	d->pb_chat = new IconButton(this);
	d->pb_chat->setText(tr("&Chat"));
	connect(d->pb_chat, SIGNAL(clicked()), SLOT(doChat()));
	d->pb_chat->hide();
	d->pb_chat->setMinimumWidth(96);
	hb4->addWidget(d->pb_chat);
	d->pb_reply = new IconButton(this);
	d->pb_reply->setText(tr("&Reply"));
	connect(d->pb_reply, SIGNAL(clicked()), SLOT(doReply()));
	d->pb_reply->hide();
	d->pb_reply->setMinimumWidth(96);
	hb4->addWidget(d->pb_reply);

	setTabOrder(d->mle, d->pb_send);

	updatePGP();
	connect(d->pa, SIGNAL(pgpKeyChanged()), SLOT(updatePGP()));
	connect(d->pa, SIGNAL(encryptedMessageSent(int, bool)), SLOT(encryptedMessageSent(int, bool)));

	resize(option.sizeEventDlg);
	optionsUpdate();
}

void EventDlg::setAccount(PsiAccount *pa)
{
	if(d->pa)
		disconnect(d->pa, SIGNAL(updatedActivity()), this, SLOT(accountUpdatedActivity()));

	d->pa = pa;
	connect(d->pa, SIGNAL(updatedActivity()), this, SLOT(accountUpdatedActivity()));
}

void EventDlg::updateIdentity(PsiAccount *pa)
{
	if(!pa) {
		close();
		return;
	}

	setAccount(pa);

	buildCompletionList();
	doWhois(true);
}

void EventDlg::accountUpdatedActivity()
{
	// TODO: act on account activity change
}

QString EventDlg::text() const
{
	return d->mle->text();
}

void EventDlg::setText(const QString &s)
{
	d->mle->setText(s);
	d->mle->moveCursor(QTextEdit::MoveEnd, false);
}

void EventDlg::setSubject(const QString &s)
{
	d->le_subj->setText(s);
}

void EventDlg::setThread(const QString &t)
{
	d->thread = t;
}

void EventDlg::setUrlOnShow()
{
	d->urlOnShow = true;
}

PsiAccount *EventDlg::psiAccount()
{
	return d->pa;
}

QStringList EventDlg::stringToList(const QString &s, bool enc) const
{
	QStringList list;

	int x1, x2;
	x1 = 0;
	x2 = 0;
	while(1) {
		// scan along for a comma
		bool found = false;
		for(int n = x1; n < (int)s.length(); ++n) {
			if(s.at(n) == ',') {
				found = true;
				x2 = n;
				break;
			}
		}
		if(!found)
			x2 = s.length();

		QString c = s.mid(x1, (x2-x1));
		QString j;
		if(enc)
			j = findJidInString(c);
		else
			j = c;
		if(j.isEmpty())
			j = c;

		j = j.stripWhiteSpace();
		//printf("j=[%s]\n", j.latin1());
		if(!j.isEmpty())
			list += j;

		if(!found)
			break;
		x1 = x2+1;
	}

	return list;
}

QString EventDlg::findJidInString(const QString &s) const
{
	int a = s.find('<');
	if(a != -1) {
		++a;
		int b = s.find('>', a);
		if(b != -1)
			return dec822jid(s.mid(a, b-a));
	}
	return "";
}

QString EventDlg::expandAddresses(const QString &in, bool enc) const
{
	//printf("in: [%s]\n", in.latin1());
	QString str;
	QStringList list = stringToList(in, enc);
	bool first = true;
	for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
		if(!first)
			str += ", ";
		first = false;

		Jid j(*it);
		QPtrList<UserListItem> ul = d->pa->findRelavent(j);
		if(ul.isEmpty()) {
			str += j.full();
			continue;
		}
		UserListItem *u = ul.first();

		Jid jid;
		if(j.resource().isEmpty())
			jid = u->jid().full();
		else
			jid = u->jid().userHost() + '/' + j.resource();

		QString name;
		if(!u->name().isEmpty())
			name += u->name() + QString(" <%1>").arg(enc822jid(jid.full()));
		else
			name = enc822jid(jid.full());
		str += name;
	}

	//printf("expanding: [%s]\n", str.latin1());
	return str;
}

void EventDlg::to_textChanged(const QString &)
{
	d->whois->start(250);
}

void EventDlg::to_changeResource(const QString &r)
{
	QString str = d->le_to->text();
	int start, len;
	if(!d->le_to->getSelection(&start, &len)) {
		//printf("bad selection\n");
		return;
	}
	//printf("selection: [%d,%d]\n", start, len);

	int p1, p2;
	QString s = findJid(str, start, &p1, &p2);
	QString j = findJidInString(s);
	if(j.isEmpty())
		j = s;
	Jid jid(j);
	if(!jid.isValid()) {
		//printf("invalid jid\n");
		return;
	}
	//printf("s=[%s], j=[%s], p: [%d,%d]\n", s.latin1(), j.latin1(), p1, p2);

	QString js = jidToString(jid, r);
	//printf("js=[%s]\n", js.latin1());
	/*str.remove(start, len);
	str.insert(start, js);
	d->le_to->deselect();
	d->le_to->setCursorPosition(0);
	d->le_to->setText(str);
	//d->le_to->setCursorPosition(start + js.length());*/
	d->le_to->insert(js);
}

void EventDlg::to_tryComplete()
{
	if(!option.jidComplete)
		return;

	QString str = d->le_to->text();
	int x = d->le_to->cursorPosition();

	int p1, p2;
	QString s = findJid(str, x, &p1, &p2);
	if(s.length() < 1 || x != p2)
		return;

	for(QStringList::ConstIterator it = d->completionList.begin(); it != d->completionList.end(); ++it) {
		QString name = *it;
		if(s.length() > name.length())
			continue;

		bool ok = true;
		int n;
		for(n = 0; n < (int)s.length(); ++n) {
			if(s.at(n).lower() != name.at(n).lower()) {
				ok = false;
				break;
			}
		}
		name = name.mid(n);
		if(ok) {
			d->le_to->insert(name);
			d->le_to->setCursorPosition(x);
			d->le_to->setSelection(x, name.length());
			break;
		}
	}
}

void EventDlg::buildCompletionList()
{
	d->completionList.clear();

	d->completionList += d->pa->jid().full();

	UserListIt it(*d->pa->userList());
	for(UserListItem *u; (u = it.current()); ++it) {
		QString j = u->jid().full();
		if(!u->name().isEmpty())
			d->completionList += u->name() + " <"+j+'>';
		d->completionList += j;
	}
}

QString EventDlg::jidToString(const Jid &jid, const QString &r) const
{
	QString name;

	QPtrList<UserListItem> ul = d->pa->findRelavent(jid);
	if(!ul.isEmpty()) {
		UserListItem *u = ul.first();

		QString j;
		if(r.isEmpty())
			j = u->jid().full();
		else
			j = u->jid().userHost() + '/' + r;

		if(!u->name().isEmpty())
			name = u->name() + QString(" <%1>").arg(enc822jid(j));
		else
			name = enc822jid(j);
	}
	else
		name = enc822jid(jid.full());

	return name;
}

void EventDlg::doWhois(bool force)
{
	QString str;
	if(d->composing) {
		str = d->le_to->text();
		if(str == d->lastWhois && !force)
			return;
	}
	else {
		str = d->le_from->text();
	}

	//printf("whois: [%s]\n", str.latin1());
	d->lastWhois = str;
	QStringList list = stringToList(str);
	bool found = false;
	if(list.count() == 1) {
		Jid j(list[0]);
		d->jid = j;
		QPtrList<UserListItem> ul = d->pa->findRelavent(j);
		if(!ul.isEmpty()) {
			d->tb_info->setEnabled(true);
			d->tb_history->setEnabled(true);
			found = true;
		}
		updateContact(d->jid);
	}
	if(!found) {
		d->jid = "";

		d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));
		d->tb_info->setEnabled(false);
		d->tb_history->setEnabled(false);
		setCaption(tr("Send Message"));
		QToolTip::remove(d->lb_status);
	}
}

UserResourceList EventDlg::getResources(const QString &s) const
{
	UserResourceList list;

	QString j = findJidInString(s);
	if(j.isEmpty())
		j = s;
	Jid jid(j);
	if(!jid.isValid())
		return list;

	QPtrList<UserListItem> ul = d->pa->findRelavent(jid);
	if(!ul.isEmpty()) {
		UserListItem *u = ul.first();
		if(u->isAvailable())
			return u->userResourceList();
	}

	return list;
}

void EventDlg::optionsUpdate()
{
	// update the font
	QFont f;
	f.fromString(option.font[fMessage]);
	d->mle->setFont(f);

	// update status icon
	doWhois(true);

	if ( option.showCounter && d->composing )
		d->lcd_count->show();
	else
		d->lcd_count->hide();

	if ( option.useEmoticons )
		d->tb_icon->show();
	else
		d->tb_icon->hide();

	// tool buttons: not required
	d->tb_url->setIcon(IconsetFactory::iconPtr("psi/www"));
	d->tb_info->setIcon(IconsetFactory::iconPtr("psi/vCard"));
	d->tb_history->setIcon(IconsetFactory::iconPtr("psi/history"));
	if(d->tb_pgp) {
		QIconSet i;
		i.setPixmap(IconsetFactory::icon("psi/cryptoNo"),  QIconSet::Automatic, QIconSet::Normal, QIconSet::Off);
		i.setPixmap(IconsetFactory::icon("psi/cryptoYes"), QIconSet::Automatic, QIconSet::Normal, QIconSet::On);
		d->tb_pgp->setIconSet(i);
	}
	if(d->lb_pgp)
		d->lb_pgp->setIcon(IconsetFactory::iconPtr(d->enc ? "psi/cryptoYes" : "psi/cryptoNo"));

	// update the readnext icon
	if(d->nextAmount > 0)
		d->pb_next->forceSetIcon(d->nextAnim);

	// update the widget icon
#ifndef Q_WS_MAC
	if(d->composing) {
		setIcon(IconsetFactory::icon("psi/sendMessage"));
	}
	else {
		if(d->anim)
			setIcon(d->anim->impix());
	}
#endif
}

QSize EventDlg::defaultSize()
{
	return QSize(420, 280);
}

void EventDlg::showEvent(QShowEvent *e)
{
	QWidget::showEvent(e);

	if(d->urlOnShow) {
		d->urlOnShow = false;
		QTimer::singleShot(1, this, SLOT(addUrl()));
	}
}

void EventDlg::resizeEvent(QResizeEvent *e)
{
	if(option.keepSizes)
		option.sizeEventDlg = e->size();
}

void EventDlg::keyPressEvent(QKeyEvent *e)
{
	if(e->key() == Key_Escape)
		close();
#ifdef Q_WS_MAC
	else if(e->key() == Key_W && e->state() & ControlButton)
		close();
#endif
	else if(d->composing && e->key() == Key_Return && ((e->state() & ControlButton) || (e->state() & AltButton)) )
		doSend();
	else if(e->key() == Key_H && (e->state() & ControlButton))
		doHistory();
	else if(e->key() == Key_I && (e->state() & ControlButton))
		doInfo();
	else
		e->ignore();
}

void EventDlg::closeEvent(QCloseEvent *e)
{
	// really lame way of checking if we are encrypting
	if(!d->mle->isEnabled())
		return;

	e->accept();
}

void EventDlg::doSend()
{
	if(!d->composing)
		return;

	if(!d->pb_send->isEnabled())
		return;

	if(!d->pa->checkConnected(this))
		return;

	if(d->mle->text().isEmpty() && d->attachView->childCount() == 0) {
		QMessageBox::information(this, tr("Warning"), tr("Please type in a message first."));
		return;
	}

	QStringList list = stringToList(d->le_to->text());
	if(list.isEmpty()) {
		QMessageBox::warning(this, tr("Warning"), tr("No recipients have been specified!"));
		return;
	}

	Message m;
	if(d->cb_type->currentItem() == 0)
		m.setType("");
	else
		m.setType("chat");

	m.setBody(d->mle->text());
	m.setSubject(d->le_subj->text());
	m.setUrlList(d->attachView->urlList());
	m.setTimeStamp(QDateTime::currentDateTime());
	m.setThread(d->thread);
	if(d->tb_pgp->isOn())
		m.setWasEncrypted(true);
	d->m = m;

	d->enc = false;
	if(d->tb_pgp->isOn()) {
		d->le_to->setEnabled(false);
		d->mle->setEnabled(false);
		d->enc = true;
		d->sendLeft = list;

		trySendEncryptedNext();
	}
	else {
		for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
			m.setTo(Jid(*it));
			d->pa->dj_sendMessage(m);
		}
		doneSend();
	}
}

void EventDlg::doneSend()
{
	close();
}

void EventDlg::doReadNext()
{
	aReadNext(d->realJid);
}

void EventDlg::doChat()
{
	QStringList list = stringToList(d->le_from->text());
	if(list.isEmpty())
		return;

	// FIXME: Maybe it's better without bare(), but then PsiAccount::actionChat
	// doesn't find the UserListItem to chat with :-(
	Jid jf(list[0]);
	Jid j(jf.bare());
	aChat(j);
}

void EventDlg::doReply()
{
	QStringList list = stringToList(d->le_from->text());
	if(list.isEmpty())
		return;
	Jid j(list[0]);
	aReply(j, "", d->le_subj->text(), d->thread);
}

void EventDlg::doQuote()
{
	QStringList list = stringToList(d->le_from->text());
	if(list.isEmpty())
		return;
	Jid j(list[0]);

	QString body;
	if(d->mle->hasSelectedText())
		body = rich2plain(d->mle->selectedText());
	else
		body = rich2plain(d->mle->text());

	aReply(j, body, d->le_subj->text(), d->thread);
}

void EventDlg::doDeny()
{
	if(!d->pa->checkConnected(this))
		return;

	QStringList list = stringToList(d->le_from->text());
	if(list.isEmpty())
		return;
	Jid j(list[0]);
	aDeny(j);
	close();
}

void EventDlg::doAuth()
{
	if(!d->pa->checkConnected(this))
		return;

	QStringList list = stringToList(d->le_from->text());
	if(list.isEmpty())
		return;
	Jid j(list[0]);
	aAuth(j);
	d->pb_auth->setEnabled(false);
	closeAfterReply();
}

void EventDlg::doHistory()
{
	d->pa->actionHistory(d->jid.withResource(""));
}

void EventDlg::doInfo()
{
	d->pa->actionInfo(d->jid);
}

void EventDlg::closeAfterReply()
{
	if(d->nextAmount == 0)
		close();
}

void EventDlg::addUrl()
{
	AddUrlDlg *w = new AddUrlDlg(this);
	int n = w->exec();
	if(n != QDialog::Accepted) {
		delete w;
		return;
	}

	QString url = w->le_url->text();
	QString desc = w->le_desc->text();
	delete w;

	d->attachView->urlAdd(url, desc);
}

void EventDlg::showHideAttachView()
{
	if(d->attachView->childCount() > 0) {
		if(d->attachView->isHidden())
			d->attachView->show();
	}
	else {
		if(!d->attachView->isHidden())
			d->attachView->hide();
	}
}

void EventDlg::updateContact(const Jid &jid)
{
	if(d->jid.compare(jid, false)) {
		QString rname = d->jid.resource();
		QPtrList<UserListItem> ul = d->pa->findRelavent(d->jid);
		UserListItem *u = 0;
		int status = -1;
		if(!ul.isEmpty()) {
			u = ul.first();
			if(rname.isEmpty()) {
				// use priority
				if(!u->isAvailable())
					status = STATUS_OFFLINE;
				else
					status = makeSTATUS((*u->userResourceList().priority()).status());
			}
			else {
				// use specific
				UserResourceList::ConstIterator rit = u->userResourceList().find(rname);
				if(rit != u->userResourceList().end())
					status = makeSTATUS((*rit).status());
				else
					status = STATUS_OFFLINE;
			}
		}

		if(status == -1 || !u)
			d->lb_status->setIcon(IconsetFactory::iconPtr("status/noauth"));
		else
			d->lb_status->setIcon(is->statusPtr(jid, status));

		if(u)
			QToolTip::add(d->lb_status, u->makeTip(true, false));
		else
			QToolTip::remove(d->lb_status);

		if(u)
			setCaption(jidnick(u->jid().full(), u->name()));
	}
}

void EventDlg::setTime(const QDateTime &t, bool late)
{
	QString str;
	//str.sprintf("<nobr>%02d/%02d %02d:%02d:%02d</nobr>", t.date().month(), t.date().day(), t.time().hour(), t.time().minute(), t.time().second());
	str = QString("<nobr>") + t.toString(LocalDate) + "</nobr>";
	if(late)
		str = QString("<font color=\"red\">") + str + "</font>";

	d->lb_time->setText(str);
}

void EventDlg::updateEvent(PsiEvent *e)
{
	Icon *oldanim = d->anim;
	d->anim = is->event2icon(e);

	if(d->anim != oldanim)
		setIcon(d->anim->impix());

	d->le_from->setText(expandAddresses(e->from().full(), false));
	d->le_from->setCursorPosition(0);
	QToolTip::add(d->le_from, e->from().full());
	setTime(e->timeStamp(), e->late());

	d->enc = false;

	if(e->type() == PsiEvent::Message) {
		MessageEvent *me = (MessageEvent *)e;
		const Message &m = me->message();

		d->enc = m.wasEncrypted();

		d->pb_auth->hide();
		d->pb_deny->hide();

		d->pb_chat->show();
		d->pb_reply->show();
		d->pb_quote->show();

		QString txt = plain2rich(m.body());

		// show subject line if the incoming message has one
		if(m.subject() != "" && !option.showSubjects)
                	txt = "<p><font color=\"red\"><b>" + tr("Subject:") + " " + m.subject() + "</b></font></p>" + txt;

		d->mle->moveCursor(QTextEdit::MoveHome, false);
		d->mle->setTextFormat(RichText);

		if(option.useEmoticons)
			txt = emoticonify(txt);

		d->mle->setText("<qt>" + linkify(txt) + "</qt>");

		d->le_subj->setText(m.subject());
		d->le_subj->setCursorPosition(0);

		d->thread = m.thread();

		d->attachView->clear();
		d->attachView->addUrlList(m.urlList());

		if(!m.invite().isEmpty())
			d->attachView->gcAdd(m.invite());
		showHideAttachView();
	}
	else if(e->type() == PsiEvent::Auth) {
		AuthEvent *ae = (AuthEvent *)e;
		QString type = ae->authType();

		d->le_subj->setText("");
		if(type == "subscribe") {
			QString body(tr("<big>[System Message]</big><br>This user wants to subscribe to your presence.  Click the button labelled \"Add/Auth\" to authorize the subscription.  This will also add the person to your contact list if it is not already there."));
			d->mle->moveCursor(QTextEdit::MoveHome, false);
			d->mle->setTextFormat(RichText);
			d->mle->setText("<qt>" + body + "</qt>");

			d->pb_chat->show();
			d->pb_reply->hide();
			d->pb_quote->hide();

			d->pb_auth->setEnabled(true);
			d->pb_auth->show();
			d->pb_deny->show();
		}
		else if(type == "subscribed") {
			QString body(tr("<big>[System Message]</big><br>You are now authorized."));
			d->mle->moveCursor(QTextEdit::MoveHome, false);
			d->mle->setTextFormat(RichText);
			d->mle->setText("<qt>" + body + "</qt>");

			d->pb_auth->hide();
			d->pb_deny->hide();
			d->pb_chat->show();
			d->pb_reply->show();
			d->pb_quote->show();
		}
		else if(type == "unsubscribed") {
			QString body(tr("<big>[System Message]</big><br>Your authorization has been removed!"));
			d->mle->moveCursor(QTextEdit::MoveHome, false);
			d->mle->setTextFormat(RichText);
			d->mle->setText("<qt>" + body + "</qt>");

			d->pb_auth->hide();
			d->pb_deny->hide();
			d->pb_chat->show();
			d->pb_reply->show();
			d->pb_quote->show();
		}
	}

	if(d->lb_pgp)
		d->lb_pgp->setIcon( IconsetFactory::iconPtr(d->enc ? "psi/cryptoYes" : "psi/cryptoNo") );

	if(!d->le_subj->text().isEmpty())
		QToolTip::add(d->le_subj, d->le_subj->text());
	else
		QToolTip::remove(d->le_subj);

	doWhois();
}

void EventDlg::updateReadNext(Icon *nextAnim, int nextAmount)
{
	int oldAmount = d->nextAmount;

	d->nextAnim = nextAnim;
	d->nextAmount = nextAmount;

	if(nextAmount == 0) {
		d->nextAnim = 0;
		d->pb_next->forceSetIcon(0);
		d->pb_next->setEnabled(false);
		d->pb_next->setText(tr("&Next"));

		if(d->pb_reply->isEnabled())
			d->pb_reply->setFocus();
		else if(d->pb_auth->isVisible())
			d->pb_auth->setFocus();
	}
	else {
		d->pb_next->setEnabled(true);
		QString str(tr("&Next"));
		str += QString(" - %1").arg(nextAmount);
		d->pb_next->setText(str);

		d->pb_next->forceSetIcon(d->nextAnim);

		if(d->nextAmount > oldAmount)
			d->pb_next->setFocus();
	}
}

void EventDlg::actionGCJoin(const QString &gc)
{
	Jid j(gc);
	d->pa->actionJoin(j.withResource(""));
}

void EventDlg::updatePGP()
{
	if(d->tb_pgp) {
		if(!d->pa->pgpKey().isEmpty()) {
			d->tb_pgp->setEnabled(true);
		}
		else {
			d->tb_pgp->setOn(false);
			d->tb_pgp->setEnabled(false);
		}
	}
}

void EventDlg::trySendEncryptedNext()
{
	if(d->sendLeft.isEmpty())
		return;
	Message m = d->m;
	m.setTo(Jid(d->sendLeft.first()));
	d->transid = d->pa->sendMessageEncrypted(m);
	if(d->transid == -1) {
		d->le_to->setEnabled(true);
		d->mle->setEnabled(true);
		d->mle->setFocus();
		return;
	}
}

void EventDlg::encryptedMessageSent(int x, bool b)
{
	if(d->transid == -1)
		return;
	if(d->transid != x)
		return;
	d->transid = -1;
	if(b) {
		// remove the item
		QStringList::Iterator it = d->sendLeft.begin();
		Jid j(*it);
		d->sendLeft.remove(it);

		//d->pa->toggleSecurity(j, d->enc);

		if(d->sendLeft.isEmpty()) {
			d->le_to->setEnabled(true);
			d->mle->setEnabled(true);
			doneSend();
		}
		else {
			trySendEncryptedNext();
			return;
		}
	}
	else
		QMessageBox::information(this, tr("Error"), tr("There was an error trying to send the message encrypted.\nCheck your OpenPGP application/settings."));

	d->le_to->setEnabled(true);
	d->mle->setEnabled(true);
	d->mle->setFocus();
}

#include "eventdlg.moc"
