/***************************************************************************
                          p2papplication.h -  description
                             -------------------
    begin                : Mon Nov 22 2004
    copyright            : (C) 2004 by Diederik van der Boor
    email                : vdboor --at-- codingdomain.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 P2PAPPLICATION_H
#define P2PAPPLICATION_H

#include "p2papplicationbase.h"

#include "../extra/p2pfragmenttracker.h"
#include "../p2pmessage.h"

class MimeMessage;



/**
 * @brief An Application subclass implementing MSNSLP on top of MSN6 P2P-style conversations.
 *
 * This base class aims to hide all complex details of the P2P and MSNSLP communication.
 * To implement a P2P service, create a derived class which
 * implements the userStarted*, contactStarted* and getAppId() methods.
 *
 * User-started invitations are handled this way:
 *
 * - The start() method is called externally to start the application.
 *   This method calls initializes the application and calls
 *   userStarted1_UserInvitesContact()
 *
 * - The userStarted1_UserInvitesContact() allows you to create the
 *   invitation message and use sendSlpInvitation() to send it.
 *
 * - The userStarted2_ContactAccepts() method is called when the
 *   contact accepts the invitation (sending a 200/OK message).
 *   It's possible to call sendSlpTransferInvitation() here.
 *
 * - The userStarted3_UserPrepares() method is called when the session is ready to transfer data.
 *   The  data-preparation message was received, a direct connection is ready,
 *   or data can be sent over the switchboard.
 *   From this method you can call start an external application,
 *   call sendDataPreparationAck() or sendData().
 *
 * - The gotData() method is called for every received data packet (e.g. file or picture data).
 *   You can store the data in a file, buffer it, etc..
 *   The session will terminate automatically when all data is received.
 *
 *
 * Contacted-started invitations are handled this way:
 *
 * - The gotMessage() method of the base class is called externally
 *   (by the ApplicationList of a Contact) to handle an incoming P2P message.
 *   The base class will relay this message to gotNegotiationMessage().
 *
 * - The contactStarted1_ContactInvitesUser() method is called when the invitation message is fully received.
 *   This allows you to extract the details from the invitation message.
 *   When needed, call offerAcceptOrReject() to show an accept/cancel link in the chat window.
 *   Otherwise, call contactStarted2_UserAccepts() directly to "automatically accept" the invitation.
 *
 * - The contactStarted2_UserAccepts() method is called when the user hits the 'accept' link
 *   in the chat window. The accept message should be prepared and sent with sendSlpOkMessage().
 *
 * - The contactStarted3_ContactConfirmsAccept() method is called when the contact ACKs the "SLP OK" message.
 *   From here, you can call sendDataPreparation() to send the data preparation message,
 *   or simply wait for the data transfer to start (contactStarted4_..).
 *
 * - The contactStarted4_ContactConfirmsPreparation() is called called when the session is ready to tranfer data.
 *   Either the data-preparation message was ACKed, or the direct connection is available.
 *   You can call sendData() from here to start the data transfer.
 *   The session will terminate automatically if all data has been sent.
 *
 * To cancel a running session, call userAborted() or use sendCancelMessage().
 * In most cases the application will terminate automatically after the contact ACK-ed the cancel message.
 * Otherwise, endApplication() needs to be called manually.
 *
 * When a data transfer is active, the methods showTransferMessage(), showTransferProgress(), showTransferComplete()
 * are called to inform the derived class of the status. It can update the GUI from these methods.
 *
 *
 * --- internals ----
 *
 * This class extends the P2PApplicationBase class, to implement the MSNSLP communication.
 * This is a session protocol which is transferred over the P2P payloads.
 * New messages are delivered by the base class though the gotNegotiationMessage() method.
 *
 * The exact contents of the P2P message is described in the P2PApplicationBase API documentation.
 * This class calls typically calls the sendSlpMessage() and the other <code>send..()</code> methods
 * to dispatch the messages.
 *
 * Some third party clients (including KMess 1.4.x) seam to send incorrect message fields.
 * This class is able to handle those not-entirely valid messages as well.
 * The situations where this happens are marked as "HACK" in the code.
 *
 * @author Diederik van der Boor
 * @ingroup Applications
 */
class P2PApplication : public P2PApplicationBase
{
  Q_OBJECT

  public: // public methods

    // The constructor
                           P2PApplication(ApplicationList *applicationList);

    // The destructor
    virtual               ~P2PApplication();

    // The contact cancelled the session
    virtual void           contactAborted(const QString &message = QString::null);
    // Returns the branch.
    QString                getBranch() const;
    // Returns the call ID (identifies the invitation).
    QString                getCallID() const;
    // Returns the nonce that's being expected.
    const QString &        getNonce() const;
    // Returns the session ID.
    quint32                getSessionID() const;
    // The user cancelled the session
    virtual void           userAborted();


  protected: // protected mehods

    // Step one of a contact-started chat: the contact invites the user
    virtual void           contactStarted1_ContactInvitesUser(const MimeMessage& message);
    // Step four of a contact-started chat: the contact confirms the data-preparation message.
    virtual void           contactStarted4_ContactConfirmsPreparation();
    // Return the content type read from the invitation message
    const QString&         getInvitationContentType() const;
    // Return the session id read from the invitation message
    quint32                getInvitationSessionID() const;
    // Called when data is received
    virtual void           gotData(const P2PMessage &message);
    // Called when all data is received, and the ack is sent.
    virtual void           gotDataComplete( const P2PMessage &lastMessage );
    // Called when an ack is received, which is not handled by the normal class.
    virtual bool           gotUnhandledAck( const P2PMessage &message, P2PMessageType ackedMessageType );
    // Send a cancel message
    void                   sendCancelMessage(const ApplicationCancelReason cancelReason);
    // Send the data preparation ACK.
    void                   sendDataPreparationAck();
    // Send the invitation for a normal session
    void                   sendSlpSessionInvitation( quint32 sessionID, const QString &eufGuid, const int appId, const QString &context );
    // Send the invitation for a direct connection.
    void                   sendSlpTransferInvitation();
    // Send an SLP 200/OK message
    void                   sendSlpOkMessage(const MimeMessage &message);
    // Assign a fixed session ID (used for p2p ink transfers)
    void                   setDataCastSessionID( quint32 sessionID );
    // The user rejected the invitation
    virtual void           userRejected();


  private: // private methods

    // Parse a ACK messsage (in certain situations we need to active some methods)
    void                   gotAck(const P2PMessage &message, const P2PMessageType messageType);
    // Called when the ACK for the data preparation was received.
    void                   gotAck_dataPreparation();
    // Called when the ACK for the sent file data was received.
    void                   gotAck_dataReceived();
    // Called when the ACK for the SLP BYE message was received.
    void                   gotAck_slpBye();
    // Called when the ACK for a SLP Error was received.
    void                   gotAck_slpError();
    // Called when the ACK for the first SLP INVITE message was received.
    void                   gotAck_slpSessionInvitation();
    // Called when the ACK of the SLP OK message was received.
    void                   gotAck_slpSessionOk();
    // Called when the ACK for the SLP transfer decline message was received.
    void                   gotAck_slpTransferDecline();
    // Called when the ACK for the SLP transfer INVITE message was received.
    void                   gotAck_slpTransferInvitation();
    // Called when the ACK for the SLP transfer OK mesages was received.
    void                   gotAck_slpTransferOk();
    // Called when the data preparation message is received
    void                   gotDataPreparation(const P2PMessage &message);
    // Got an direct connection handshake.
    void                   gotDirectConnectionHandshake(const P2PMessage &message);
    // Got a message with SessionID 0
    void                   gotNegotiationMessage(const MimeMessage &slpMessage, const QString &preamble);
    // Got an MSNSLP ACK message
    void                   gotSlpAck(const MimeMessage &slpMessage);
    // Got an MSNSLP BYE message
    void                   gotSlpBye(const MimeMessage &slpMessage);
    // Got an MSNSLP INVITE message
    void                   gotSlpInvite(const MimeMessage &slpMessage);
    // Got an MSNSLP 200 OK header
    void                   gotSlpOk(const MimeMessage &slpMessage);
    // Got an MSNSLP session invitation
    void                   gotSlpSessionInvitation(const MimeMessage &slpMimeBody);
    // Got an MSNSLP status header
    void                   gotSlpStatus(const MimeMessage &slpMessage, const QString &preamble);
    // Got an MSNSLP transfer invitation
    void                   gotSlpTransferInvitation(const MimeMessage &slpMimeBody);
    // Got an MSNSLP transfer invitation response
    void                   gotSlpTransferResponse(const MimeMessage &slpMimeBody, bool secondInvite = false);
    // Signal the derived class it can initiate the file transfer
    void                   initiateTransfer();
    // Send an SLP BYE message to close the session
    void                   sendSlpBye();
    // Send an SLP invitation message
    void                   sendSlpInvitation(const MimeMessage &message, const QString &contentType, P2PMessageType messageType);
    // Send an SLP error message (to decline an invitation for example)
    void                   sendSlpError( const QString &statusLine, const ulong sessionID = 0,
                                         const QString &messageContentType = 0,
                                         P2PMessageType messageType = P2P_MSG_SLP_ERROR );
    // Show a timeout message because an expected message was not received.
    void                   showTimeoutMessage( P2PWaitingState waitingState );


  private slots:
    // The direct connection is authorized.
    void                   slotConnectionAuthorized();
    // The direct connection is established.
    void                   slotConnectionEstablished();
    // The direct connection could not be made.
    void                   slotConnectionFailed();


  private: // private fields
    // The application list, parent of all application objects.
    ApplicationList       *applicationList_;
    // Branch ID, identifies the INVITE-message
    QString                branch_;
    // Call ID, identifies the session at MSNSLP level.
    QString                callID_;
    // True if we got an SLP message (requires a different error handling)
    bool                   gotSlpMessage_;
    // Content type from the invitation message
    QString                invitationContentType_;
    // CSeq field from the invitation message
    int                    invitationCSeq_;
    // SessionID field from the invitaiton message
    quint32                invitationSessionID_;
    // The nonce for direct connections
    QString                nonce_;
    // Session ID, identifies the session at MSNP2P level.
    quint32                sessionID_;
    // True if the user needs to acknowledge the data preparation message. (doesn't send an ACK automatically)
    bool                   userShouldAcknowledge_;
};

#endif
