#include <qpixmap.h>
#include <qbuffer.h>
#include <qcstring.h>
#include <qdatetime.h>
#include <qsize.h>
#include <qfile.h>

#include "xmpp.h"
#include "avatars.h"
#include "common.h"
#include "iconset.h"
#include "psiaccount.h"
#include "profiles.h"
#include "vcardfactory.h"

#define MAX_AVATAR_SIZE 64

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

//------------------------------------------------------------------------------
//  Avatar: Base class for avatars.
//------------------------------------------------------------------------------

Avatar::Avatar()
{
}


Avatar::~Avatar()
{
}

int Avatar::maxSize()
{
	return (option.avatarsSize > MAX_AVATAR_SIZE ? MAX_AVATAR_SIZE : option.avatarsSize);
}

void Avatar::setImage(const QImage& i)
{

	if (i.width() > maxSize() || i.height() > option.avatarsSize)
		pixmap_.convertFromImage(i.smoothScale(maxSize(),maxSize(),QImage::ScaleMin));
	else
		pixmap_.convertFromImage(i);
}

void Avatar::setImage(const QByteArray& ba)
{
	setImage(QImage(ba));
}

void Avatar::setImage(const QPixmap& p)
{
	if (p.width() > maxSize() || p.height() > maxSize())
		setImage(p.convertToImage());
	else
		pixmap_ = p;
}

void Avatar::resetImage()
{
	pixmap_ = QPixmap();
}


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

//------------------------------------------------------------------------------
// VCardAvatar: Avatars coming from VCards of contacts.
//------------------------------------------------------------------------------

class VCardAvatar : public QObject, public Avatar
{
	Q_OBJECT

public:
	VCardAvatar(AvatarFactory* factory, const Jid& jid);
	QPixmap getPixmap();

signals:
	void avatarChanged(const Jid&);

private:
	Jid jid_;
	AvatarFactory* factory_;
	const VCard* lastVCard_; // Never dereference this, might point to dead object
};


VCardAvatar::VCardAvatar(AvatarFactory* factory, const Jid& jid) :
	jid_(jid), factory_(factory)
{
	const VCard* vcard = VCardFactory::vcard(jid_);
	if (vcard && vcard->photo()) {
		setImage(vcard->photo());
	}
	lastVCard_ = vcard;
}


QPixmap VCardAvatar::getPixmap()
{
	// Try to find an avatar if the current one is empty
	// FIXME: The pixmap shouldn't be updated by active polling, but by
	// some signal from the VCard factory when the vcard is updated.
	const VCard* vcard = VCardFactory::vcard(jid_);
	if  (lastVCard_ != vcard) {
		if (vcard && vcard->photo()) {
			setImage(vcard->photo());
		}
		else
			resetImage();
		lastVCard_ = vcard;
		emit avatarChanged(jid_);
	}

	return pixmap();
}


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

//------------------------------------------------------------------------------
// FileAvatar: Avatars coming from local files.
//------------------------------------------------------------------------------

class FileAvatar : public Avatar
{
public:
	FileAvatar(const Jid& jid);
	void import(const QString& file);
	void removeFromDisk();
	bool exists();
	QPixmap getPixmap();
	const Jid& getJid() const
		{ return jid_; }

protected:
	bool isDirty() const;
	QString getFileName() const;
	void refresh();
	QDateTime lastModified() const
		{ return lastModified_; }

private:
	QDateTime lastModified_;
	Jid jid_;
};


FileAvatar::FileAvatar(const Jid& jid) : jid_(jid)
{
}

void FileAvatar::import(const QString& file)
{
	if (QFileInfo(file).exists()) {
		// Check if the manual dir exists
		if (!QDir(AvatarFactory::getAvatarsDir()).exists())
			QDir("").mkdir(AvatarFactory::getAvatarsDir());
		if (!QDir(AvatarFactory::getManualDir()).exists())
			QDir("").mkdir(AvatarFactory::getManualDir());

		QFile source_file(file);
		QFile target_file(getFileName());
		if (source_file.open(IO_ReadOnly) && target_file.open(IO_WriteOnly)) {
			// Copy all the data
			char data[4096];
			while (!source_file.atEnd()) {
				Q_LONG read = source_file.readBlock(data,4096);
				target_file.writeBlock(data,read);
			}
		}
	}
}

void FileAvatar::removeFromDisk()
{
	QFile f(getFileName());
	f.remove();
}

bool FileAvatar::exists()
{
	return QFileInfo(getFileName()).exists();
}

QPixmap FileAvatar::getPixmap()
{
	refresh();
	return pixmap();
}

void FileAvatar::refresh()
{
	if (isDirty()) {
		if (QFileInfo(getFileName()).exists()) {
			QImage img(getFileName());
			setImage(QImage(getFileName()));
		}
		else
			resetImage();
	}
}


QString FileAvatar::getFileName() const
{
	QString f = getJid().bare();
	f.replace('@',"_at_");
	return QDir(AvatarFactory::getManualDir()).filePath(f);
}


bool FileAvatar::isDirty() const
{
	return (pixmap().isNull()
			|| !QFileInfo(getFileName()).exists()
			|| QFileInfo(getFileName()).lastModified() > lastModified());
}


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

//------------------------------------------------------------------------------
// ClientAvatar: Avatar representing the logo of a specific client.
//------------------------------------------------------------------------------

class ClientAvatar : public Avatar
{
public:
	ClientAvatar(const QString& name);

protected:
	static ClientAvatar getClientAvatar(const QString& client);
	static QMap<QString,ClientAvatar*> client_avatars;
};


ClientAvatar::ClientAvatar(const QString& name)
{
	//printf("%s\n",name.latin1());
	QPixmap p;
	if (name == "Psi")
		p = IconsetFactory::icon("psi/psiMac").pixmap();
	else if (name == "gaim")
		p = IconsetFactory::icon("client/gaim").pixmap();
	else if (name == "Gabber")
		p = IconsetFactory::icon("client/gabber").pixmap();
	else if (name == "Gossip")
		p = IconsetFactory::icon("client/gossip").pixmap();
	else if (name == "Exodus")
		p = IconsetFactory::icon("client/exodus").pixmap();
	else if (name == "Pandion")
		p = IconsetFactory::icon("client/pandion").pixmap();
	else if (name == "Nitro")
		p = IconsetFactory::icon("client/nitro").pixmap();
	else if (name == "Kopete")
		p = IconsetFactory::icon("client/kopete").pixmap();
	else if (name == "Tkabber")
		p = IconsetFactory::icon("client/tkabber").pixmap();
	else if (name == "MSN Transport")
		p = IconsetFactory::icon("client/msn").pixmap();
	else if (name == "AIM Transport")
		p = IconsetFactory::icon("client/aim").pixmap();

	// Clients with an ugly version string come last
	else if (name.contains("Gadu-Gadu"))
		p = IconsetFactory::icon("client/gadu").pixmap();
	else if (name.contains("Miranda"))
		p = IconsetFactory::icon("client/miranda").pixmap();
	else if (name.contains("JAJC"))
		p = IconsetFactory::icon("client/jajc").pixmap();
	else if (name.contains("Trillian"))
		p = IconsetFactory::icon("client/trillian").pixmap();
	else if (name.contains("Yahoo"))
		p = IconsetFactory::icon("client/yahoo").pixmap();
	else if (name.contains("JIT"))
		p = IconsetFactory::icon("client/icq").pixmap();
	//else
	//	p = IconsetFactory::icon("client/unknown").pixmap();

	setImage(p);
}


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

//------------------------------------------------------------------------------
// Avatar factory
//------------------------------------------------------------------------------

AvatarFactory::AvatarFactory(PsiAccount* pa) : pa_(pa)
{
	VCardFactory::registerVCardChanged(this,SLOT(updateAvatar(const Jid&)));
}


QPixmap AvatarFactory::getAvatar(const Jid& jid, const QString& resource)
{
	// Compute the avatar of the user
	Avatar* av = retrieveAvatar(jid,resource);

	// If the avatar changed since the previous request, notify everybody of this
	if (av != active_avatars_[jid.full()]) {
		active_avatars_[jid.full()] = av;
		active_avatars_[jid.bare()] = av;
		emit avatarChanged(jid);
	}

	return (av ? av->getPixmap() : QPixmap());
}

Avatar* AvatarFactory::retrieveAvatar(const Jid& jid, const QString& client)
{
	// printf("Retrieving avatar of %s\n", jid.full().latin1());

	// Try finding a file avatar.
	//printf("File avatar\n");
	if (!file_avatars_.contains(jid.bare())) {
		//printf("File avatar not yet loaded\n");
		file_avatars_[jid.bare()] = new FileAvatar(jid);
	}
	//printf("Trying file avatar\n");
	if (!file_avatars_[jid.bare()]->isEmpty())
		return file_avatars_[jid.bare()];

	// Try finding an avatar in the VCard
	//printf("VCard avatar\n");
	if (!vcard_avatars_.contains(jid.bare())) {
		//printf("VCard avatar not yet loaded\n");
		vcard_avatars_[jid.bare()] = new VCardAvatar(this, jid);
		connect(vcard_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&)));
	}
	//printf("Trying VCard avatar\n");
	if (!vcard_avatars_[jid.bare()]->isEmpty()) {
		return vcard_avatars_[jid.bare()];
	}

	// Return the client avatar
	if (client.isEmpty()) {
		return 0;
	}
	else {
		if (!client_avatars_.contains(client)) {
			// printf("Client avatar not yet loaded\n");
			client_avatars_[client] = new ClientAvatar(client);
		}
		return client_avatars_[client];
	}
}

QPixmap AvatarFactory::getSelfAvatar()
{
	return getAvatar(account()->jid(),"");
}

void AvatarFactory::updateAvatar(const Jid& j)
{
	// FIXME: Maybe we should really look up the client version
	getAvatar(j,"");

	// FIXME: This signal might be emitted twice (first time from getAvatar()).
	emit avatarChanged(j);
}

void AvatarFactory::importManualAvatar(const Jid& j, const QString& fileName)
{
	FileAvatar(j).import(fileName);
	emit avatarChanged(j);
}

void AvatarFactory::removeManualAvatar(const Jid& j)
{
	FileAvatar(j).removeFromDisk();
	// TODO: Remove from caches. Maybe create a clearManualAvatar() which
	// removes the file but doesn't remove the avatar from caches (since it'll
	// be created again whenever the FileAvatar is requested)
	emit avatarChanged(j);
}

bool AvatarFactory::hasManualAvatar(const Jid& j)
{
	return FileAvatar(j).exists();
}

QString AvatarFactory::getAvatarsDir()
{
	QDir avatars(pathToProfile(activeProfile) + "/avatars");
	if (!avatars.exists()) {
		QDir profile(pathToProfile(activeProfile));
		profile.mkdir("avatars");
	}

	return avatars.path();
}

QString AvatarFactory::getManualDir()
{
	return getAvatarsDir() + "/manual";
}

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

#include "avatars.moc"
