/*
    kopetecontact.cpp - Kopete Contact

    Copyright (c) 2002-2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
    Copyright (c) 2002-2003 by Martijn Klingens       <klingens@kde.org>
    Copyright (c) 2002-2004 by Olivier Goffart        <ogoffart @tiscalinet.be>

    Kopete    (c) 2002-2004 by the Kopete developers  <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This library is free software; you can redistribute it and/or         *
    * modify it under the terms of the GNU Lesser General Public            *
    * License as published by the Free Software Foundation; either          *
    * version 2 of the License, or (at your option) any later version.      *
    *                                                                       *
    *************************************************************************
*/

#include "kopetecontact.h"

#include <tqapplication.h>

#include <kdebug.h>

#include <tdeversion.h>
#include <kinputdialog.h>

#include <kabcpersistence.h>
#include <kdialogbase.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <kmessagebox.h>
#include <klistviewsearchline.h>

#include "kopetecontactlist.h"
#include "kopeteglobal.h"
#include "kopeteuiglobal.h"
#include "kopeteprotocol.h"
#include "kopeteaccount.h"
#include "kopetestdaction.h"
#include "kopetechatsession.h"
#include "kopeteview.h"
#include "kopetemetacontact.h"
#include "kopeteprefs.h"
#include "metacontactselectorwidget.h"
#include "kopeteemoticons.h"

//For the moving to another metacontact dialog
#include <tqlabel.h>
#include <tqimage.h>
#include <tqmime.h>
#include <tqvbox.h>
#include <klistview.h>
#include <tqcheckbox.h>
#include <tqwhatsthis.h>
#include <tqstylesheet.h>

namespace Kopete {

struct Contact::Private
{
public:
	bool fileCapable;

	OnlineStatus onlineStatus;
	Account *account;

	MetaContact *metaContact;

	TQString contactId;
	TQString icon;

	TQTime idleTimer;
	unsigned long int idleTime;

	Kopete::ContactProperty::Map properties;

};

Contact::Contact( Account *account, const TQString &contactId,
	MetaContact *parent, const TQString &icon )
	: TQObject( parent )
{
	d = new Private;

	//kdDebug( 14010 ) << k_funcinfo << "Creating contact with id " << contactId << endl;

	d->contactId = contactId;
	d->metaContact = parent;
	d->fileCapable = false;
	d->account = account;
	d->idleTime = 0;
	d->icon = icon;

	// If can happend that a MetaContact may be used without a account
	// (ex: for unit tests or chat window style preview)
	if ( account )
	{
		account->registerContact( this );
		connect( account, TQT_SIGNAL( isConnectedChanged() ), TQT_SLOT( slotAccountIsConnectedChanged() ) );
	}

	// Need to check this because myself() may have no parent
	// Maybe too the metaContact doesn't have a valid protocol() 
	// (ex: for unit tests or chat window style preview)
	if( parent && protocol() )
	{
		connect( parent, TQT_SIGNAL( aboutToSave( Kopete::MetaContact * ) ),
			protocol(), TQT_SLOT( slotMetaContactAboutToSave( Kopete::MetaContact * ) ) );

		parent->addContact( this );
	}

	
}

Contact::~Contact()
{
	//kdDebug(14010) << k_funcinfo << endl;
	emit( contactDestroyed( this ) );
	delete d;
}



OnlineStatus Contact::onlineStatus() const
{
	if ( this == account()->myself() || account()->isConnected() )
		return d->onlineStatus;
	else
		return protocol()->accountOfflineStatus();
}

void Contact::setOnlineStatus( const OnlineStatus &status )
{
	if( status == d->onlineStatus )
		return;

	OnlineStatus oldStatus = d->onlineStatus;
	d->onlineStatus = status;

	Kopete::Global::Properties *globalProps = Kopete::Global::Properties::self();

	// Contact changed from Offline to another status
	if( oldStatus.status() == OnlineStatus::Offline &&
		status.status() != OnlineStatus::Offline )
	{
		setProperty( globalProps->onlineSince(), TQDateTime::currentDateTime() );
		/*kdDebug(14010) << k_funcinfo << "REMOVING lastSeen property for " <<
			d->displayName << endl;*/
		removeProperty( globalProps->lastSeen() );
	}
	else if( oldStatus.status() != OnlineStatus::Offline &&
		oldStatus.status() != OnlineStatus::Unknown &&
		status.status() == OnlineStatus::Offline ) // Contact went back offline
	{
		removeProperty( globalProps->onlineSince() );
		/*kdDebug(14010) << k_funcinfo << "SETTING lastSeen property for " <<
			d->displayName << endl;*/
		setProperty( globalProps->lastSeen(), TQDateTime::currentDateTime() );
	}

	if ( this == account()->myself() || account()->isConnected() )
		emit onlineStatusChanged( this, status, oldStatus );
}

void Contact::slotAccountIsConnectedChanged()
{
	if ( this == account()->myself() )
		return;

	if ( account()->isConnected() )
		emit onlineStatusChanged( this, d->onlineStatus, protocol()->accountOfflineStatus() );
	else
		emit onlineStatusChanged( this, protocol()->accountOfflineStatus(), d->onlineStatus );
}


void Contact::sendFile( const KURL &, const TQString &, uint )
{
	kdWarning( 14010 ) << k_funcinfo << "Plugin "
		<< protocol()->pluginId() << " has enabled file sending, "
		<< "but didn't implement it!" << endl;
}

void Contact::slotAddContact()
{
	if( metaContact() )
	{
		metaContact()->setTemporary( false );
		ContactList::self()->addMetaContact( metaContact() );
	}
}

KPopupMenu* Contact::popupMenu( ChatSession *manager )
{
	// Build the menu
	KPopupMenu *menu = new KPopupMenu();

	// insert title
	TQString titleText;
	TQString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
	if( nick.isEmpty() )
		titleText = TQString::fromLatin1( "%1 (%2)" ).arg( contactId(), onlineStatus().description() );
	else
		titleText = TQString::fromLatin1( "%1 <%2> (%3)" ).arg( nick, contactId(), onlineStatus().description() );
	menu->insertTitle( titleText );

	if( metaContact() && metaContact()->isTemporary() && contactId() != account()->myself()->contactId() )
	{
		KAction *actionAddContact = new KAction( i18n( "&Add to Your Contact List" ), TQString::fromLatin1( "add_user" ),
		                                         0, TQT_TQOBJECT(this), TQT_SLOT( slotAddContact() ), TQT_TQOBJECT(menu), "actionAddContact" );
		actionAddContact->plug( menu );
		menu->insertSeparator();
	}

	// FIXME: After KDE 3.2 we should make isReachable do the isConnected call so it can be removed here - Martijn
	bool reach = account()->isConnected() && isReachable();
	bool myself = (this == account()->myself());

	KAction *actionSendMessage = KopeteStdAction::sendMessage( TQT_TQOBJECT(this), TQT_SLOT( sendMessage() ), TQT_TQOBJECT(menu), "actionSendMessage" );
	actionSendMessage->setEnabled( reach && !myself );
	actionSendMessage->plug( menu );

	KAction *actionChat = KopeteStdAction::chat( TQT_TQOBJECT(this), TQT_SLOT( startChat() ), TQT_TQOBJECT(menu), "actionChat" );
	actionChat->setEnabled( reach && !myself );
	actionChat->plug( menu );

	KAction *actionSendFile = KopeteStdAction::sendFile( TQT_TQOBJECT(this), TQT_SLOT( sendFile() ), TQT_TQOBJECT(menu), "actionSendFile" );
	actionSendFile->setEnabled( reach && d->fileCapable && !myself );
	actionSendFile->plug( menu );

	// Protocol specific options will go below this separator
	// through the use of the customContextMenuActions() function

	// Get the custom actions from the protocols ( pure virtual function )
	TQPtrList<KAction> *customActions = customContextMenuActions( manager );
	if( customActions && !customActions->isEmpty() )
	{
		menu->insertSeparator();

		for( KAction *a = customActions->first(); a; a = customActions->next() )
			a->plug( menu );
	}
	delete customActions;

	menu->insertSeparator();

	if( metaContact() && !metaContact()->isTemporary() )
		KopeteStdAction::changeMetaContact( TQT_TQOBJECT(this), TQT_SLOT( changeMetaContact() ), TQT_TQOBJECT(menu), "actionChangeMetaContact" )->plug( menu );

	KopeteStdAction::contactInfo( TQT_TQOBJECT(this), TQT_SLOT( slotUserInfo() ), TQT_TQOBJECT(menu), "actionUserInfo" )->plug( menu );

#if 0 //this is not fully implemented yet (and doesn't work).  disable for now   - Olivier 2005-01-11
	if ( account()->isBlocked( d->contactId ) )
		KopeteStdAction::unblockContact( this, TQT_SLOT( slotUnblock() ), menu, "actionUnblockContact" )->plug( menu );
	else
		KopeteStdAction::blockContact( this, TQT_SLOT( slotBlock() ), menu, "actionBlockContact" )->plug( menu );
#endif

	if( metaContact() && !metaContact()->isTemporary() )
		KopeteStdAction::deleteContact( TQT_TQOBJECT(this), TQT_SLOT( slotDelete() ), TQT_TQOBJECT(menu), "actionDeleteContact" )->plug( menu );

	return menu;
}

void Contact::changeMetaContact()
{
	KDialogBase *moveDialog = new KDialogBase( Kopete::UI::Global::mainWidget(), "moveDialog", true, i18n( "Move Contact" ),
		KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true );

	TQVBox *w = new TQVBox( moveDialog );
	w->setSpacing( KDialog::spacingHint() );
	Kopete::UI::MetaContactSelectorWidget *selector = new Kopete::UI::MetaContactSelectorWidget(w);
	selector->setLabelMessage(i18n( "Select the meta contact to which you want to move this contact:" ));
	// exclude this metacontact as a target metacontact for the move
	selector->excludeMetaContact( metaContact() );
	TQCheckBox *chkCreateNew = new TQCheckBox( i18n( "Create a new metacontact for this contact" ), w );
	TQWhatsThis::add( chkCreateNew , i18n( "If you select this option, a new metacontact will be created in the top-level group "
		"with the name of this contact and the contact will be moved to it." ) );
	TQObject::connect( chkCreateNew , TQT_SIGNAL( toggled(bool) ) ,  selector , TQT_SLOT ( setDisabled(bool) ) ) ;

	moveDialog->setMainWidget(w);
	if( moveDialog->exec() == TQDialog::Accepted )
	{
		Kopete::MetaContact *mc = selector->metaContact();
		if(chkCreateNew->isChecked())
		{
			mc=new Kopete::MetaContact();
			Kopete::ContactList::self()->addMetaContact(mc);
		}
		if( mc )
		{
			setMetaContact( mc );
		}
	}

	moveDialog->deleteLater();
}

void Contact::setMetaContact( MetaContact *m )
{
	MetaContact *old = d->metaContact;
	if(old==m) //that make no sens
		return;

	if( old )
	{
		int result=KMessageBox::No;
		if( old->isTemporary() )
			result=KMessageBox::Yes;
		else if( old->contacts().count()==1 )
		{ //only one contact, including this one, that mean the contact will be empty efter the move
			result = KMessageBox::questionYesNoCancel( Kopete::UI::Global::mainWidget(), i18n( "You are moving the contact `%1' to the meta contact `%2'.\n"
				"`%3' will be empty afterwards. Do you want to delete this contact?" )
					.arg(contactId(), m ? m->displayName() : TQString(), old->displayName())
				, i18n( "Move Contact" ), KStdGuiItem::del(), i18n( "&Keep" ) , TQString::fromLatin1("delete_old_contact_when_move") );
			if(result==KMessageBox::Cancel)
				return;
		}
		old->removeContact( this );
		disconnect( old, TQT_SIGNAL( aboutToSave( Kopete::MetaContact * ) ),
			protocol(), TQT_SLOT( slotMetaContactAboutToSave( Kopete::MetaContact * ) ) );

		if(result==KMessageBox::Yes)
		{
			//remove the old metacontact.  (this delete the MC)
			ContactList::self()->removeMetaContact(old);
		}
		else
		{
			d->metaContact = m; //i am forced to do that now if i want the next line works
			//remove cached data for this protocol which will not be removed since we disconnected
			protocol()->slotMetaContactAboutToSave( old );
		}
	}

	d->metaContact = m;

	if( m )
	{
		m->addContact( this );
		m->insertChild( this );
		// it is necessary to call this write here, because MetaContact::addContact() does not differentiate
		// between adding completely new contacts (which should be written to kabc) and restoring upon restart
		// (where no write is needed).
		KABCPersistence::self()->write( m );
		connect( d->metaContact, TQT_SIGNAL( aboutToSave( Kopete::MetaContact * ) ),
		protocol(), TQT_SLOT( slotMetaContactAboutToSave( Kopete::MetaContact * ) ) );
	}
	sync();
}

void Contact::serialize( TQMap<TQString, TQString> &/*serializedData*/,
	TQMap<TQString, TQString> & /* addressBookData */ )
{
}


void Contact::serializeProperties(TQMap<TQString, TQString> &serializedData)
{

	Kopete::ContactProperty::Map::ConstIterator it;// = d->properties.ConstIterator;
	for (it=d->properties.begin(); it != d->properties.end(); ++it)
	{
		if (!it.data().tmpl().persistent())
			continue;

		TQVariant val = it.data().value();
		TQString key = TQString::fromLatin1("prop_%1_%2").arg(TQString::fromLatin1(val.typeName()), it.key());

		serializedData[key] = val.toString();

	} // end for()
} // end serializeProperties()

void Contact::deserializeProperties(
	TQMap<TQString, TQString> &serializedData )
{
	TQMap<TQString, TQString>::ConstIterator it;
	for ( it=serializedData.begin(); it != serializedData.end(); ++it )
	{
		TQString key = it.key();

		if ( !key.startsWith( TQString::fromLatin1("prop_") ) ) // avoid parsing other serialized data
			continue;

		TQStringList keyList = TQStringList::split( TQChar('_'), key, false );
		if( keyList.count() < 3 ) // invalid key, not enough parts in string "prop_X_Y"
			continue;

		key = keyList[2]; // overwrite key var with the real key name this property has
		TQString type( keyList[1] ); // needed for TQVariant casting

		TQVariant variant( it.data() );
		if( !variant.cast(TQVariant::nameToType(type.latin1())) )
		{
			kdDebug(14010) << k_funcinfo <<
				"Casting TQVariant to needed type FAILED" <<
				"key=" << key << ", type=" << type << endl;
			continue;
		}

		Kopete::ContactPropertyTmpl tmpl = Kopete::Global::Properties::self()->tmpl(key);
		if( tmpl.isNull() )
		{
			kdDebug( 14010 ) << k_funcinfo << "no ContactPropertyTmpl defined for" \
				" key " << key << ", cannot restore persistent property" << endl;
			continue;
		}

		setProperty(tmpl, variant);
	} // end for()
}


bool Contact::isReachable()
{
	// The default implementation returns false when offline and true
	// otherwise. Subclass if you need more control over the process.
	return onlineStatus().status() != OnlineStatus::Offline;
}


void Contact::startChat()
{
	KopeteView *v=manager( CanCreate )->view(true, TQString::fromLatin1("kopete_chatwindow") );
	if(v)
		v->raise(true);
}

void Contact::sendMessage()
{
	KopeteView *v=manager( CanCreate )->view(true, TQString::fromLatin1("kopete_emailwindow") );
	if(v)
		v->raise(true);
}

void Contact::execute()
{
	// FIXME: After KDE 3.2 remove the isConnected check and move it to isReachable - Martijn
	if ( account()->isConnected() && isReachable() )
	{
		KopeteView *v=manager( CanCreate )->view(true, KopetePrefs::prefs()->interfacePreference() );
		if(v)
			v->raise(true);
	}
	else
	{
		KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
			i18n( "This user is not reachable at the moment. Please try a protocol that supports offline sending, or wait "
			"until this user comes online." ), i18n( "User is Not Reachable" ) );
	}
}

void Contact::slotDelete()
{
	if ( KMessageBox::warningContinueCancel( Kopete::UI::Global::mainWidget(),
		i18n( "Are you sure you want to remove the contact  '%1' from your contact list?" ).
		arg( d->contactId ), i18n( "Remove Contact" ), KGuiItem(i18n("Remove"), TQString::fromLatin1("delete_user") ),
		TQString::fromLatin1("askRemoveContact"), KMessageBox::Notify | KMessageBox::Dangerous )
		== KMessageBox::Continue )
	{
		deleteContact();
	}
}

void Contact::deleteContact()
{
	// Default implementation simply deletes the contact
	deleteLater();
}


MetaContact * Contact::metaContact() const
{
	return d->metaContact;
}

TQString Contact::contactId() const
{
	return d->contactId;
}

Protocol * Contact::protocol() const
{
	return d->account ? d->account->protocol() : 0L;
}

Account * Contact::account() const
{
	return d->account;
}



void Contact::sync(unsigned int)
{
	/* Default implementation does nothing */
}

TQString& Contact::icon() const
{
	return d->icon;
}

void Contact::setIcon( const TQString& icon )
{
	d->icon = icon;
	return;
}

TQPtrList<KAction> *Contact::customContextMenuActions()
{
	return 0L;
}

TQPtrList<KAction> *Contact::customContextMenuActions( ChatSession * /* manager */ )
{
	return customContextMenuActions();
}


bool Contact::isOnline() const
{
	return onlineStatus().isDefinitelyOnline();
}


bool Contact::isFileCapable() const
{
	return d->fileCapable;
}

void Contact::setFileCapable( bool filecap )
{
	d->fileCapable = filecap;
}


bool Contact::canAcceptFiles() const
{
	return isOnline() && d->fileCapable;
}

unsigned long int Contact::idleTime() const
{
	if(d->idleTime==0)
		return 0;

	return d->idleTime+(d->idleTimer.elapsed()/1000);
}

void Contact::setIdleTime( unsigned long int t )
{
	bool idleChanged = false;
	if(d->idleTime != t)
		idleChanged = true;
	d->idleTime=t;
	if(t > 0)
		d->idleTimer.start();
//FIXME: if t == 0, idleTime() will now return garbage
//	else
//		d->idleTimer.stop();
	if(idleChanged)
		emit idleStateChanged(this);
}


TQStringList Contact::properties() const
{
	return d->properties.keys();
}

bool Contact::hasProperty(const TQString &key) const
{
	return d->properties.contains(key);
}

const ContactProperty &Contact::property(const TQString &key) const
{
	if(hasProperty(key))
		return d->properties[key];
	else
		return Kopete::ContactProperty::null;
}

const Kopete::ContactProperty &Contact::property(
	const Kopete::ContactPropertyTmpl &tmpl) const
{
	if(hasProperty(tmpl.key()))
		return d->properties[tmpl.key()];
	else
		return Kopete::ContactProperty::null;
}


void Contact::setProperty(const Kopete::ContactPropertyTmpl &tmpl,
	const TQVariant &value)
{
	if(tmpl.isNull() || tmpl.key().isEmpty())
	{
		kdDebug(14000) << k_funcinfo <<
			"No valid template for property passed!" << endl;
		return;
	}

	if(value.isNull() || value.canCast(TQVariant::String) && value.toString().isEmpty())
	{
		removeProperty(tmpl);
	}
	else
	{
		TQVariant oldValue = property(tmpl.key()).value();

		if(oldValue != value)
		{
			Kopete::ContactProperty prop(tmpl, value);
			d->properties.insert(tmpl.key(), prop, true);

			emit propertyChanged(this, tmpl.key(), oldValue, value);
		}
	}
}

void Contact::removeProperty(const Kopete::ContactPropertyTmpl &tmpl)
{
	if(!tmpl.isNull() && !tmpl.key().isEmpty())
	{

		TQVariant oldValue = property(tmpl.key()).value();
		d->properties.remove(tmpl.key());
		emit propertyChanged(this, tmpl.key(), oldValue, TQVariant());
	}
}


TQString Contact::toolTip() const
{
	Kopete::ContactProperty p;
	TQString tip;
	TQStringList shownProps = KopetePrefs::prefs()->toolTipContents();

	// --------------------------------------------------------------------------
	// Fixed part of tooltip

	TQString iconName = TQString::fromLatin1("kopete-contact-icon:%1:%2:%3")
		.arg( KURL::encode_string( protocol()->pluginId() ),
				KURL::encode_string( account()->accountId() ),
				KURL::encode_string( contactId() ) );

	// TODO:  the nickname should be a configurable properties, like others. -Olivier
	TQString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
	if ( nick.isEmpty() )
	{
		tip = i18n( "<b>DISPLAY NAME</b><br><img src=\"%2\">&nbsp;CONTACT STATUS",
			"<b><nobr>%3</nobr></b><br><img src=\"%2\">&nbsp;%1" ).
			arg( Kopete::Message::escape( onlineStatus().description() ), iconName,
				Kopete::Message::escape( d->contactId ) );
	}
	else
	{
		tip = i18n( "<b>DISPLAY NAME</b> (CONTACT ID)<br><img src=\"%2\">&nbsp;CONTACT STATUS",
			"<nobr><b>%4</b> (%3)</nobr><br><img src=\"%2\">&nbsp;%1" ).
				arg( Kopete::Message::escape( onlineStatus().description() ), iconName,
					Kopete::Message::escape( contactId() ),
					Kopete::Emoticons::parseEmoticons( Kopete::Message::escape( nick ) ) );
	}

	// --------------------------------------------------------------------------
	// Configurable part of tooltip

	for(TQStringList::Iterator it=shownProps.begin(); it!=shownProps.end(); ++it)
	{
		if((*it) == TQString::fromLatin1("FormattedName"))
		{
			TQString name = formattedName();
			if(!name.isEmpty())
			{
				tip += i18n("<br><b>Full Name:</b>&nbsp;FORMATTED NAME",
							"<br><b>Full Name:</b>&nbsp;<nobr>%1</nobr>").arg(TQStyleSheet::escape(name));
			}
		}
		else if ((*it) == TQString::fromLatin1("FormattedIdleTime"))
		{
			TQString time = formattedIdleTime();
			if(!time.isEmpty())
			{
				tip += i18n("<br><b>Idle:</b>&nbsp;FORMATTED IDLE TIME",
					"<br><b>Idle:</b>&nbsp;<nobr>%1</nobr>").arg(time);
			}
		}
		else if ((*it) == TQString::fromLatin1("homePage"))
		{
			TQString url = property(*it).value().toString();
			if(!url.isEmpty())
			{
				tip += i18n("<br><b>Home Page:</b>&nbsp;FORMATTED URL",
					"<br><b>Home Page:</b>&nbsp;<a href=\"%1\"><nobr>%2</nobr></a>").
						arg( KURL::encode_string( url ), Kopete::Message::escape( TQStyleSheet::escape(url) ) );
			}
		}
		else if ((*it) == TQString::fromLatin1("awayMessage"))
		{
			TQString awaymsg = property(*it).value().toString();
			if(!awaymsg.isEmpty())
			{
				tip += i18n("<br><b>Away Message:</b>&nbsp;FORMATTED AWAY MESSAGE",
							"<br><b>Away&nbsp;Message:</b>&nbsp;%1").arg ( Kopete::Emoticons::parseEmoticons( Kopete::Message::escape(awaymsg) ) );
			}
		}
		else
		{
			p = property(*it);
			if(!p.isNull())
			{
				TQVariant val = p.value();
				TQString valueText;

				switch(val.type())
				{
					case TQVariant::DateTime:
						valueText = TDEGlobal::locale()->formatDateTime(val.toDateTime());
						valueText = Kopete::Message::escape( valueText );
						break;
					case TQVariant::Date:
						valueText = TDEGlobal::locale()->formatDate(val.toDate());
						valueText = Kopete::Message::escape( valueText );
						break;
					case TQVariant::Time:
						valueText = TDEGlobal::locale()->formatTime(val.toTime());
						valueText = Kopete::Message::escape( valueText );
						break;
					default:
						if( p.isRichText() )
						{
							valueText = val.toString();
						}
						else
						{
							valueText = Kopete::Message::escape( val.toString() );
						}
				}

				tip += i18n("<br><b>PROPERTY LABEL:</b>&nbsp;PROPERTY VALUE",
					"<br><nobr><b>%2:</b></nobr>&nbsp;%1").
						arg( valueText, TQStyleSheet::escape(p.tmpl().label()) );
			}
		}
	}

	return tip;
}

TQString Kopete::Contact::formattedName() const
{
	if( hasProperty(TQString::fromLatin1("FormattedName")) )
		return property(TQString::fromLatin1("FormattedName")).value().toString();

	TQString ret;
	Kopete::ContactProperty first, last;

	first = property(TQString::fromLatin1("firstName"));
	last = property(TQString::fromLatin1("lastName"));
	if(!first.isNull())
	{
		if(!last.isNull()) // contact has both first and last name
		{
			ret = i18n("firstName lastName", "%2 %1")
				.arg(last.value().toString())
				.arg(first.value().toString());
		}
		else // only first name set
		{
			ret = first.value().toString();
		}
	}
	else if(!last.isNull()) // only last name set
	{
		ret = last.value().toString();
	}

	return ret;
}

TQString Kopete::Contact::formattedIdleTime() const
{
	TQString ret;
	unsigned long int leftTime = idleTime();

	if ( leftTime > 0 )
	{	// FIXME: duplicated from code in kopetecontactlistview.cpp
		unsigned long int days, hours, mins, secs;

		days = leftTime / ( 60*60*24 );
		leftTime = leftTime % ( 60*60*24 );
		hours = leftTime / ( 60*60 );
		leftTime = leftTime % ( 60*60 );
		mins = leftTime / 60;
		secs = leftTime % 60;

		if ( days != 0 )
		{
			ret = i18n( "<days>d <hours>h <minutes>m <seconds>s",
				"%4d %3h %2m %1s" )
				.arg( secs )
				.arg( mins )
				.arg( hours )
				.arg( days );
		}
		else if ( hours != 0 )
		{
			ret = i18n( "<hours>h <minutes>m <seconds>s", "%3h %2m %1s" )
				.arg( secs )
				.arg( mins )
				.arg( hours );
		}
		else
		{
			ret = i18n( "<minutes>m <seconds>s", "%2m %1s" )
				.arg( secs )
				.arg( mins );
		}
	}
	return ret;
}


void Kopete::Contact::slotBlock()
{
	account()->block( d->contactId );
}

void Kopete::Contact::slotUnblock()
{
	account()->unblock( d->contactId );
}

void Kopete::Contact::setNickName( const TQString &name )
{
	setProperty( Kopete::Global::Properties::self()->nickName(), name );
}

TQString Kopete::Contact::nickName() const
{
	TQString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
	if( !nick.isEmpty() )
		return nick;

	return contactId();
}

void Contact::virtual_hook( uint , void * )
{ }


} //END namespace Kopete


#include "kopetecontact.moc"