/***************************************************************************
 *                                                                         *
 *   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 "p2pmessage.h"

#include <qcstring.h>
#include <kdebug.h>

P2PMessage::P2PMessage()
  : MimeMessage()
{

}


// ..once KMess discovers the mime message is highly more advanced (a p2p message),
// the message will be transformed here into a p2pmessage. I use the copy constructor
// of mime-message for that purpose.
P2PMessage::P2PMessage(const MimeMessage &mimeMessage)
  : MimeMessage(mimeMessage)
{
  parseP2PHeader();
}



// Retreives the current session ID.
unsigned long P2PMessage::getSessionID() const
{
  return sessionID_;
}

// Retreives the current message ID. */
unsigned long P2PMessage::getMessageID() const
{
  return messageID_;
}

// Retreives the current message offset.
unsigned long P2PMessage::getDataOffset() const
{
  return dataOffset_;
}

// Retreives the current message size.
unsigned long P2PMessage::getDataSize() const
{
  return dataSize_;
}

// Retreives the total message size.
unsigned long P2PMessage::getTotalSize() const
{
  return totalSize_;
}

// Retreives the current message size.
unsigned long P2PMessage::getFlags() const
{
  return flags_;
}

// Retrieves the ack session ID field.
unsigned long P2PMessage::getAckMessageID() const
{
  return ackMessageID_;
}

// Retreives the ack unique ID field.
unsigned long P2PMessage::getAckUniqueID() const
{
  return ackUniqueID_;
}

// Retreives the ack data size field.
unsigned long P2PMessage::getAckDataSize() const
{
  return ackDataSize_;
}



// Return a pointer to the data.
char *P2PMessage::getData() const
{
  const QByteArray &dataRef = getBinaryData(); // API Check ;-), would fail if the base
  return (dataRef.data() + 48);                // class decides not to return a reference.
}



// The message is an ACK.
bool P2PMessage::isAck() const
{
  return (flags_ & MSN_FLAG_ACK) == MSN_FLAG_ACK;
}



// Waiting for reply
bool P2PMessage::isWaiting() const
{
  return (flags_ & MSN_FLAG_WAITING) == MSN_FLAG_WAITING;
}



// Return true if the error flag was set.
bool P2PMessage::isError() const
{
  return (flags_ & MSN_FLAG_ERROR) == MSN_FLAG_ERROR;
}


// Return true if this is picture data
bool P2PMessage::isPictureData() const
{
  // Used the MSN_FLAG_FILETRANSFER mask here on purpose,
  // I want to be sure only the picture data flag is set here.
  return (flags_ & MSN_FLAG_FILETRANSFER) == MSN_FLAG_DATA;
}



// Client received all data, and requests BYE message
bool P2PMessage::isClosingAck() const
{
  return (flags_ & MSN_FLAG_CLOSING_ACK) == MSN_FLAG_CLOSING_ACK;
}



// He who sent BYE
bool P2PMessage::isSentByeAck() const
{
  return (flags_ & MSN_FLAG_BYE_SENT) == MSN_FLAG_BYE_SENT;
}



// Set if the message contains file data
bool P2PMessage::isFileData() const
{
  return (flags_ & MSN_FLAG_FILETRANSFER) == MSN_FLAG_FILETRANSFER;
}



// Return true if this is an fragment (splitted message packet).
bool P2PMessage::isFragment() const
{
  return (dataSize_ < totalSize_);
}



// Returns true if the message size exceeds the total size
bool P2PMessage::isLastFragment() const
{
  // offset + this message size >= the total size.
  return (dataOffset_ + dataSize_) >= totalSize_;
}



// Parse the binary P2P header bytes
void P2PMessage::parseP2PHeader()
{
  char *bodyData = getBinaryData().data();

  if(!bodyData)
  {
    // bodyData may be NULL
    kdDebug() << "P2PMessage - WARNING - P2P message has no data set!" << endl;
    return;
  }

  // Header:
  //
  // 0    4    8        16        24   28   32   36   40        48
  // |....|....|....|....|....|....|....|....|....|....|....|....|
  // |sid |mid |offset   |totalsize|size|flag|asid|auid|a-datasz |
  //
  // More info can be found at http://siebe.bot2k3.net/docs/

  sessionID_       = extractBytes     ( bodyData,  0);
  messageID_       = extractBytes     ( bodyData,  4);
  dataOffset_      = extractLongBytes ( bodyData,  8);
  totalSize_       = extractLongBytes ( bodyData, 16);
  dataSize_        = extractBytes     ( bodyData, 24);
  flags_           = extractBytes     ( bodyData, 28);
  ackMessageID_    = extractBytes     ( bodyData, 32);
  ackUniqueID_     = extractBytes     ( bodyData, 36);
  ackDataSize_     = extractLongBytes ( bodyData, 40);
}





// ------------------------------------------------------
// A utility to fill the byte data block

void P2PMessage::insertBytes(QByteArray &buffer, const unsigned int value, const int offset)
{
  // Also based on Kopete code. I started with a nice struct,
  // but not all c++ compilers use the same data size for the fields:
  /*
  * "The only restrictions that the language spec places are that
  *  (1) a char is one byte,
  *  (2) char <= short int <= int <= long int <= long long int.
  *  The exact size is completely compiler dependent."
  *
   * gcc on i686-pc-linux-gnu gives these results:
  * - sizeof(int)           = 4
  * - sizeof(long int)      = 4
  * - sizeof(long long int) = 8
  * this beats me.
  *
  * In other words, I'll use bitwise shifts to set bytes manually.
  */

  buffer[offset + 0] = (char) ( value        & 0xFF);
  buffer[offset + 1] = (char) ((value >>  8) & 0xFF);
  buffer[offset + 2] = (char) ((value >> 16) & 0xFF);
  buffer[offset + 3] = (char) ((value >> 24) & 0xFF);
  // I think it's obvious we're copying every byte individually here..
  // However, note the bytes are placed in network order. (big endian)
}

void P2PMessage::insertShortBytes(QByteArray &buffer, const unsigned short value, const int offset)
{
  buffer[offset + 0] = (char) ( value        & 0xFF);
  buffer[offset + 1] = (char) ((value >>  8) & 0xFF);
}

unsigned int P2PMessage::extractBytes(const char *data, const int offset)
{
  // Convert the bytes from network order to a normal int.
  return (((unsigned char) data[offset + 0]      )
        | ((unsigned char) data[offset + 1] <<  8)
        | ((unsigned char) data[offset + 2] << 16)
        | ((unsigned char) data[offset + 3] << 24));
}

unsigned long P2PMessage::extractLongBytes(const char *data, const int offset)
{
  // Convert the bytes from network order to a long int.
  return (((unsigned char) data[offset + 0]      )
        | ((unsigned char) data[offset + 1] <<  8)
        | ((unsigned char) data[offset + 2] << 16)
        | ((unsigned char) data[offset + 3] << 24));

        // FIXME: gcc complains that the shift width is larger then the actual data type supports..
        //        as long as the msn servers don't send huge messages, we don't have much to worry..
//        | ((unsigned char) data[offset + 4] << 32)
//        | ((unsigned char) data[offset + 5] << 40)
//        | ((unsigned char) data[offset + 6] << 48)
//        | ((unsigned char) data[offset + 7] << 56));
}

#include "p2pmessage.moc"
