#include "mmsmessage.hh"
#include <QDataStream>
#include <QDebug>
#include "types.h"
#include "qwsppdu.h"
#include "private.hh"
#include "mmspart.hh"
#include <QCryptographicHash>

MmsMessage::MmsMessage(const MessageType& type) {
  m_version = "1.0";
  m_type = type;
}

MmsMessage::MmsMessage(QIODevice *in) {
  m_version = "1.0";

  QWspPduDecoder d(in);

  if (!decodeHeader(d)) {
    m_type = Invalid;
    return;
  }

  //  quint8 pdu = d.decodeOctet();
  //  qDebug() << pdu;

  QWspMultipart p = d.decodeMultipart();
  for (int x = 0; x < p.count(); x++) {
    m_parts.append(MmsPart(p.part(x)));
  }
}

bool MmsMessage::decodeHeader(QWspPduDecoder& d) {
  while (!d.device()->atEnd()) {
    quint8 pdu = d.decodeOctet();

    switch (pdu) {
    case MM_MTYPE_HDR:
      qDebug() << "MM_MTYPE_HDR";
      m_type = getMessageType(d);
      break;
    case MM_TID_HDR:
      qDebug() << "MM_TID_HDR";
      m_transactionId = d.decodeTextString();
      break;
    case MM_VERSION_HDR:
      qDebug() << "MM_VERSION_HDR";
      m_version = d.decodeVersion();
      break;
    case MM_FROM_HDR:
      qDebug() << "MM_FROM_HDR";
      m_from = getFrom(d);
      break;
    case MM_TO_HDR:
      qDebug() << "MM_TO_HDR";
      m_to.append(getAddr(d));
      break;
    case MM_CTYPE_HDR:
      qDebug() << "MM_CTYPE_HDR";
      m_contentType = d.decodeContentType();
      // This should be the last of the headers.
      return true;
    case MM_SUBJECT_HDR:
      qDebug() << "MM_SUBJECT_HDR";
      m_subject = d.decodeEncodedString();
      break;
    default:
      qDebug() << "Unknown PDU" << QString("0x%1").arg(QString::number((unsigned int)pdu, 16));
      return false;
    }
  }

  return false;
}

QList<MmsPart> MmsMessage::parts() {
  return m_parts;
}

void MmsMessage::addPart(const MmsPart& part) {
  m_parts.append(part);
}

void MmsMessage::setContentRelated(bool related) {
  if (related) {
    m_contentType = "application/vnd.wap.multipart.related";
  }
  else {
    m_contentType = "application/vnd.wap.multipart.mixed";
  }
}

bool MmsMessage::encode(QIODevice *out) {
  if (!isValid()) {
    qWarning() << "MmsMessage::encode invalid message";
    return false;
  }

  if (m_contentType.isEmpty()) {
    qWarning() << "MmsMessage::encode no content type.";
    return false;
  }

  if (m_parts.isEmpty()) {
    qWarning() << "MmsMessage::encode No parts added.";
    return false;
  }

  if (isContentRelated()) {
    bool hasSmil = false;

    for (int x = 0; x < m_parts.size(); x++) {
      bool smil = m_parts.at(x).isSmil();
      if (smil) {
	hasSmil = smil;
	break;
      }
    }

    if (!hasSmil) {
      qWarning() << "MmsMessage::encode related content without SMIL";
      return false;
    }
  }

  if (m_transactionId.isEmpty()) {
    //    m_transactionId = "ABC";
    QByteArray data;
    for (int x = 0; x < m_parts.size(); x++) {
      data.append(m_parts.at(x).data());
    }

    if (data.isEmpty()) {
      qWarning() << "MmsMessage::encode No data.";
      return false;
    }

    QCryptographicHash h(QCryptographicHash::Md5);
    h.addData(data);
    QByteArray r = h.result();

    m_transactionId = r.toHex();
  }

  QWspPduEncoder e(out);

  if (!encodeHeader(e)) {
    return false;
  }

  //  int smil = -1
  // TODO: encode smil 1st ?

  e.encodeUIntVar(m_parts.size());
  for (int x = 0; x < m_parts.size(); x++) {
    m_parts.at(x).encode(e);
  }

  return true;
}

bool MmsMessage::encodeHeader(QWspPduEncoder& e) {
  // X-Mms-Message-Type
  e.encodeOctet(MM_MTYPE_HDR);
  setMessageType(e, m_type);

  // X-Mms-Transaction-ID
  e.encodeOctet(MM_TID_HDR);
  e.encodeTextString(m_transactionId);

  // X-Mms-MMS-Version
  e.encodeOctet(MM_VERSION_HDR);
  e.encodeVersion(m_version);

  // Now the rest:
  e.encodeOctet(MM_FROM_HDR);
  ::setFrom(e, m_from);

  for (int x = 0; x < m_to.size(); x++) {
    e.encodeOctet(MM_TO_HDR);
    setAddr(e, m_to.at(x));
  }

  if (!m_subject.isEmpty()) {
    e.encodeOctet(MM_SUBJECT_HDR);
    e.encodeEncodedString(m_subject);
  }

  for (int x = 0; x < m_cc.size(); x++) {
    e.encodeOctet(MM_CC_HDR);
    setAddr(e, m_cc.at(x));
  }

  for (int x = 0; x < m_bcc.size(); x++) {
    e.encodeOctet(MM_BCC_HDR);
    setAddr(e, m_bcc.at(x));
  }

  // Last:
  e.encodeOctet(MM_CTYPE_HDR);
  e.encodeContentType(m_contentType);

  return true;
}

#include <QFile>
#include <QCoreApplication>
#include <QBuffer>
int main(int argc, char *argv[]) {
  QCoreApplication app(argc, argv);
  //#if 0
  QStringList args = app.arguments();
  if (args.size() != 5) {
    qWarning() << "Usage" << args.at(0) << "<to> <file> <out> <subject>";
    return 0;
  }

  QString to = args.at(1);
  QString out = args.at(3);
  QString subject = args.at(4);

  QString type;
  if (args.at(2).endsWith(".jpg") || args.at(2).endsWith(".jpeg")) {
    type = "image/jpeg";
  }
  else if (args.at(2).endsWith(".gif")) {
    type = "image/gif";
  }
  else if (args.at(2).endsWith(".png")) {
    type = "image/png";
  }
  else if (args.at(2).endsWith(".txt") || args.at(2).endsWith(".text")) {
    type = "text/plain";
  }
  else {
    qWarning() << "Only JPEG, PNG, GIF and TXT are supported";
    return 1;
  }

  QFile file(args.at(2));
  if (!file.open(QFile::ReadOnly)) {
    qWarning() << "Failed to open" << args.at(2) << file.errorString();
    return 1;
  }

  MmsMessage m = MmsMessage(MmsMessage::SendReq);
  m.setTo(QStringList() << to);
  m.setSubject(subject);
  m.setContentRelated(false);

  MmsPart p(type, file.readAll());
  m.addPart(p);

  QFile o(out);
  if (!o.open(QFile::WriteOnly | QFile::Truncate)) {
    qWarning() << "Failed to open" << out << o.errorString();
    return 1;
  }

  m.encode(&o);

  o.flush();
  o.close();

  if (o.error() != QFile::NoError) {
    qWarning() << "Failed to write" << out << o.errorString();
    return 1;
  }

  return 0;
  //#endif

#if 0
  QFile f(app.arguments().at(1));

  if (!f.open(QFile::ReadOnly)) {
    qDebug() << f.errorString();
    return 1;
  }

  MmsMessage m(&f);
  //#if 0
  qDebug() << m.isValid() << m.type() << m.transactionId() << m.version() << m.from();
  qDebug() << m.to() << m.cc() << m.bcc() << m.subject() << m.contentType() << m.isContentRelated();

  QList<MmsPart> parts = m.parts();
  for (int x = 0; x < parts.size(); x++) {
    qDebug() << "Part" << x;
    qDebug() << parts.at(x).contentType() << parts.at(x).charset();
    qDebug() << parts.at(x).isText() << parts.at(x).isSmil() << parts.at(x).suggestedName();

    if (parts.at(x).isText() || parts.at(x).isSmil()) {
      qDebug() << parts.at(x).data();
    }
  }
  //#endif
  //#endif

  QFile o("lol");
  o.open(QFile::WriteOnly | QFile::Truncate);

  //  m.setTo(QStringList() << "");

  if (!m.encode(&o)) {
    qDebug() << "Failed to encode :(";
  }
#endif
  return 0;
}
