/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/mailnews/MimeHeaderParser.h"
#include "mozilla/DebugOnly.h"
#include "nsMemory.h"
#include "nsCOMPtr.h"
#include "nsIMsgHeaderParser.h"
#include "mozilla/Components.h"

namespace mozilla {
namespace mailnews {

void detail::DoConversion(const nsTArray<nsString>& aUTF16Array,
                          nsTArray<nsCString>& aUTF8Array) {
  uint32_t count = aUTF16Array.Length();
  aUTF8Array.SetLength(count);
  for (uint32_t i = 0; i < count; ++i)
    CopyUTF16toUTF8(aUTF16Array[i], aUTF8Array[i]);
}

void MakeMimeAddress(const nsACString& aName, const nsACString& aEmail,
                     nsACString& full) {
  nsAutoString utf16Address;
  MakeMimeAddress(NS_ConvertUTF8toUTF16(aName), NS_ConvertUTF8toUTF16(aEmail),
                  utf16Address);

  CopyUTF16toUTF8(utf16Address, full);
}

void MakeMimeAddress(const nsAString& aName, const nsAString& aEmail,
                     nsAString& full) {
  nsCOMPtr<nsIMsgHeaderParser> headerParser(
      components::HeaderParser::Service());

  nsCOMPtr<msgIAddressObject> address;
  headerParser->MakeMailboxObject(aName, aEmail, getter_AddRefs(address));
  nsTArray<RefPtr<msgIAddressObject>> addresses;
  addresses.AppendElement(address);
  headerParser->MakeMimeHeader(addresses, full);
}

void MakeDisplayAddress(const nsAString& aName, const nsAString& aEmail,
                        nsAString& full) {
  nsCOMPtr<nsIMsgHeaderParser> headerParser(
      components::HeaderParser::Service());

  nsCOMPtr<msgIAddressObject> object;
  headerParser->MakeMailboxObject(aName, aEmail, getter_AddRefs(object));
  object->ToString(full);
}

void RemoveDuplicateAddresses(const nsACString& aHeader,
                              const nsACString& aOtherEmails,
                              nsACString& result) {
  nsCOMPtr<nsIMsgHeaderParser> headerParser(
      components::HeaderParser::Service());

  headerParser->RemoveDuplicateAddresses(aHeader, aOtherEmails, result);
}

/////////////////////////////////////////////
// These are the core shim methods we need //
/////////////////////////////////////////////

nsCOMArray<msgIAddressObject> DecodedHeader(const nsAString& aHeader) {
  nsCOMArray<msgIAddressObject> retval;
  if (aHeader.IsEmpty()) {
    return retval;
  }
  nsCOMPtr<nsIMsgHeaderParser> headerParser(
      components::HeaderParser::Service());
  NS_ENSURE_TRUE(headerParser, retval);
  nsTArray<RefPtr<msgIAddressObject>> addresses;
  nsresult rv = headerParser->ParseDecodedHeader(aHeader, false, addresses);
  MOZ_ASSERT(NS_SUCCEEDED(rv), "Javascript jsmime returned an error!");
  if (NS_SUCCEEDED(rv) && addresses.Length() > 0) {
    retval.SetCapacity(addresses.Length());
    for (auto& addr : addresses) {
      retval.AppendElement(addr);
    }
  }
  return retval;
}

nsCOMArray<msgIAddressObject> EncodedHeader(const nsACString& aHeader,
                                            const char* aCharset) {
  nsCOMArray<msgIAddressObject> retval;
  if (aHeader.IsEmpty()) {
    return retval;
  }
  nsCOMPtr<nsIMsgHeaderParser> headerParser(
      components::HeaderParser::Service());
  NS_ENSURE_TRUE(headerParser, retval);
  nsTArray<RefPtr<msgIAddressObject>> addresses;
  nsresult rv =
      headerParser->ParseEncodedHeader(aHeader, aCharset, false, addresses);
  MOZ_ASSERT(NS_SUCCEEDED(rv), "This should never fail!");
  if (NS_SUCCEEDED(rv) && addresses.Length() > 0) {
    retval.SetCapacity(addresses.Length());
    for (auto& addr : addresses) {
      retval.AppendElement(addr);
    }
  }
  return retval;
}

nsCOMArray<msgIAddressObject> EncodedHeaderW(const nsAString& aHeader) {
  nsCOMArray<msgIAddressObject> retval;
  if (aHeader.IsEmpty()) {
    return retval;
  }
  nsCOMPtr<nsIMsgHeaderParser> headerParser(
      components::HeaderParser::Service());
  NS_ENSURE_TRUE(headerParser, retval);
  nsTArray<RefPtr<msgIAddressObject>> addresses;
  nsresult rv = headerParser->ParseEncodedHeaderW(aHeader, addresses);
  MOZ_ASSERT(NS_SUCCEEDED(rv), "This should never fail!");
  if (NS_SUCCEEDED(rv) && addresses.Length() > 0) {
    retval.SetCapacity(addresses.Length());
    for (auto& addr : addresses) {
      retval.AppendElement(addr);
    }
  }
  return retval;
}

void ExtractAllAddresses(const nsCOMArray<msgIAddressObject>& aHeader,
                         nsTArray<nsString>& names,
                         nsTArray<nsString>& emails) {
  uint32_t count = aHeader.Length();

  // Prefill arrays before we start
  names.SetLength(count);
  emails.SetLength(count);

  for (uint32_t i = 0; i < count; i++) {
    aHeader[i]->GetName(names[i]);
    aHeader[i]->GetEmail(emails[i]);
  }

  if (count == 1 && names[0].IsEmpty() && emails[0].IsEmpty()) {
    names.Clear();
    emails.Clear();
  }
}

void ExtractDisplayAddresses(const nsCOMArray<msgIAddressObject>& aHeader,
                             nsTArray<nsString>& displayAddrs) {
  uint32_t count = aHeader.Length();

  displayAddrs.SetLength(count);
  for (uint32_t i = 0; i < count; i++) aHeader[i]->ToString(displayAddrs[i]);

  if (count == 1 && displayAddrs[0].IsEmpty()) displayAddrs.Clear();
}

/////////////////////////////////////////////////
// All of these are based on the above methods //
/////////////////////////////////////////////////

void ExtractEmails(const nsCOMArray<msgIAddressObject>& aHeader,
                   nsTArray<nsString>& emails) {
  nsTArray<nsString> names;
  ExtractAllAddresses(aHeader, names, emails);
}

void ExtractEmail(const nsCOMArray<msgIAddressObject>& aHeader,
                  nsACString& email) {
  AutoTArray<nsString, 1> names;
  AutoTArray<nsString, 1> emails;
  ExtractAllAddresses(aHeader, names, emails);

  if (emails.Length() > 0)
    CopyUTF16toUTF8(emails[0], email);
  else
    email.Truncate();
}

void ExtractFirstAddress(const nsCOMArray<msgIAddressObject>& aHeader,
                         nsACString& name, nsACString& email) {
  AutoTArray<nsString, 1> names, emails;
  ExtractAllAddresses(aHeader, names, emails);
  if (names.Length() > 0) {
    CopyUTF16toUTF8(names[0], name);
    CopyUTF16toUTF8(emails[0], email);
  } else {
    name.Truncate();
    email.Truncate();
  }
}

void ExtractFirstAddress(const nsCOMArray<msgIAddressObject>& aHeader,
                         nsAString& name, nsACString& email) {
  AutoTArray<nsString, 1> names, emails;
  ExtractAllAddresses(aHeader, names, emails);
  if (names.Length() > 0) {
    name = names[0];
    CopyUTF16toUTF8(emails[0], email);
  } else {
    name.Truncate();
    email.Truncate();
  }
}

void ExtractName(const nsCOMArray<msgIAddressObject>& aHeader,
                 nsACString& name) {
  nsCString email;
  ExtractFirstAddress(aHeader, name, email);
  if (name.IsEmpty()) name = email;
}

void ExtractName(const nsCOMArray<msgIAddressObject>& aHeader,
                 nsAString& name) {
  AutoTArray<nsString, 1> names;
  AutoTArray<nsString, 1> emails;
  ExtractAllAddresses(aHeader, names, emails);
  if (names.Length() > 0) {
    if (names[0].IsEmpty())
      name = emails[0];
    else
      name = names[0];
  } else {
    name.Truncate();
  }
}

}  // namespace mailnews
}  // namespace mozilla
