/***************************************************************************
                          msnconnection.h  -  description
                             -------------------
    begin                : Thu Jan 23 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 MSNCONNECTION_H
#define MSNCONNECTION_H

#include "../kmessdebug.h"
#include "msnsocketbase.h"

#include <QHash>
#include <QDomElement>


// Forward declarations
class HttpSoapConnection;

class QByteArray;
class MimeMessage;
class MsnSocketBase;
class MultiPacketMessage;
class QStringList;



/**
 * @brief MSN server protocol implementation.
 *
 * The class provides the following facilities:
 * - parsing the raw network data into protocol commands.
 * - sending protocol commands and payloads to the actual connection implementations
 * - built-in handling of multi-packet <code>MSG</code> commands.
 *
 * Data from the basic I/O implementation is received by the dataReceived() slot.
 * This method identifies the data and calls one of the following methods to deliver the message the a derived class:
 * - parseCommand()
 * - parseMimeMessage()
 * - parsePayloadMessage()
 * - isPayloadCommand()
 *
 * These methods are implemented in the derived classes,
 * to handle the action for every command.
 *
 * To send protocol commands to the server, use:
 * - sendCommand()
 * - sendMimeMessage()
 * - sendPayloadMessage()
 *
 * @author Mike K. Bennett
 * @author Valerio Pilo <valerio@kmess.org>
 * @ingroup NetworkCore
 */
class MsnConnection : public QObject
{
  Q_OBJECT

  friend class KMessTest;

#ifdef KMESS_NETWORK_WINDOW
  friend class NetworkWindow;
#endif

  public: // Public methods
    // The destructor
    virtual             ~MsnConnection();
    // Whether or not the class is connected
    bool                 isConnected() const;


  protected: // Protected enumerations
    /// Ack type parameter for sendMimeMessage()
    enum AckType
    {
      ACK_NONE,             /// Don't request any ACK message.
      ACK_NAK_ONLY,         /// Send a NAK on failure, but nothing on success.
      ACK_ALWAYS,           /// Send a NAK on failure, and ACK on success
      ACK_ALWAYS_P2P        /// Special case for P2P data messsages ('<code>D</code>' type).
    };


  protected: // Protected methods
    // The constructor
                         MsnConnection( MsnSocketBase::ServerType serverType );
    // Register a SOAP client to the class.
    void                 addSoapClient( HttpSoapConnection *client );

    /**
     * @brief Close the connection.
     *
     * This method is called by this class only
     * when the connection needs to be closed.
     */
    virtual void         closeConnection() = 0;
    // Connect to the given server via the socket
    void                 connectToServer( const QString& server = QString(), const quint16 port = 0 );
    // Disconnect and delete the a SOAP client.
    void                 deleteSoapClient( HttpSoapConnection *client );
    // Disconnect and delete all SOAP clients.
    void                 deleteSoapClients();
    // Disconnect from the server, if connected
    void                 disconnectFromServer( bool isTransfer = false );
    // Get the IP address of this machine.
    const QString        getLocalIp() const;
    // Initialize the object
    bool                 initialize();
    // Return whether the given command is an error command.
    bool                 isErrorCommand( const QString &command ) const;
    // Test whether the given command is a payload command
    virtual bool         isPayloadCommand(const QString &command) const;
    // Specify which accepted commands carry payload data for this connection
    void                 setAcceptedPayloadCommands( QStringList commandList );

    /**
     * @brief Process a received command
     *
     * A command may look like:
     * @code
ADC 27 AL N=someone@kmessdemo.org
@endcode
     * This is a confirmation of a sent command (with ACK-number 27) that the contact someone@kmessdemo.org was added to the allow (<code>AL</code>) list.
     *
     * The command is always a three-letter acronym, stored in <code>command[0]</code>
     * Arguments are separated by spaces, stored in the remaining <code>command[1..n]</code> elements.
     *
     * Most commands sent with sendCommand() are echo'ed back to the client.
     * This is not only a confirmation, but also allow event-based development
     * (or Model-Viewer-Controller; the server is the model in this situation).
     * Instead of assuming a command like "add contact" succeeds,
     * the client simply waits for a returned <code>ADC</code> command.
     * Hence, the same <code>ADC</code> command is also used to report about new contacts.
     * The client only have to execute that the server intructs to do.
     *
     * When an error occurs, the command is a three-diget code, followed by the ACK-number of the sent command.
     * For example, in response to a <code>CAL</code> command, the server may return code <code>217</code>; "person is offline or invisible".
     * @code
>>> CAL 2 myname@kmessdemo.org
@endcode
     * @code
<<< 217 2
@endcode
     *
     * Some commands, like <code>MSG</code> or <code>UBX</code> are followed by a payload.
     * These commands are handled by parseMimeMessage() or parsePayloadMessage().
     *
     * @param  command  The command and arguments.
     */
    virtual void         parseCommand(const QStringList& command) = 0;

    /**
     * @brief Process a received error command
     *
     * An incoming error command may look like:
     * @code
205 12 65
@endcode
     * @code
<ml><d n="microsoft.com"><c n="messenger" t="1" l="4" /></d></ml>
@endcode
     *
     * or like:
     * @code
207 12
@endcode
     *
     * The first kind has a payload, the second does not. The additional command
     * parameter reveals that there's a payload.
     * We need to be able to parse both kinds, and report the additional data to
     * the user if any is present.
     *
     * The meaning of error codes can be looked up on MSNPiki:
     * http://msnpiki.msnfanatic.com/index.php/Reference:Error_List
     * but please note that at the moment of writing, it's outdated: it is not
     * mentioned there that even errors may contain payload data.
     *
     * @param  command      The received error and it's arguments.
     * @param  payloadData  The message payload which followed the command, if any.
     */
    virtual void         parseError( const QStringList &command, const QByteArray &payloadData );

    /**
     * @brief Process a received MIME message.
     *
     * A incoming message may look like:
     * @code
MSG somecontact@kmessdemo.org KMessDemo 146
@endcode
     * @code
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
X-MMS-IM-Format: FN=Arial; EF=; CO=0; CS=0; PF=0

no prob. How is development doing?
@endcode
     *
     * Notice the MIME-style header, and body below.
     * The <code>Content-Type</code> header indicates the message type,
     * for example: <code>text/plain</code> (a chat message)
     * <code>text/x-mms-emoticon</code> (custom emoticon definition)
     * or <code>text/x-msmsgsemailnotification</code> (new email notification).
     *
     * All MIME messages are preceeded by the <code>MSG</code> command.
     * This command is not delivered to parseCommand().
     * Instead, this class waits for the payload (the MIME message) to arrive.
     * The complete command and payload is forwarded to this method.
     * When the MIME message was splitted in a multi-packet message,
     * it's merged before this method is called.
     * The last argument of <code>command</code> can be ignored.
     * It indicates the size of the payload, in this case 146 bytes.
     *
     * @param  command  The MSG command and it's arguments
     * @param  message  The MIME message which followed the command.
     */
    virtual void         parseMimeMessage( const QStringList& command, const MimeMessage &message ) = 0;
    // Internal processing of multi-packet messages.
    void                 parseMultiPacketMimeMessage( const QStringList &command, const MimeMessage &mimeMessage );

    /**
     * @brief Process a received payload message
     *
     * A incoming message may look like:
     * @code
UBX someuser@kmessdemo.org 67
@endcode
     * @code
<Data><PSM>hello everyone</PSM><CurrentMedia></CurrentMedia></Data>
@endcode
     *
     * Notice the similarities with parseMimeMessage().
     * Both methods handle commands followed by payloads,
     * but differ in the type of messages:
     * - parseMimeMessage() only handles MIME payloads of the <code>MSG</code> command.
     * - this method handles any payload of a command returned by isPayloadCommand().
     *
     * The value of <code>command[0]</code> indicates which command is received.
     * The payload may have any contents, although XML has only been observed so far.
     * The last argument of <code>command</code> can be ignored.
     * It indicates the size of the payload, in this case 67 bytes.
     *
     * @param  command      The received command and it's arguments.
     * @param  messageData  The message payload which followed the command.
     */
    virtual void         parsePayloadMessage( const QStringList &command, const QByteArray &messageData ) = 0;
    // Send a command to the server, returning the ack
    int                  sendCommand( const QString& prefix, const QString &text = "\r\n" );
    // Send a command to the server, returning the ack
    int                  sendMimeMessage( AckType ackType, const MimeMessage &message );
    // Send a payload command to the server, convenience function
    int                  sendPayloadMessage( const QString &prefix, const QString &arguments, const QString &payload );
    // Send a payload command to the server
    int                  sendPayloadMessage( const QString &prefix, const QString &arguments, const QByteArray &payload );
    // Set whether we're sending pings or not
    void                 setSendPings( bool sendPings );
    // If it is possible, switch to the HTTP connection
    bool                 switchToHttpSocket();
    //Switch back to the TCP connection
    bool                 switchToTcpSocket();
    // Write data to the socket without conversions
    void                 writeBinaryData( const QByteArray& data );
    // Write data to the socket
    void                 writeData( const QString& data );


  protected slots: // Protected slots

    /**
     * @brief Called when the connection attempt is successful.
     *
     * This allows the first command to be sent.
     */
    virtual void         slotConnected() = 0;

    /**
     * @brief Shows error dialog boxes
     *
     * Closes the connection, and displays
     * a suitable type of message box.
     *
     * @param  error  The error reason or explanation
     * @param  type   The type of error
     */
    virtual void         slotError( QString error, MsnSocketBase::ErrorType type ) = 0;

  private:  // private methods
    // Connect a new socket's signals to our slots
    void                 attachToSocketSignals();

  private slots:
    // Read data from the socket
    void                 slotDataReceived( const QStringList &commandLine, const QByteArray &payloadData );
    // Called when the connection is lost.
    void                 slotDisconnected();

  private: // Private attributes
    // An acknowledgement number
    int                  ack_;
    // Whether or not the object was initialized
    bool                 initialized_;
    // A buffer for multi-packet messages
    QHash<QString,MultiPacketMessage*> multiPacketBuffer_;
    // The persistent soap clients.
    QList<HttpSoapConnection*> soapClients_;
    // The socket
    MsnSocketBase       *socket_;

  private: // Private static attributes
    // Whether or not the fallback HTTP socket is in use
    static bool          useHttpSocket_;


  signals:
    // Signal that the server has disconnected
    void                 disconnected();
    // Signal that a ping to the connection has been sent
    void                 pingSent();
    // Signal to send information to the main window's status bar
    void                 statusMessage( QString message, bool isError );

};



#endif
