/*
 * userlist.cpp - high-level roster
 * 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"userlist.h"

#include"im.h"
#include"openpgp.h"
#include"common.h"
#include"contactview.h"
#include"avatars.h"

using namespace XMPP;

static QString dot_truncate(const QString &in, int clip)
{
	if((int)in.length() <= clip)
		return in;
	QString s = in;
	s.truncate(clip);
	s += "...";
	return s;
}

//----------------------------------------------------------------------------
// UserResource
//----------------------------------------------------------------------------
UserResource::UserResource()
:Resource()
{
}

UserResource::UserResource(const Resource &r)
{
	setResource(r);
}

UserResource::~UserResource()
{
}

void UserResource::setResource(const Resource &r)
{
	setName(r.name());
	setStatus(r.status());
}

const QString & UserResource::versionString() const
{
	return v_ver;
}

const QString & UserResource::clientName() const
{
	return v_clientName;
}

const QString & UserResource::clientVersion() const
{
	return v_clientVersion;
}

const QString & UserResource::clientOS() const
{
	return v_clientOS;
}

void UserResource::setClient(const QString &name, const QString& version, const QString& os)
{
	v_clientName = name;
	v_clientVersion = version;
	v_clientOS = os;
	v_ver = v_clientName + " " + v_clientVersion;
	if ( !v_clientOS.isEmpty() )
		v_ver += " / " + v_clientOS;
}

const QString & UserResource::publicKeyID() const
{
	return v_keyID;
}

int UserResource::pgpVerifyStatus() const
{
	return v_pgpVerifyStatus;
}

QDateTime UserResource::sigTimestamp() const
{
	return sigts;
}

void UserResource::setPublicKeyID(const QString &s)
{
	v_keyID = s;
}

void UserResource::setPGPVerifyStatus(int x)
{
	v_pgpVerifyStatus = x;
}

void UserResource::setSigTimestamp(const QDateTime &ts)
{
	sigts = ts;
}

bool operator<(const UserResource &r1, const UserResource &r2)
{
	return r1.priority() > r2.priority();
}

bool operator<=(const UserResource &r1, const UserResource &r2)
{
	return r1.priority() >= r2.priority();
}

bool operator==(const UserResource &r1, const UserResource &r2)
{
	return r1.priority() == r2.priority();
}

bool operator>(const UserResource &r1, const UserResource &r2)
{
	return r1.priority() < r2.priority();
}

bool operator>=(const UserResource &r1, const UserResource &r2)
{
	return r1.priority() <= r2.priority();
}


//----------------------------------------------------------------------------
// UserResourceList
//----------------------------------------------------------------------------
UserResourceList::UserResourceList()
:QValueList<UserResource>()
{
}

UserResourceList::~UserResourceList()
{
}

UserResourceList::Iterator UserResourceList::find(const QString & _find)
{
	for(UserResourceList::Iterator it = begin(); it != end(); ++it) {
		if((*it).name() == _find)
			return it;
	}

	return end();
}

UserResourceList::Iterator UserResourceList::priority()
{
	UserResourceList::Iterator highest = end();

	for(UserResourceList::Iterator it = begin(); it != end(); ++it) {
		if(highest == end() || (*it).priority() > (*highest).priority())
			highest = it;
	}

	return highest;
}

UserResourceList::ConstIterator UserResourceList::find(const QString & _find) const
{
	for(UserResourceList::ConstIterator it = begin(); it != end(); ++it) {
		if((*it).name() == _find)
			return it;
	}

	return end();
}

UserResourceList::ConstIterator UserResourceList::priority() const
{
	UserResourceList::ConstIterator highest = end();

	for(UserResourceList::ConstIterator it = begin(); it != end(); ++it) {
		if(highest == end() || (*it).priority() > (*highest).priority())
			highest = it;
	}

	return highest;
}

void UserResourceList::sort()
{
	qHeapSort(*this);
}


//----------------------------------------------------------------------------
// UserListItem
//----------------------------------------------------------------------------
UserListItem::UserListItem(bool self)
{
	v_inList = false;
	v_self = self;
	v_private = false;
	lastmsgtype = -1;
	v_avatarFactory = 0;
}

UserListItem::~UserListItem()
{
}

bool UserListItem::inList() const
{
	return v_inList;
}

void UserListItem::setJid(const Jid &j)
{
	LiveRosterItem::setJid(j);

	int n = jid().full().find('@');
	if(n == -1)
		v_isTransport = true;
	else
		v_isTransport = false;
}

bool UserListItem::isTransport() const
{
	return v_isTransport;
}

bool UserListItem::isAvailable() const
{
	return !v_url.isEmpty();
}

bool UserListItem::isHidden() const
{
	return groups().contains(ContactView::tr("Hidden"));
}

bool UserListItem::isAway() const
{
	int status;
	if(!isAvailable())
		status = STATUS_OFFLINE;
	else
		status = makeSTATUS((*userResourceList().priority()).status());

	if(status == STATUS_AWAY || status == STATUS_XA || status == STATUS_DND)
		return true;
	else
		return false;
}

QDateTime UserListItem::lastAvailable() const
{
	return v_t;
}

int UserListItem::lastMessageType() const
{
	return lastmsgtype;
}

void UserListItem::setLastMessageType(const int mtype)
{
//	printf("setting message type to %i\n", mtype);
	lastmsgtype = mtype;
}

const QString & UserListItem::presenceError() const
{
	return v_perr;
}

bool UserListItem::isSelf() const
{
	return v_self;
}

void UserListItem::setInList(bool b)
{
	v_inList = b;
}

void UserListItem::setLastAvailable(const QDateTime &t)
{
	v_t = t;
}

void UserListItem::setPresenceError(const QString &e)
{
	v_perr = e;
}

UserResourceList & UserListItem::userResourceList()
{
	return v_url;
}

UserResourceList::Iterator UserListItem::priority()
{
	return v_url.priority();
}

const UserResourceList & UserListItem::userResourceList() const
{
	return v_url;
}

UserResourceList::ConstIterator UserListItem::priority() const
{
	return v_url.priority();
}

QString UserListItem::makeTip(bool trim, bool doLinkify) const
{
	return "<qt>" + makeBareTip(trim,doLinkify) + "</qt>";
}

QString UserListItem::makeBareTip(bool trim, bool doLinkify) const
{
	QString str;

#ifdef AVATARS
	str += "<table cellspacing=\"0\"><tr>";
	if (option.avatarsEnabled) {
		QString client;
		QString res;
		if (!userResourceList().isEmpty()) {
			client = (*userResourceList().priority()).clientName();
			res = (*userResourceList().priority()).name();
		}
		if (avatarFactory()) {
			QPixmap p = avatarFactory()->getAvatar(jid().withResource(res),client);
			if (!p.isNull()) {
				str += "<td align=\"center\">";
				QMimeSourceFactory::defaultFactory()->setPixmap("avatar.png",p);
				str += "<img src=\"avatar.png\">";
				str += "</td>";
			}
		}
	}

	str += "<td>";
#endif

	QString nick = jidnick(jid().full(), name());
	str += QString("<nobr>%1</nobr>").arg(expandEntities(nick));
	if(jid().full() != nick)
		str += QString("<br>[%1]").arg(expandEntities(jid().full()));

	// subscription
	if(!v_self)
		str += QString("<br><nobr>") + QObject::tr("Subscription") + ": " + subscription().toString() + "</nobr>";

	if(!v_keyID.isEmpty())
		str += QString("<br><nobr>") + QObject::tr("OpenPGP") + ": " + v_keyID.right(8) + "</nobr>";

	// resources
	if(!userResourceList().isEmpty()) {
		UserResourceList srl = userResourceList();
		srl.sort();

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

			int status = makeSTATUS(r.status());
			QString istr = "status/offline";
			if(status == STATUS_ONLINE)
				istr = "status/online";
			else if(status == STATUS_AWAY)
				istr = "status/away";
			else if(status == STATUS_XA)
				istr = "status/xa";
			else if(status == STATUS_DND)
				istr = "status/dnd";
			else if(status == STATUS_CHAT)
				istr = "status/chat";
			else if(status == STATUS_INVISIBLE)
				istr = "status/invisible"; //this shouldn't happen

			QString imgTag = "icon name"; // or 'img src' if appropriate QMimeSourceFactory is installed. but mblsha noticed that QMimeSourceFactory unloads sometimes
			QString secstr;
			if(isSecure(r.name()))
				secstr += QString(" <%1=\"psi/cryptoYes\">").arg(imgTag);
			str += QString("<br><nobr>") + QString("<%1=\"%1\"> ").arg(imgTag).arg(istr) + QString("<b>%1</b> ").arg(expandEntities(name)) + QString("(%1)").arg(r.priority()) + secstr + "</nobr>";

			if(!r.publicKeyID().isEmpty()) {
				int v = r.pgpVerifyStatus();
				if(v == OpenPGP::VerifyGood || v == OpenPGP::VerifyNoKey || v == OpenPGP::VerifyBad) {
					if(v == OpenPGP::VerifyGood) {
						QString d = r.sigTimestamp().toString(Qt::TextDate);
						str += QString("<br><nobr>") + QObject::tr("Signed") + " @ " + "<font color=\"#2A993B\">" + d + "</font>";
					}
					else if(v == OpenPGP::VerifyNoKey) {
						QString d = r.sigTimestamp().toString(Qt::TextDate);
						str += QString("<br><nobr>") + QObject::tr("Signed") + " @ " + d;
					}
					else if(v == OpenPGP::VerifyBad) {
						str += QString("<br><nobr>") + "<font color=\"#810000\">" + QObject::tr("Bad signature") + "</font>";
					}

					if(v_keyID != r.publicKeyID())
						str += QString(" [%1]").arg(r.publicKeyID().right(8));
					str += "</nobr>";
				}
			}

			// last status
			if(r.status().timeStamp().isValid()) {
				QString d = r.status().timeStamp().toString(Qt::TextDate);
				str += QString("<br><nobr>") + QObject::tr("Last Status") + " @ " + d + "</nobr>";
			}

			// gabber music
			if(!r.status().songTitle().isEmpty()) {
				QString s = r.status().songTitle();
				if(trim)
					s = dot_truncate(s, 80);
				s = expandEntities(s);
				str += QString("<br><nobr>") + QObject::tr("Listening to") + QString(": %1").arg(s) + "</nobr>";
			}

			// client
			if(!r.versionString().isEmpty()) {
				QString ver = r.versionString();
				if(trim)
					ver = dot_truncate(ver, 80);
				ver = expandEntities(ver);
				str += QString("<br><nobr>") + QObject::tr("Using") + QString(": %1").arg(ver) + "</nobr>";
			}

			// status message
			QString s = r.status().status();
			if(!s.isEmpty()) {
				QString head = QObject::tr("Status Message");
				if(trim)
					s = plain2rich(clipStatus(s, 200, 12));
				else
					s = plain2rich(s);
				if ( doLinkify )
					s = linkify(s);
				if( option.useEmoticons && !doLinkify )
					s = emoticonify(s);
				str += QString("<br><nobr><u>%1</u></nobr><br>%2").arg(head).arg(s);
			}
		}
	}
	else {
		// last available
		if(!lastAvailable().isNull()) {
			QString d = lastAvailable().toString(Qt::TextDate);
			str += QString("<br><nobr>") + QObject::tr("Last Available") + " @ " + d + "</nobr>";
		}

		// presence error
		if(!v_perr.isEmpty()) {
			str += QString("<br><nobr>") + QObject::tr("Presence Error") + QString(": %1").arg(expandEntities(v_perr)) + "</nobr>";
		}

		// status message
		QString s = lastUnavailableStatus().status();
		if(!s.isEmpty()) {
			QString head = QObject::tr("Last Status Message");
			if(trim)
				s = plain2rich(clipStatus(s, 200, 12));
			else {
				s = plain2rich(clipStatus(s, 200, 12));
				if ( doLinkify )
					s = linkify(s);
			}
			str += QString("<br><nobr><u>%1</u></nobr><br>%2").arg(head).arg(s);
		}
	}

#ifdef AVATARS
	str += "</td>";
	str += "</tr></table>";
#endif

	return str;
}

QString UserListItem::makeDesc() const
{
	return makeTip(false);
}

bool UserListItem::isPrivate() const
{
	return v_private;
}

void UserListItem::setPrivate(bool b)
{
	v_private = b;
}

bool UserListItem::isSecure(const QString &rname) const
{
	for(QStringList::ConstIterator it = secList.begin(); it != secList.end(); ++it) {
		if(*it == rname)
			return true;
	}
	return false;
}

void UserListItem::setSecure(const QString &rname, bool b)
{
	for(QStringList::Iterator it = secList.begin(); it != secList.end(); ++it) {
		if(*it == rname) {
			if(!b)
				secList.remove(it);
			return;
		}
	}
	if(b)
		secList.append(rname);
}

const QString & UserListItem::publicKeyID() const
{
	return v_keyID;
}

void UserListItem::setPublicKeyID(const QString &k)
{
	v_keyID = k;
}

AvatarFactory* UserListItem::avatarFactory() const
{
	return v_avatarFactory;
}

void UserListItem::setAvatarFactory(AvatarFactory* av)
{
	v_avatarFactory = av;
}



//----------------------------------------------------------------------------
// UserList
//----------------------------------------------------------------------------
UserList::UserList()
{
}

UserList::~UserList()
{
}

UserListItem *UserList::find(const XMPP::Jid &j)
{
	UserListIt it(*this);
	for(UserListItem *i; (i = it.current()); ++it) {
		if(i->jid().compare(j))
			return i;
	}
	return 0;
}

