/*
    kopetecontact.h - 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.      *
    *                                                                       *
    *************************************************************************
*/

#ifndef __KOPETECONTACT_H__
#define __KOPETECONTACT_H__

#include <qobject.h>
#include <kurl.h>
#include <kdemacros.h>
#include "kopeteglobal.h"

#include "kopete_export.h"

class QImage;
class KPopupMenu;
class KAction;

namespace Kopete
{

class Group;
class MetaContact;
class ChatSession;
class OnlineStatus;
class Plugin;
class Protocol;
class Account;
typedef QPtrList<Group> GroupList;

/**
 * @author Duncan Mac-Vicar P. <duncan@kde.org>
 * @author Martijn Klingens <klingens@kde.org>
 * @author Olivier Goffart <ogoffart@ tiscalinet.be>
 *
 * This class abstracts a generic contact
 * Use it for inserting contacts in the contact list for example.
 */
class KOPETE_EXPORT Contact : public QObject
{
	Q_OBJECT

	Q_ENUMS( CanCreateFlags )
	Q_PROPERTY( QString formattedName READ formattedName )
	Q_PROPERTY( QString formattedIdleTime READ formattedIdleTime )
	Q_PROPERTY( bool isOnline READ isOnline )
	Q_PROPERTY( bool fileCapable READ isFileCapable WRITE setFileCapable )
	Q_PROPERTY( bool canAcceptFiles READ canAcceptFiles )
	//Q_PROPERTY( bool isReachable READ isReachable )
	Q_PROPERTY( QString contactId READ contactId )
	Q_PROPERTY( QString icon READ icon WRITE setIcon )
	Q_PROPERTY( QString toolTip READ toolTip )
	Q_PROPERTY( QString nickName READ nickName WRITE setNickName )
	//Q_PROPERTY( unsigned long idleTime READ idleTime WRITE setIdleTime )

public:
	/**
	 * \brief Create new contact.
	 *
	 * <b>The parent MetaContact must not be NULL</b>
	 *
	 * \note id is required to be unique per protocol and per account.
	 * Across those boundaries ids may occur multiple times.
	 * The id is solely for comparing items safely (using pointers is
	 * more crash-prone). DO NOT assume anything regarding the id's
	 * value! Even if it may look like an ICQ UIN or an MSN passport,
	 * this is undefined and may change at any time!
	 *
	 * @param account is the parent account. this constructor automatically register the contact to the account
	 * @param id is the Contact's unique Id (mostly the user's login)
	 * @param parent is the parent @ref MetaContact this Contact is part of
	 * @param icon is an optional icon
	 */
	Contact( Account *account, const QString &id, MetaContact *parent,
		const QString &icon = QString::null );

	~Contact();

	/**
	 * \brief Get the metacontact for this contact
	 * @return The MetaContact object for this contact
	 */
	MetaContact *metaContact() const;


	/**
	 * \brief Get the unique id that identifies a contact.
	 *
	 * \note Id is required to be unique per protocol and per account.
	 * Across those boundaries ids may occur multiple times.
	 * The id is solely for comparing items safely (using pointers is
	 * more crash-prone). DO NOT assume anything regarding the id's
	 * value! Even if it may look like an ICQ UIN or an MSN passport,
	 * this is undefined and may change at any time!
	 *
	 * @return The unique id of the contact
	 */
	QString contactId() const;

	/**
	 * \brief Get the protocol that the contact belongs to.
	 *
	 * simply return account()->protocol()
	 *
	 * @return the contact's protocol
	 */
	Protocol* protocol() const;

	/**
	 * \brief Get the account that this contact belongs to
	 *
	 * @return the Account object for this contact
	 */
	Account* account() const;

	/**
	 * \brief Move this contact to a new MetaContact.
	 * This basically reparents the contact and updates the internal
	 * data structures.
	 * If the old contact is going to be empty, a question may ask to the user if it wants to delete the old contact.
	 *
	 * @param m The new MetaContact to move this contact to
	 */
	void setMetaContact(MetaContact *m);


	/**
	 * @brief Get whether this contact is online.
	 * @return @c true if the contact is online, @c false otherwise.
	 */
	bool isOnline() const;

	/**
	 * \brief Get whether this contact can receive messages
	 *
	 * Used in determining if the contact is able to
	 * receive messages.  This function must be defined by child classes
	 *
	 * @return true if the contact can be reached
	 * @return false if the contact can not be reached
	 */
	// FIXME: After KDE 3.2 we should split this into a public, NON-virtual
	//        isReachable() accessor that checks for account->isConnected()
	//        and then calls a new virtual method that does the
	//        protocol-specific work, like 'doIsUnreachable' or so - Martijn
	//
	//FIXME: Can this be made const please? - JK
	virtual bool isReachable();

	/**
	 * @brief Serialize the contact for storage in the contact list.
	 *
	 * The provided serializedData contain the contact id in the field
	 * "contactId". If you don't like this, or don't want to
	 * store these fields at all,
	 * you are free to remove them from the list.
	 *
	 * Most plugins don't need more than these fields, so they only need
	 * to set the address book fields themselves. If you have nothing to
	 * save at all you can clear the QMap, an empty map is treated as
	 * 'nothing to save'.
	 *
	 * The provided addressBookFields QMap contains the index field as
	 * marked with @ref Plugin::addAddressBookField() with the
	 * contact id as value. If no index field is available the QMap is
	 * simply passed as an empty map.
	 *
	 * @sa Protocol::deserializeContact
	 */
	virtual void serialize( QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData );

	/**
	 * @brief Serialize the contacts persistent properties for storage in the contact list.
	 *
	 * Does the same as @ref serialize() does but for KopeteContactProperties
	 * set in this contact with their persistency flag turned on.
	 * In contrary to @ref serialize() this does not need to be reimplemented.
	 *
	 */
	void serializeProperties(QMap<QString, QString> &serializedData);

	/**
	 * @brief Deserialize the contacts persistent properties
	 */
	void deserializeProperties(QMap<QString, QString> &serializedData);

	/**
	 * @brief Get the online status of the contact
	 * @return the online status of the contact
	 */
	OnlineStatus onlineStatus() const;

	/**
	 * \brief Set the contact's online status
	 */
	void setOnlineStatus(const OnlineStatus &status);

	/**
	 * \brief Get the set of custom menu items for this contact
	 *
	 * Returns a set of custom menu items for the context menu
	 * which is displayed in showContextMenu (private).  Protocols
	 * should use this to add protocol-specific actions to the
	 * popup menu. Kopete take care of the deletion of the action collection.
	 * Actions should have the collection as parent.
	 *
	 * @return Collection of menu items to be show on the context menu
	 * @todo if possible, try to use KXMLGUI
	 */
	virtual QPtrList<KAction> *customContextMenuActions();

	/**
	 * @todo  What is this function for ?
	 */
	virtual QPtrList<KAction> *customContextMenuActions( ChatSession *manager );

	/**
	 * @brief Get the Context Menu for this contact
	 *
	 * This menu includes generic actions common to each protocol, and action defined in
	 * @ref customContextMenuActions()
	 */
	KPopupMenu *popupMenu( ChatSession *manager = 0L );

	/**
	 * \brief Get whether or not this contact is capable of file transfers
	 *
	 *
	 * \see setFileCapable()
	 * \return true if the protocol for this contact is capable of file transfers
	 * \return false if the protocol for this contact is not capable of file transfers
	 *
	 * @todo have a capabilioties. or move to protocol capabilities
	 */
	bool isFileCapable() const;

	/**
	 * \brief Set the file transfer capability of this contact
	 *
	 * \param filecap The new file transfer capability setting
	 * @todo have a capabilioties. or move to protocol capabilities
	 */
	void setFileCapable( bool filecap );

	/**
	 * \brief Get whether or not this contact can accept file transfers
	 *
	 * This function checks to make sure that the contact is online as well as
	 * capable of sending files.
	 * \see isReachable()
	 * @return true if this contact is online and is capable of receiving files
	 * @todo have a capabilioties. or move to protocol capabilities
	 */
	bool canAcceptFiles() const;

	enum CanCreateFlags {  CannotCreate=false , CanCreate=true  };

	/**
	 * Returns the primary message manager affiliated with this contact
	 * Although a contact can have more than one active message manager
	 * (as is the case with MSN at least), only one message manager will
	 * ever be the contacts "primary" message manager.. aka the 1 on 1 chat.
	 * This function should always return that instance.
	 *
	 * @param canCreate If a new message manager can be created in addition
	 * to any existing managers. Currently, this is only set to true when
	 * a chat is initiated by the user by clicking the contact list.
	 */
	virtual ChatSession * manager( CanCreateFlags canCreate = CannotCreate ) =0;

	/**
	 * Returns the name of the icon to use for this contact
	 *  If null, the protocol icon need to be used.
	 * The icon is not colored, nor has the status icon overloaded
	 */
	QString& icon() const;

	/**
	 * @brief Change the icon to use for this contact
	 * If you don't want to have the protocol icon as icon for this contact, you may set
	 * another icon.  The icon doesn't need to be colored with the account icon as this operation
	 * will be performed later.
	 *
	 * if you want to go back to the protocol icon, set a null string.
	 */
	void setIcon( const QString& icon );

	/**
	 * \brief Get the time (in seconds) this contact has been idle
	 * It will return the time set in @ref setIdleTime() with an addition of the time
	 * since you set this last time
	 * @return time this contact has been idle for, in seconds
	 //
         // FIXME: Can we make this just 'unsigned long' ? QT Properties can't handle
         // 'unsigned long int'
	 */
	virtual unsigned long int idleTime() const;

	/**
	 * \brief Set the current idle time in seconds.
	 * Kopete will automatically calculate the time in @ref idleTime
	 * except if you set 0.
	 //
	 // FIXME: Can we make this just 'unsigned long' ? QT Properties can't handle
	 // 'unsigned long int'
	 */
	void setIdleTime(unsigned long int);

	/**
	 * @return A QStringList containing all property keys
	 **/
	QStringList properties() const;

	/**
	 * Check for existance of a certain property stored
	 * using "key".
	 * \param key the property to check for
	 **/
	bool hasProperty(const QString &key) const;

	/**
	 * \brief Get the value of a property with key "key".
	 *
	 * If you don't know the type of the returned QVariant, you will need
	 * to check for it.
	 * \return the value of the property
	 **/
	const Kopete::ContactProperty &property(const QString &key) const;
	const Kopete::ContactProperty &property(const Kopete::ContactPropertyTmpl &tmpl) const;

	/**
	 * \brief Add or Set a property for this contact.
	 *
	 * @param tmpl The template this property is based on, key, label etc. are
	 * taken from this one
	 * @param value The value to store
	 *
	 * \note Setting a NULL value or an empty QString castable value
	 * removes the property if it already existed.
	 * <b>Don't</b> abuse this for property-removal, instead use
	 * @ref removeProperty() if you want to remove on purpose.
	 * The Removal is done to clean up the list of properties and to purge them
	 * from UI.
	 **/
	void setProperty(const Kopete::ContactPropertyTmpl &tmpl, const QVariant &value);

        /**
	 * \brief Convenience method to set the nickName property to the specified value
	 * @param name The nickname to set
	 */
	void setNickName( const QString &name );

	/**
	 * \brief Convenience method to retrieve the nickName property.
	 *
	 * This method will return the contactId if there has been no nickName property set
	 */
	QString nickName() const;

	/**
	 * \brief Remove a property if it exists
	 *
	 * @param tmpl the template this property is based on
	 **/
	void removeProperty(const Kopete::ContactPropertyTmpl &tmpl);

	/**
	 * \brief Get the tooltip for this contact
	 * Makes use of formattedName() and formattedIdleTime().
	 * \return an RTF tooltip depending on KopetePrefs settings
	 **/
	QString toolTip() const;

	/**
	 * Returns a formatted string of "firstName" and/or "lastName" properties
	 * if present.
	 * Suitable for GUI display
	 **/
	QString formattedName() const;

	/**
	 * Returns a formatted string of idleTime().
	 * Suitable for GUI display
	 **/
	QString formattedIdleTime() const;

	/**
	 * used in @ref sync()
	 */
	enum Changed{ MovedBetweenGroup = 0x01, ///< the contact has been moved between groups
		      DisplayNameChanged = 0x02 ///< the displayname of the contact changed
	};


public slots:
	/**
	 * This should typically pop up a KopeteChatWindow
	 */
	void startChat();

	/**
	 * Pops up an email type window
	 */
	void sendMessage();

	/**
	 * The user clicked on the contact, do the default action
	 */
	void execute();

	/**
	 * Changes the MetaContact that this contact is a part of.  This function
	 * is called by the KAction changeMetaContact that is part of the context
	 * menu.
	 */
	void changeMetaContact();

	/**
	 * Method to retrieve user information.  Should be implemented by
	 * the protocols, and popup some sort of dialog box
	 *
	 * reimplement it to show the informlation
	 * @todo rename and make it pure virtual
	 */
	virtual void slotUserInfo() {};

	/**
	 * @brief Syncronise the server and the metacontact.
	 * Protocols with server-side contact lists can implement this to
	 * sync the server groups with the metaContact groups. Or the server alias if any.
	 *
	 * This method is called every time the metacontact has been moved or renamed.
	 *
	 * default implementation does nothing
	 *
	 * @param changed is a bitmask of the @ref Changed enum which say why the call to this funtion is done.
	 */
	virtual void sync(unsigned int changed = 0xFF);

	/**
	 * Method to delete a contact from the contact list,
	 * should be implemented by protocol plugin to handle
	 * protocol-specific actions required to delete a contact
	 * (ie. messages to the server, etc)
	 * the default implementation simply call deleteLater()
	 */
	virtual void deleteContact();

	/**
	 * This is the Contact level slot for sending files. It should be
	 * implemented by all contacts which have the setFileCapable() flag set to
	 * true. If the function is called through the GUI, no parameters are sent
	 * and they take on default values (the file is chosen with a file open dialog)
	 *
	 * @param sourceURL The actual KURL of the file you are sending
	 * @param fileName (Optional) An alternate name for the file - what the
	 * receiver will see
	 * @param fileSize (Optional) Size of the file being sent. Used when sending
	 * a nondeterminate
	 *                file size (such as over  asocket
	 */
	virtual void sendFile( const KURL &sourceURL = KURL(),
			       const QString &fileName = QString::null, uint fileSize = 0L );

private slots:

	/**
	 * This add the contact totally in the list if it was a temporary contact
	 */
	void slotAddContact();

	/**
	 * slot called when the action "delete" is called.
	 */
	void slotDelete();

	/**
	 * slot called when the action "block" is called.
	 */
	void slotBlock();

	/**
	 * slot called when the action "unblock" is called.
	 */
	void slotUnblock();

	/**
	 * The account's isConnected has changed.
	 */
	void slotAccountIsConnectedChanged();

signals:
	/**
	 * The contact's online status changed
	 */
	void onlineStatusChanged( Kopete::Contact *contact,
		const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus );

	/**
	 * The contact is about to be destroyed.
	 * Called when entering the destructor. Useful for cleanup, since
	 * metaContact() is still accessible at this point.
	 *
	 * @warning this signal is emit in the Contact destructor, so all
	 * virtual method are not available
	 */
	void contactDestroyed( Kopete::Contact *contact );

	/**
	 * The contact's idle state changed.
	 * You need to emit this signal to update the view.
	 * That mean when activity has been noticed
	 */
	void idleStateChanged( Kopete::Contact *contact );

	/**
	 * One of the contact's properties has changed.
	 * @param contact this contact, useful for listening to signals from more than one contact
	 * @param key the key whose value has changed
	 * @param oldValue the value before the change, or an invalid QVariant if the property is new
	 * @param newValue the value after the change, or an invalid QVariant if the property was removed
	 */
	void propertyChanged( Kopete::Contact *contact, const QString &key,
		const QVariant &oldValue, const QVariant &newValue );

protected:
	virtual void virtual_hook(uint id, void *data);

private:
	class Private;
	Private *d;


};


} //END namespace Kopete

#endif

// vim: set noet ts=4 sts=4 sw=4:

