/***************************************************************************
                          mimeapplication.cpp -  description
                             -------------------
    begin                : Thu Nov 23 2004
    copyright            : (C) 2003 by Mike K. Bennett
                           (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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "mimeapplication.h"

#include "../../kmessdebug.h"
#include "../mimemessage.h"

#include <KLocale>



// Constructor
MimeApplication::MimeApplication(const QString &contactHandle)
  : Application(contactHandle)
{
}



// Destructor
MimeApplication::~MimeApplication()
{
}



/**
 * @brief The contact aborted the session
 *
 * @param message Optional message to display, defaults to getContactAbortMessage().
 */
void MimeApplication::contactAborted(const QString &message)
{
#ifdef KMESSDEBUG_APPLICATION
  kmDebug();
#endif

  // Make sure a second message is not displayed here.
  // The contact could be sending multiple error messages.
  if( isClosing() )
  {
    kmWarning() << "Attempted to close application twice "
                  "(contact=" << getContactHandle() <<
                  " action=endapplication)";

    // Make sure the timer is started again.
    endApplication();
    return;
  }


  // Remove the accept links
  modifyOfferMessage();

  if( message.isEmpty() )
  {
    showEventMessage( getContactAbortMessage(), ChatMessage::CONTENT_APP_CANCELED, true );
  }
  else
  {
    showEventMessage( message, ChatMessage::CONTENT_APP_CANCELED, true );
  }

  endApplication();
}



// Generate a random session ID
QString MimeApplication::generateSessionId() const
{
  // The official client only changes the session ID if you quit the application.
  QString sessionId( "{6672F94C-45BF-11D7-B4AE-00010A1008DF}" );

#ifdef KMESSDEBUG_MIMEAPPLICATION
  kmDebug() << "Generated session ID " << sessionId << ".";
#endif

  return sessionId;
}



// Return the session ID, generating one if it doesn't exist
const QString& MimeApplication::getSessionId()
{
  if ( sessionId_.isEmpty() )
  {
    sessionId_ = generateSessionId();
  }
  return sessionId_;
}



// A message for the application was received
void MimeApplication::gotMessage(const MimeMessage& message)
{
#ifdef KMESSDEBUG_MIMEAPPLICATION
  kmDebug() << "An application message was received";
#endif

  QString invitationCommand( message.getValue("Invitation-Command") );

  if(invitationCommand == "INVITE")
  {
    // This is the start of a contact-started app.  Get the app's cookie
    QString cookie( message.getValue("Invitation-Cookie") );
    startByInvite(cookie);
    contactStarted1_ContactInvitesUser(message);
  }
  else if(invitationCommand == "ACCEPT")
  {
    // Call the next step of the process, depending on who started the app
    if(isUserStartedApp())
    {
      userStarted2_ContactAccepts(message); // Usually to send an ACCEPT message back.
      userStarted3_UserPrepares();          // Finally, start the application
    }
    else
    {
      contactStarted3_ContactConfirmsAccept(message);
    }
  }
  else if(invitationCommand == "CANCEL")
  {
    // The contact canceled the app.
    QString cancelCode( message.getValue("Cancel-Code") );

    if(cancelCode == "REJECT" )
    {
      // Used to "cancel" the invitation
      contactRejected();
    }
    else if(cancelCode == "REJECT_NOT_INSTALLED")
    {
      // Used if the application type is not supported
      contactRejected( i18n("The invitation was rejected. It is not supported by the other client.") );
    }
    else if(cancelCode == "CANCEL")
    {
      // Used to end the session prematurely
      contactAborted();
    }
    else if(cancelCode == "FAIL")
    {
      // The receiving client does not know any of the specified Session-protocols
      contactAborted( i18n("The invitation was aborted. An internal error occured.") );
    }
    else if(cancelCode == "TIMEOUT")
    {
      // The client sending an INVITE has got bored of waiting for your ACCEPT
      // (or the principal cancelled the request)
      contactAborted( i18n("The invitation was aborted. Timeout while waiting for user to accept.") );
    }
    else if(cancelCode == "FTTIMEOUT")
    {
      // There was an error transferring the file itself
      contactAborted( i18n("The invitation was aborted. Timeout while waiting for file data.") );
    }
    else if(cancelCode == "OUTBANDCANCEL")
    {
      // The switchboard window in which the INVITE message was sent is closing
      contactAborted( i18n("The invitation was aborted. The switchboard closed the chat connection.") );
    }
    else
    {
      // Fallback
      kmDebug() << "got unhandled Cancel-Code " << cancelCode << ".";
      contactAborted( i18n("The invitation was aborted.") );
    }
  }
  else
  {
    kmDebug() << "got unhandled Invitation-Command " << invitationCommand << ".";
  }
}



// Send a message to the contact
void MimeApplication::sendMessage(const MimeMessage& message)
{
  emit putMsg(this, message);
}



// Send a cancel message and terminate the application.
void MimeApplication::sendCancelMessage(const ApplicationCancelReason cancelReason)
{
  MimeMessage message;
  QString cancelCode;

  if(cancelReason == CANCEL_INVITATION)
  {
    cancelCode = "REJECT";
  }
  else if(cancelReason == CANCEL_NOT_INSTALLED)
  {
    cancelCode = "REJECT_NOT_INSTALLED";
  }
  else if(cancelReason == CANCEL_ABORT)
  {
    cancelCode = "CANCEL";
  }
  else if(cancelReason == CANCEL_TIMEOUT)
  {
    cancelCode = "TIMEOUT"; // TODO: what about "FTTIMEOUT"?
  }
  else if(cancelReason == CANCEL_FAILED)
  {
    cancelCode = "FAIL";
  }
  else
  {
#ifdef KMESSDEBUG_MIMEAPPLICATION
      kmWarning() << "Unknown cancelReason used.";
#endif
      cancelCode = "CANCEL";
  }

  message.addField( "Invitation-Command", "CANCEL"    );
  message.addField( "Invitation-Cookie",  getCookie() );
  message.addField( "Cancel-Code",        cancelCode  );

  emit putMsg(this, message);

  // Terminate the application automatically.
  // Makes the behavour more consistent with the P2P applications as well
  // (which also don't bother about terminating the application)
  endApplication();
}


/**
 * @brief Called when the user aborted the application.
 *
 * Displays a message using getUserAbortMessage() and notifies the contact.
 *
 * This method may be overwritten to add additional event handling.
 */
void MimeApplication::userAborted()
{
#ifdef KMESSDEBUG_MIMEAPPLICATION
  kmDebug() << "user requests to abort the session.";
#endif

  // Make sure a second message is not displayed here.
  if( isClosing() )
  {
    kmWarning() << "Attempted to close application twice "
                  " contact=" << getContactHandle() <<
                  " class="   << metaObject()->className() <<
                  " action=endapplication)";

    endApplication();
    return;
  }

  // Display abort message
  modifyOfferMessage();
  showEventMessage( getUserAbortMessage(), ChatMessage::CONTENT_APP_CANCELED, false );

  // Send cancel message.
  // Prepares the application to abort.
  sendCancelMessage( CANCEL_ABORT );

  // Set the state to avoid crashes.
  setClosing( true );
}



/**
 * @brief Called when the user rejected (declined) the application.
 *
 * Displays a message using getUserRejectMessage() and notifies the contact.
 */
void MimeApplication::userRejected()
{
  if( isClosing() )
  {
#ifdef KMESSDEBUG_MIMEAPPLICATION
    kmWarning() << "Attempted to close application twice "
                  " contact=" << getContactHandle() <<
                  " class="   << metaObject()->className() <<
                  " action=endapplication)";
#endif

    endApplication();
    return;
  }

#ifdef KMESSDEBUG_MIMEAPPLICATION
  kmDebug() << "user requests to reject the invitation.";
#endif

  // Send abort messages
  modifyOfferMessage();
  showEventMessage( getUserRejectMessage(), ChatMessage::CONTENT_APP_CANCELED, false );
  sendCancelMessage( CANCEL_INVITATION );

  // Set the state to avoid crashes.
  setClosing( true );
}



#include "mimeapplication.moc"
