/***************************************************************************
                          msnswitchboardconnection.h  -  description
                             -------------------
    begin                : Fri Jan 24 2003
    copyright            : (C) 2003 by Mike K. Bennett
    email                : mkb137b@hotmail.com
 ***************************************************************************/

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

#ifndef MSNSWITCHBOARDCONNECTION_H
#define MSNSWITCHBOARDCONNECTION_H

#include "msnconnection.h"
#include "mimemessage.h"
#include "../contact/contactbase.h"

#include <QMap>
#include <QPair>
#include <QStringList>


// Forward declarations
class ChatInformation;
class KMessTest;
class MsnObject;
class P2PMessage;
class ContactBase;
class ChatMessage;
class OfflineImService;

class QFont;
class QTimer;



/**
 * A connection to a switchboard server, over which chats are conducted.
 *
 * @author Mike K. Bennett
 * @ingroup NetworkCore
 */
class MsnSwitchboardConnection : public MsnConnection
{
  Q_OBJECT

  friend class KMessTest;


  public: // Public enumerations
    // The supported applications
    enum ApplicationType
    {
      FILETRANSFER
    };

    /// Types of emittable warnings
    enum WarningType
    {
      WARNING_UNSUPPORTED_VOICECLIP      /// A voice clip has been received
    , WARNING_UNSUPPORTED_ACTIONMESSAGE  /// An action message has been received
    , WARNING_UNSUPPORTED_UNKNOWN        /// An unknown message type has been received
    , WARNING_CONNECTION_DROP            /// The connection has been dropped
    , WARNING_TOO_MANY_EMOTICONS         /// There were too many emoticons in the last message
    , WARNING_INK_UNSUPPORTED_BY_CONTACT /// One or more contacts cannot receive the Ink message
    };

    // The constructor
                         MsnSwitchboardConnection();
    // The copy constructor
                         MsnSwitchboardConnection( const MsnSwitchboardConnection &other );
    // The destructor
    virtual             ~MsnSwitchboardConnection();
    // Close the connection, only for emergaincy situations
    virtual void         closeConnection();
    // Clean up, close the connection, destroy this object
    void                 closeConnectionLater(bool autoDelete = false);
    // Make a list of the contacts in the chat
    const QStringList    getContactsInChat() const;
    // Return the first contact the chat started with.
    const QString&       getFirstContact() const;
    // Return the last contact who left the chat.
    const QString&       getLastContact() const;
    // Return whether the user started the chat
    bool                 getUserStartedChat() const;
    // Initialize the object, optionally presetting a contact to reinvite
    bool                 initialize( QString handle = QString() );
    // Invite a contact into the chat
    void                 inviteContact( const QString& handle );
    // Check whether the switchboard is buzy (has too many pending messages)
    bool                 isBusy() const;
    // Check if a certain contact is in the chat
    bool                 isContactInChat(const QString& handle) const;
    // Check if all contacts left
    bool                 isEmpty() const;
    // Check if only the given contact is in the chat
    bool                 isExclusiveChatWithContact(const QString& handle) const;
    // Check whether the connection is idle
    bool                 isExpired() const;
    // Check whether the switchboard is currently not connected, nor trying to connect
    bool                 isInactive() const;
    // Check whether the switchboard is waiting for a new connection
    bool                 isWaiting() const;
    // Send a message to the contact(s)
    void                 sendChatMessage( const QString& text );
    // Send a ink to the contact(s)
    void                 sendInk( InkFormat format, const QByteArray& ink );
    // Send a nudge to a contact
    void                 sendNudge();
    // The user is typing so send a typing message
    void                 sendTypingMessage();
    // Send wink
    void                 sendWink( const MsnObject& msnobject );
    // Start a switchboard connection
    void                 start( const ChatInformation &chatInfo );
    // Deliver a message for an Application object.
    void                 sendApplicationMessage( const MimeMessage &message );
    // Set true if the user request the starting of the chat
    void                 setUserStartedChat( bool startedByUser );


  public slots:
    // Send a "ping" to avoid MSN closing the connection
    void                 sendKeepAlive();


  protected slots:
    // The socket connected, so send the version command.
    virtual void         slotConnected();
    // Shows error dialog boxes
    void                 slotError( QString error, MsnSocketBase::ErrorType type );


  private slots:
    // An ApplicationList object indicated it aborted all it's applications.
    void                 slotApplicationsAborted(const QString &contact);


  private:  // private structs
    struct UnAckedMessage
    {
      uint        time;
      AckType     ackType;
      MimeMessage message;
    };


  private: // Private methods
    // Initialize or restart the last activity timer, for keep alive messages
    void                 activity();
    // Clean the old unacked messages
    void                 cleanUnackedMessages();
    // Do what's required when a contact joins the chat
    void                 contactJoined(const QString& handle, const QString& friendlyName, const uint capabilities);
    // Remove a contact from the list of contacts in the chat
    void                 contactLeft(const QString& handle);
    // Convert an html format (#RRGGBB) color to an msn format (BBGGRR) color
    const QString        convertHtmlColorToMsnColor( const QString &color ) const;
    // Convert and msn format color (BBGGRR) to an html format (#RRGGBB) color
    const QString        convertMsnColorToHtmlColor( QString& color) const;
    // Received a positive delivery message.
    void                 gotAck(const QStringList& command);
    // Received notification that a contact is no longer in session.
    void                 gotBye(const QStringList& command);
    // Received the initial roster information for new contacts joining a session.
    void                 gotIro(const QStringList& command);
    // Received notification of a new client in the session.
    void                 gotJoi(const QStringList& command);
    // Received a negative acknowledgement of the receipt of a message.
    void                 gotNak(const QStringList& command);
    // Received notification of the termination of a client-server session.
    void                 gotOut(const QStringList& command);
    // Received a client-server authentication message.
    void                 gotUsr(const QStringList& command);
    // Parse a regular command
    void                 parseCommand(const QStringList& command);
    // Parse a normal plain text chat message
    void                 parseChatMessage(const QString &contactHandle, const QString &friendlyName, const QString &contactPicture, const MimeMessage &message);
    // Parse the clientcaps message, exchanged by a lot of third party clients.
    void                 parseClientCapsMessage(const QString &contactHandle, const MimeMessage &message);
    // Parse a datacast message (e.g. nudge or voice clip)
    void                 parseDatacastMessage(const QString &contactHandle, const MimeMessage &message);
    // Parse an emoticon message
    void                 parseEmoticonMessage(const QString &contactHandle, const QString &messageBody);
    // Parse an error command
    void                 parseError( const QStringList& command, const QByteArray &payloadData );
    // Parse a message command
    void                 parseMimeMessage(const QStringList& command, const MimeMessage &message);
    // Parse a payload command
    virtual void         parsePayloadMessage(const QStringList &command, const QByteArray &payload);
    // Parse a p2p message, used for invitation
    void                 parseP2PMessage( const QString &contactHandle, const MimeMessage &message );
    // Parse a contact is typing message.
    void                 parseTypingMessage( const QString &contactHandle, const MimeMessage &message );
    // Send a client caps message to the contacts
    void                 sendClientCaps();
    // Send a message to the contact(s), or leave it pending until a connection is restored
    void                 sendMimeMessageWhenReady(AckType ackType, const MimeMessage &message);
    // Send a multipacket message to contact(s), or leave it pending until a connection is restored
    void                 sendMimeMessageWhenReady( AckType ackType, MultiPacketMessage &message );
    // Send messages that weren't sent because a contact had to be re-called
    void                 sendPendingMessages();
    // Store a message for later acknowledgement
    void                 storeMessageForAcknowledgement(int ack, AckType ackType, const MimeMessage& message);


  private: // Private attributes

    enum ConnectionState
    {
      SB_DISCONNECTED,
      SB_CONNECTING,
      SB_AUTHORIZING,
      SB_INVITING_CONTACTS,
      SB_CHAT_STARTED,
      SB_CONTACTS_LEFT,
      SB_REQUESTING_CHAT,
      SB_DISCONNECTING
    };

    // Whether this object is aborting all it's applications
    bool                 abortingApplications_;
    // The number of ACK messages pending
    int                  acksPending_;
    // The authorization used to connect to the switchboard server
    QString              authorization_;
    // Whether this object should be deleted after finishing closeConnectionLater()
    bool                 autoDeleteLater_;
    // Whether this switchboard is connected to a chatwindow or it's a background connection.
    bool                 backgroundConnection_;
    // The id of the chat, used for authenticating a contact-started chat
    QString              chatId_;
    // Whether this object is closing it's connection using closeConnectionLater()
    bool                 closingConnection_;
    // Current status of connection to the switchboard server
    ConnectionState      connectionState_;
    // A list of the contacts in the chat
    QStringList          contactsInChat_;
    // A pointer to the shared current account singleton.
    CurrentAccount      *currentAccount_;
    // The last contact that was participating to the chat.
    QString              firstContact_;
    // Whether or not the object has been initialized
    bool                 initialized_;
    // The last contact that last the chat.
    QString              lastContact_;
    // A list of contacts that have been invited when the switchboard wasn't ready yet
    QStringList          pendingInvitations_;
    // A list of custom emoticon which have already been sent to the contacts
    // NOTE: Behavior changed since WLM 8+, emoticon data must be sent every time.
//     QStringList          sentEmoticons_;
    // A list of messages that should be sent to the contact when connection is reestablished
    QList< QPair<AckType,MimeMessage>* > pendingMessages_;
    // Queue of sent messages waiting ACK or NAK
    QHash< int, UnAckedMessage* > unAckedMessages_;
    // Whether the user (true) or the contact (false) started the chat
    bool                 userStartedChat_;
    // Timer to keep track of the last activity, to manage keep alive messages.
    QTimer              *keepAliveTimer_;
    // Timer to keep track of last activities, to manage keep alive messages.
    int                  keepAlivesRemaining_;
    // Service to send offline messages
    OfflineImService    *offlineImService_;


  signals:
    // Signal that a contact joined (or was already in) the chat
    void                 contactJoinedChat( ContactBase *contact );
    // Signal that a contact left the chat
    void                 contactLeftChat  ( ContactBase *contact, bool isChatIdle );
    // Signal that a contact is typing
    void                 contactTyping    ( ContactBase *contact );
    // Signal a chat message
    void                 chatMessage( const ChatMessage &message );
    // The switchboard received a ink message, requesting processing by the ChatMaster
    void                 gotInkMessage( const QString &inkData, const QString &handle, InkFormat format );
    // The switchboard received a Mime message, requesting processing by ChatMaster
    void                 gotMessage( const MimeMessage &message, const QString &handle );
    // The switchboard received a P2P message, requesting processing by ChatMaster
    void                 gotMessage( const P2PMessage &message, const QString &handle );
    // The switchboard received a msn object (wink, voice clip)
    void                 gotMsnObject( const QString &msnObjectData, const QString &handle );
    // The switchboard received a nudge (buzzer)
    void                 receivedNudge( ContactBase *contact );
    // The switchboard is ready to send more application messages
    void                 readySend();
    // Request the Chat Master to link this switchboard to a chat window
    void                 requestChatWindow( MsnSwitchboardConnection *switchboard );
    // Signal that this class needs a new chat connection
    void                 requestNewSwitchboard( QString contactHandle );
    // Signal that the delivery of a message has failed
    void                 sendingFailed( const QString &handle, const MimeMessage &message );
    // Signal that a warning message needs to be displayed in chat
    void                 showWarning( MsnSwitchboardConnection::WarningType type, ContactBase *contact );
    // The switchboard is closing, remove it from the Chat Master references
    void                 deleteMe( MsnSwitchboardConnection *object );

};



#endif
