/***************************************************************************
                          inktransferp2p.cpp -  description
                             -------------------
    begin                : Sun Mar 23 2008
    copyright            : (C) 2008 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 "inktransferp2p.h"

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

#include <KLocalizedString>



/**
 * The constructor
 */
InkTransferP2P::InkTransferP2P( ApplicationList *applicationList )
: P2PApplication( applicationList )
{
  // Open the buffer directly.
  // The session is only used to receive data.
  inkBuffer_.open( QIODevice::ReadWrite );

  // Set the role / session mode of this class.
  // Is checked for by the ChatMaster, and P2PApplication base class.
  setMode( Application::APP_MODE_DATACAST );
  setDataCastSessionID( SESSION_ID );
}



/**
 * The destructor
 */
InkTransferP2P::~InkTransferP2P()
{
}



/**
 * Called when data is received.
 *
 * This is the only method which will be called.
 * Ink transfer only uses the P2P transport layer to transmit data packets.
 *
 * @param  message  P2P message with the data.
 */
void InkTransferP2P::gotData(const P2PMessage &message)
{
  kmDebug() << "Received data...";
  // Write the data in the buffer.
  bool success = writeP2PDataToFile( message, &inkBuffer_ );
  if( ! success )
  {
    return;
  }

  // When all data is received, the parent class calls showTransferComplete().
  // That method will send the msnObjectReceived() signal.
}



/**
 * Hide standard informative application message (e.g. user invited, cancelled).
 *
 * Avoid annoying messages in the chat windows about "Contact sent something KMess does not support".
 * This is useful for interactive invitations, like file transfer. Since msnobject transfer happen
 * in the background, it's not helping to have empty chat windows popping up with error messages.
 */
void InkTransferP2P::showEventMessage(const QString &message, const ChatMessage::ContentsClass contents, bool isIncoming )
{
  Q_UNUSED( isIncoming );

  kmWarning() << "suppressed message:" << message << "(contact=" << getContactHandle() << ", contentsClass=" << contents << ")";
}



/**
 * Show a message to notify about a system error.
 *
 * Notify if an Ink message could not be sent or received.
 */
void InkTransferP2P::showSystemMessage( const QString &message, const ChatMessage::ContentsClass contents, bool isIncoming )
{
  Q_UNUSED( message );

  QString errorMessage;

  if( isIncoming )
  {
    errorMessage = i18nc( "Error message shown in chat, %1 is the contact's friendly name",
                          "<html>%1 has tried to send you an handwritten message, "
                          "but it could not be received.</html>" );
  }
  else
  {
    errorMessage = i18nc( "Error message shown in chat, %1 is the contact's friendly name",
                          "<html>The handwritten message to %1 could not be delivered.</html>" );
  }

  emit applicationMessage( ChatMessage( ChatMessage::TYPE_SYSTEM, contents, isIncoming, errorMessage, getContactHandle() ) );
}



/**
 * Called when the transfer is complete.
 * Reads the buffer, calls the inkReceived() signal.
 */
void InkTransferP2P::showTransferComplete()
{
#ifdef KMESSDEBUG_MSNOBJECTTRANSFER_P2P
  kmDebug() << "Last data part received, emitting signals";
#endif

  // Read the complete string. It's transmitted in multi byte format.
  const QByteArray &bufferDataUcs2 = inkBuffer_.data();
  QString bufferData( QString::fromUtf16( reinterpret_cast<const ushort*>( bufferDataUcs2.data() ), bufferDataUcs2.size() / 2 ) );

  // When a MimeMessage is sent over P2P,
  // there is a null-terminator after the head and body parts.
  int endMime = bufferData.indexOf("\r\n\r\n");
  if( endMime == -1 )
  {
    kmWarning() << "Failed to find end of MIME header:" << bufferData;
    return;
  }

  // Get the ink header.
  InkFormat format;
  MimeMessage mimeHeader( bufferData.left( endMime ) );

  // Test header.
  if( mimeHeader.getValue( "Content-Type" ) == "image/gif" )
  {
    format = FORMAT_GIF;
  }
  else if( mimeHeader.getValue( "Content-Type" ) == "application/x-ms-ink" )
  {
    format = FORMAT_ISF;
  }
  else
  {
    kmWarning() << "Unable to parse incoming ink from" << getContactHandle()
               << "with mime-type:" << mimeHeader.getValue( "Content-Type" );
    mimeHeader.print();
    return;
  }

  // Jump over the null terminator
  int bodyStart = endMime + 4;
  if( bufferData.at( bodyStart ) == '\0' )
  {
    bodyStart++;
  }

  // Avoid final null terminator
  int bodyEnd = bufferData.size();
  if( bufferData.at( bodyEnd - 1 ) == '\0' )
  {
    bodyEnd--;
  }

  // Get body
  QString mimeBody( bufferData.mid( bodyStart, bodyEnd - bodyStart ) );

  // Emit the signal.
  emit inkReceived( mimeBody, getContactHandle(), format );

  // ACK message will automatically be sent by the base class.
  // The ChatMaster class parses and validates the ink mime body.
  // Other issues (like bad message flags) are also handled.

  // Request termination
  endApplication();
}


#include "inktransferp2p.moc"
