/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "prprf.h"
#include "plstr.h"
#include "nsCOMPtr.h"
#include "nsMsgUtils.h"
#include "nsIImportService.h"
#include "nsIImportAddressBooks.h"
#include "nsIImportGeneric.h"
#include "nsISupportsPrimitives.h"
#include "nsIImportABDescriptor.h"
#include "nsIImportFieldMap.h"
#include "nsStringGlue.h"
#include "nsIFile.h"
#include "nsIAddrDatabase.h"
#include "nsIAbManager.h"
#include "nsIAbLDIFService.h"
#include "nsAbBaseCID.h"
#include "nsIStringBundle.h"
#include "nsImportStringBundle.h"
#include "nsTextFormatter.h"
#include "nsServiceManagerUtils.h"
#include "msgCore.h"
#include "ImportDebug.h"
#include "nsIAbMDBDirectory.h"
#include "nsComponentManagerUtils.h"
#include "nsIArray.h"
#include "nsCOMArray.h"
#include "nsArrayUtils.h"

static void ImportAddressThread(void *stuff);

class AddressThreadData;

class nsImportGenericAddressBooks : public nsIImportGeneric
{
public:

  nsImportGenericAddressBooks();

  NS_DECL_THREADSAFE_ISUPPORTS

  /* nsISupports GetData (in string dataId); */
  NS_IMETHOD GetData(const char *dataId, nsISupports **_retval) override;

  NS_IMETHOD SetData(const char *dataId, nsISupports *pData) override;

  NS_IMETHOD GetStatus(const char *statusKind, int32_t *_retval) override;

  NS_IMETHOD WantsProgress(bool *_retval) override;

  NS_IMETHOD BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) override;

  NS_IMETHOD ContinueImport(bool *_retval) override;

  NS_IMETHOD GetProgress(int32_t *_retval) override;

  NS_IMETHOD CancelImport(void) override;

private:
  virtual ~nsImportGenericAddressBooks();
  void  GetDefaultLocation(void);
  void  GetDefaultBooks(void);
  void  GetDefaultFieldMap(void);

public:
  static void  SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError);
  static void ReportError(const char16_t *pName, nsString *pStream,
                          nsIStringBundle *aBundle);

private:
  nsIImportAddressBooks *    m_pInterface;
  nsCOMPtr<nsIArray> m_Books;
  nsCOMArray<nsIAddrDatabase> m_DBs;
  nsCOMPtr <nsIFile>              m_pLocation;
  nsIImportFieldMap *      m_pFieldMap;
  bool              m_autoFind;
  char16_t *          m_description;
  bool              m_gotLocation;
  bool              m_found;
  bool              m_userVerify;
  nsISupportsString *    m_pSuccessLog;
  nsISupportsString *    m_pErrorLog;
  uint32_t          m_totalSize;
  bool              m_doImport;
  AddressThreadData *      m_pThreadData;
  char *            m_pDestinationUri;
  nsCOMPtr<nsIStringBundle>   m_stringBundle;
};

class AddressThreadData {
public:
  bool              driverAlive;
  bool              threadAlive;
  bool              abort;
  bool              fatalError;
  uint32_t          currentTotal;
  uint32_t          currentSize;
  nsIArray         *books;
  nsCOMArray<nsIAddrDatabase>* dBs;
  nsCOMPtr<nsIAbLDIFService> ldifService;
  nsIImportAddressBooks *    addressImport;
  nsIImportFieldMap *      fieldMap;
  nsISupportsString *    successLog;
  nsISupportsString *    errorLog;
  char *            pDestinationUri;
    nsIStringBundle*            stringBundle;

  AddressThreadData();
  ~AddressThreadData();
};


nsresult NS_NewGenericAddressBooks(nsIImportGeneric** aImportGeneric)
{
    NS_PRECONDITION(aImportGeneric != nullptr, "null ptr");
    if (! aImportGeneric)
        return NS_ERROR_NULL_POINTER;

  nsImportGenericAddressBooks *pGen = new nsImportGenericAddressBooks();

  if (pGen == nullptr)
    return NS_ERROR_OUT_OF_MEMORY;

  NS_ADDREF(pGen);
  nsresult rv = pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void **)aImportGeneric);
  NS_RELEASE(pGen);

    return rv;
}

nsImportGenericAddressBooks::nsImportGenericAddressBooks()
{
  m_pInterface = nullptr;
  m_pSuccessLog = nullptr;
  m_pErrorLog = nullptr;
  m_totalSize = 0;
  m_doImport = false;
  m_pThreadData = nullptr;
  m_pDestinationUri = nullptr;
  m_pFieldMap = nullptr;

  m_autoFind = false;
  m_description = nullptr;
  m_gotLocation = false;
  m_found = false;
  m_userVerify = false;

  nsImportStringBundle::GetStringBundle(IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle));
}


nsImportGenericAddressBooks::~nsImportGenericAddressBooks()
{
  if (m_pDestinationUri)
    NS_Free(m_pDestinationUri);

  if (m_description)
    NS_Free(m_description);

  NS_IF_RELEASE(m_pFieldMap);
  NS_IF_RELEASE(m_pInterface);
  NS_IF_RELEASE(m_pSuccessLog);
  NS_IF_RELEASE(m_pErrorLog);
}



NS_IMPL_ISUPPORTS(nsImportGenericAddressBooks, nsIImportGeneric)


NS_IMETHODIMP nsImportGenericAddressBooks::GetData(const char *dataId, nsISupports **_retval)
{
  NS_PRECONDITION(_retval != nullptr, "null ptr");
  if (!_retval)
    return NS_ERROR_NULL_POINTER;

  nsresult rv;
  *_retval = nullptr;
  if (!PL_strcasecmp(dataId, "addressInterface")) {
    *_retval = m_pInterface;
    NS_IF_ADDREF(m_pInterface);
  }

  if (!PL_strcasecmp(dataId, "addressLocation")) {
    if (!m_pLocation)
      GetDefaultLocation();
    NS_IF_ADDREF(*_retval = m_pLocation);
  }

  if (!PL_strcasecmp(dataId, "addressBooks")) {
    if (!m_pLocation)
      GetDefaultLocation();
    if (!m_Books)
      GetDefaultBooks();
    *_retval = m_Books;
  }

  if (!PL_strcasecmp(dataId, "addressDestination")) {
    if (m_pDestinationUri) {
            nsCOMPtr<nsISupportsCString> abString = do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
            NS_ENSURE_SUCCESS(rv, rv);
            abString->SetData(nsDependentCString(m_pDestinationUri));
            NS_IF_ADDREF(*_retval = abString);
    }
  }

  if (!PL_strcasecmp(dataId, "fieldMap")) {
    if (m_pFieldMap) {
      *_retval = m_pFieldMap;
      m_pFieldMap->AddRef();
    }
    else {
      if (m_pInterface && m_pLocation) {
        bool needsIt = false;
        m_pInterface->GetNeedsFieldMap(m_pLocation, &needsIt);
        if (needsIt) {
          GetDefaultFieldMap();
          if (m_pFieldMap) {
            *_retval = m_pFieldMap;
            m_pFieldMap->AddRef();
          }
        }
      }
    }
  }

  if (!PL_strncasecmp(dataId, "sampleData-", 11)) {
    // extra the record number
    const char *pNum = dataId + 11;
    int32_t  rNum = 0;
    while (*pNum) {
      rNum *= 10;
      rNum += (*pNum - '0');
      pNum++;
    }
    IMPORT_LOG1("Requesting sample data #: %ld\n", (long)rNum);
    if (m_pInterface) {
      nsCOMPtr<nsISupportsString>  data = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
      if (NS_FAILED(rv))
        return rv;
      char16_t *  pData = nullptr;
      bool      found = false;
      rv = m_pInterface->GetSampleData(rNum, &found, &pData);
      if (NS_FAILED(rv))
        return rv;
      if (found) {
        data->SetData(nsDependentString(pData));
        *_retval = data;
        NS_ADDREF(*_retval);
      }
      NS_Free(pData);
    }
  }

  return NS_OK;
}


NS_IMETHODIMP nsImportGenericAddressBooks::SetData(const char *dataId, nsISupports *item)
{
  NS_PRECONDITION(dataId != nullptr, "null ptr");
  if (!dataId)
    return NS_ERROR_NULL_POINTER;

  if (!PL_strcasecmp(dataId, "addressInterface")) {
    NS_IF_RELEASE(m_pInterface);
    if (item)
      item->QueryInterface(NS_GET_IID(nsIImportAddressBooks), (void **) &m_pInterface);
  }
  if (!PL_strcasecmp(dataId, "addressBooks")) {
    if (item)
      item->QueryInterface(NS_GET_IID(nsIArray), (void **) &m_Books);
  }

  if (!PL_strcasecmp(dataId, "addressLocation")) {
    m_pLocation = nullptr;

    if (item) {
      nsresult rv;
      m_pLocation = do_QueryInterface(item, &rv);
      NS_ENSURE_SUCCESS(rv,rv);
    }

    if (m_pInterface)
      m_pInterface->SetSampleLocation(m_pLocation);
  }

  if (!PL_strcasecmp(dataId, "addressDestination")) {
    if (item) {
      nsCOMPtr<nsISupportsCString> abString;
      item->QueryInterface(NS_GET_IID(nsISupportsCString), getter_AddRefs(abString));
      if (abString) {
        if (m_pDestinationUri)
          NS_Free(m_pDestinationUri);
        m_pDestinationUri = nullptr;
                nsAutoCString tempUri;
                abString->GetData(tempUri);
                m_pDestinationUri = ToNewCString(tempUri);
      }
    }
  }

  if (!PL_strcasecmp(dataId, "fieldMap")) {
    NS_IF_RELEASE(m_pFieldMap);
    if (item)
      item->QueryInterface(NS_GET_IID(nsIImportFieldMap), (void **) &m_pFieldMap);
  }

  return NS_OK;
}

NS_IMETHODIMP nsImportGenericAddressBooks::GetStatus(const char *statusKind, int32_t *_retval)
{
  NS_PRECONDITION(statusKind != nullptr, "null ptr");
  NS_PRECONDITION(_retval != nullptr, "null ptr");
  if (!statusKind || !_retval)
    return NS_ERROR_NULL_POINTER;

  *_retval = 0;

  if (!PL_strcasecmp(statusKind, "isInstalled")) {
    GetDefaultLocation();
    *_retval = (int32_t) m_found;
  }

  if (!PL_strcasecmp(statusKind, "canUserSetLocation")) {
    GetDefaultLocation();
    *_retval = (int32_t) m_userVerify;
  }

  if (!PL_strcasecmp(statusKind, "autoFind")) {
    GetDefaultLocation();
    *_retval = (int32_t) m_autoFind;
  }

  if (!PL_strcasecmp(statusKind, "supportsMultiple")) {
    bool      multi = false;
    if (m_pInterface)
      m_pInterface->GetSupportsMultiple(&multi);
    *_retval = (int32_t) multi;
  }

  if (!PL_strcasecmp(statusKind, "needsFieldMap")) {
    bool      needs = false;
    if (m_pInterface && m_pLocation)
      m_pInterface->GetNeedsFieldMap(m_pLocation, &needs);
    *_retval = (int32_t) needs;
  }

  return NS_OK;
}

void nsImportGenericAddressBooks::GetDefaultLocation(void)
{
  if (!m_pInterface)
    return;

  if ((m_pLocation && m_gotLocation) || m_autoFind)
    return;

  if (m_description)
    NS_Free(m_description);
  m_description = nullptr;
  m_pInterface->GetAutoFind(&m_description, &m_autoFind);
  m_gotLocation = true;
  if (m_autoFind) {
    m_found = true;
    m_userVerify = false;
    return;
  }

  nsCOMPtr <nsIFile> pLoc;
  m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found, &m_userVerify);
  if (!m_pLocation)
    m_pLocation = pLoc;
}

void nsImportGenericAddressBooks::GetDefaultBooks(void)
{
  if (!m_pInterface || m_Books)
    return;

  if (!m_pLocation && !m_autoFind)
    return;

  nsresult rv = m_pInterface->FindAddressBooks(m_pLocation, getter_AddRefs(m_Books));
  if (NS_FAILED(rv)) {
    IMPORT_LOG0("*** Error: FindAddressBooks failed\n");
  }
}

void nsImportGenericAddressBooks::GetDefaultFieldMap(void)
{
  if (!m_pInterface || !m_pLocation)
    return;

  NS_IF_RELEASE(m_pFieldMap);

  nsresult  rv;
  nsCOMPtr<nsIImportService> impSvc(do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
  if (NS_FAILED(rv)) {
    IMPORT_LOG0("*** Unable to get nsIImportService.\n");
    return;
  }

  rv = impSvc->CreateNewFieldMap(&m_pFieldMap);
  if (NS_FAILED(rv))
    return;

  int32_t  sz = 0;
  rv = m_pFieldMap->GetNumMozFields(&sz);
  if (NS_SUCCEEDED(rv))
    rv = m_pFieldMap->DefaultFieldMap(sz);
  if (NS_SUCCEEDED(rv))
    rv = m_pInterface->InitFieldMap(m_pFieldMap);
  if (NS_FAILED(rv)) {
    IMPORT_LOG0("*** Error: Unable to initialize field map\n");
    NS_IF_RELEASE(m_pFieldMap);
  }
}


NS_IMETHODIMP nsImportGenericAddressBooks::WantsProgress(bool *_retval)
{
  NS_PRECONDITION(_retval != nullptr, "null ptr");
  NS_ENSURE_ARG_POINTER(_retval);

  GetDefaultLocation();
  GetDefaultBooks();

  bool result = false;

  if (m_Books) {
    uint32_t    count = 0;
    uint32_t    i;
    bool        import;
    uint32_t    size;
    uint32_t    totalSize = 0;

    m_Books->GetLength(&count);

    for (i = 0; i < count; i++) {
      nsCOMPtr<nsIImportABDescriptor> book = do_QueryElementAt(m_Books, i);
      if (book) {
        import = false;
        size = 0;
        nsresult rv = book->GetImport(&import);
        if (NS_SUCCEEDED(rv) && import) {
          (void) book->GetSize(&size);
          result = true;
        }
        totalSize += size;
      }
    }

    m_totalSize = totalSize;
  }

  m_doImport = result;

  *_retval = result;

  return NS_OK;
}

void nsImportGenericAddressBooks::SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError)
{
  nsAutoString str;
  if (pSuccess) {
    pSuccess->GetData(str);
        str.Append(success);
        pSuccess->SetData(success);
  }
  if (pError) {
    pError->GetData(str);
        str.Append(error);
        pError->SetData(error);
  }
}

already_AddRefed<nsIAddrDatabase> GetAddressBookFromUri(const char *pUri)
{
  if (!pUri)
    return nullptr;

  nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID);
  if (!abManager)
    return nullptr;

  nsCOMPtr<nsIAbDirectory> directory;
  abManager->GetDirectory(nsDependentCString(pUri),
                          getter_AddRefs(directory));
  if (!directory)
    return nullptr;

  nsCOMPtr<nsIAbMDBDirectory> mdbDirectory = do_QueryInterface(directory);
  if (!mdbDirectory)
    return nullptr;

  nsCOMPtr<nsIAddrDatabase> pDatabase;
  mdbDirectory->GetDatabase(getter_AddRefs(pDatabase));
  return pDatabase.forget();
}

already_AddRefed<nsIAddrDatabase> GetAddressBook(const char16_t *name,
                                                 bool makeNew)
{
  if (!makeNew) {
    // FIXME: How do I get the list of address books and look for a
    // specific name.  Major bogosity!
    // For now, assume we didn't find anything with that name
  }

  IMPORT_LOG0("In GetAddressBook\n");

  nsresult rv;
  nsCOMPtr<nsIAddrDatabase> pDatabase;
  nsCOMPtr<nsIFile> dbPath;
  nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv))
  {
    /* Get the profile directory */
    rv = abManager->GetUserProfileDirectory(getter_AddRefs(dbPath));
    if (NS_SUCCEEDED(rv))
    {
      // Create a new address book file - we don't care what the file
      // name is, as long as it's unique
      rv = dbPath->Append(NS_LITERAL_STRING("impab.mab"));
      if (NS_SUCCEEDED(rv))
      {
        rv = dbPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);

        if (NS_SUCCEEDED(rv))
        {
          IMPORT_LOG0("Getting the address database factory\n");

          nsCOMPtr<nsIAddrDatabase> addrDBFactory =
            do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
          if (NS_FAILED(rv))
            return nullptr;

          IMPORT_LOG0("Opening the new address book\n");
          rv = addrDBFactory->Open(dbPath, true, true,
                                   getter_AddRefs(pDatabase));
        }
      }
    }
  }
  if (NS_FAILED(rv))
  {
    IMPORT_LOG0("Failed to get the user profile directory from the address book session\n");
  }

  if (pDatabase && dbPath)
  {
    // We made a database, add it to the UI?!?!?!?!?!?!
    // This is major bogosity again!  Why doesn't the address book
    // just handle this properly for me?  Uggggg...

    nsCOMPtr<nsIAbDirectory> parentDir;
    abManager->GetDirectory(NS_LITERAL_CSTRING(kAllDirectoryRoot),
                            getter_AddRefs(parentDir));
    if (parentDir)
    {
      nsAutoCString URI("moz-abmdbdirectory://");
      nsAutoCString leafName;
      rv = dbPath->GetNativeLeafName(leafName);
      if (NS_FAILED(rv))
        IMPORT_LOG0("*** Error: Unable to get name of database file\n");
      else
      {
        URI.Append(leafName);
        rv = parentDir->CreateDirectoryByURI(nsDependentString(name), URI);
        if (NS_FAILED(rv))
          IMPORT_LOG0("*** Error: Unable to create address book directory\n");
      }
    }

    if (NS_SUCCEEDED(rv))
      IMPORT_LOG0("Added new address book to the UI\n");
    else
      IMPORT_LOG0("*** Error: An error occurred while adding the address book to the UI\n");
  }

  return pDatabase.forget();
}

NS_IMETHODIMP nsImportGenericAddressBooks::BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval)
{
  NS_PRECONDITION(_retval != nullptr, "null ptr");
    if (!_retval)
        return NS_ERROR_NULL_POINTER;

  nsString  success;
  nsString  error;

  if (!m_doImport) {
    *_retval = true;
    nsImportStringBundle::GetStringByID(IMPORT_NO_ADDRBOOKS, m_stringBundle,
                                        success);
    SetLogs(success, error, successLog, errorLog);
    return NS_OK;
  }

  if (!m_pInterface || !m_Books) {
    nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED,
                                        m_stringBundle, error);
    SetLogs(success, error, successLog, errorLog);
    *_retval = false;
    return NS_OK;
  }

  bool needsFieldMap = false;

  if (NS_FAILED(m_pInterface->GetNeedsFieldMap(m_pLocation, &needsFieldMap)) ||
      (needsFieldMap && !m_pFieldMap)) {
    nsImportStringBundle::GetStringByID(IMPORT_ERROR_AB_NOTINITIALIZED,
                                        m_stringBundle, error);
    SetLogs(success, error, successLog, errorLog);
    *_retval = false;
    return NS_OK;
  }

  NS_IF_RELEASE(m_pSuccessLog);
  NS_IF_RELEASE(m_pErrorLog);
  m_pSuccessLog = successLog;
  m_pErrorLog = errorLog;
  NS_IF_ADDREF(m_pSuccessLog);
  NS_IF_ADDREF(m_pErrorLog);


  // create the info need to drive address book import. We're
  // not going to create a new thread for this since address books
  // don't tend to be large, and import is rare.
  m_pThreadData = new AddressThreadData();
  m_pThreadData->books = m_Books;
  NS_ADDREF(m_Books);
  m_pThreadData->addressImport = m_pInterface;
  NS_ADDREF(m_pInterface);
  m_pThreadData->fieldMap = m_pFieldMap;
  NS_IF_ADDREF(m_pFieldMap);
  m_pThreadData->errorLog = m_pErrorLog;
  NS_IF_ADDREF(m_pErrorLog);
  m_pThreadData->successLog = m_pSuccessLog;
  NS_IF_ADDREF(m_pSuccessLog);
  if (m_pDestinationUri)
    m_pThreadData->pDestinationUri = strdup(m_pDestinationUri);

  uint32_t count = 0;
  m_Books->GetLength(&count);
  // Create/obtain any address books that we need here, so that we don't need
  // to do so inside the import thread which would just proxy the create
  // operations back to the main thread anyway.
  nsCOMPtr<nsIAddrDatabase> db = GetAddressBookFromUri(m_pDestinationUri);
  for (uint32_t i = 0; i < count; ++i)
  {
    nsCOMPtr<nsIImportABDescriptor> book = do_QueryElementAt(m_Books, i);
    if (book)
    {
      if (!db)
      {
        nsString name;
        book->GetPreferredName(name);
        db = GetAddressBook(name.get(), true);
      }
      m_DBs.AppendObject(db);
    }
  }
  m_pThreadData->dBs = &m_DBs;

  NS_IF_ADDREF(m_pThreadData->stringBundle = m_stringBundle);

  nsresult rv;
  m_pThreadData->ldifService = do_GetService(NS_ABLDIFSERVICE_CONTRACTID, &rv);

  ImportAddressThread(m_pThreadData);
  delete m_pThreadData;
  m_pThreadData = nullptr;
  *_retval = true;

  return NS_OK;
}

NS_IMETHODIMP nsImportGenericAddressBooks::ContinueImport(bool *_retval)
{
    NS_PRECONDITION(_retval != nullptr, "null ptr");
    if (!_retval)
        return NS_ERROR_NULL_POINTER;

  *_retval = true;
  if (m_pThreadData) {
    if (m_pThreadData->fatalError)
      *_retval = false;
  }

  return NS_OK;
}


NS_IMETHODIMP nsImportGenericAddressBooks::GetProgress(int32_t *_retval)
{
  // This returns the progress from the the currently
  // running import mail or import address book thread.
    NS_PRECONDITION(_retval != nullptr, "null ptr");
    if (!_retval)
        return NS_ERROR_NULL_POINTER;

  if (!m_pThreadData || !(m_pThreadData->threadAlive)) {
    *_retval = 100;
    return NS_OK;
  }

  uint32_t sz = 0;
  if (m_pThreadData->currentSize && m_pInterface) {
    if (NS_FAILED(m_pInterface->GetImportProgress(&sz)))
      sz = 0;
  }

  if (m_totalSize)
    *_retval = ((m_pThreadData->currentTotal + sz) * 100) / m_totalSize;
  else
    *_retval = 0;

  // never return less than 5 so it looks like we are doing something!
  if (*_retval < 5)
    *_retval = 5;

  // as long as the thread is alive don't return completely
  // done.
  if (*_retval > 99)
    *_retval = 99;

  return NS_OK;
}


NS_IMETHODIMP nsImportGenericAddressBooks::CancelImport(void)
{
  if (m_pThreadData) {
    m_pThreadData->abort = true;
    m_pThreadData = nullptr;
  }

  return NS_OK;
}


AddressThreadData::AddressThreadData()
{
  fatalError = false;
  driverAlive = true;
  threadAlive = true;
  abort = false;
  currentTotal = 0;
  currentSize = 0;
  books = nullptr;
  addressImport = nullptr;
  successLog = nullptr;
  errorLog = nullptr;
  pDestinationUri = nullptr;
  fieldMap = nullptr;
  stringBundle = nullptr;
  ldifService = nullptr;
}

AddressThreadData::~AddressThreadData()
{
  if (pDestinationUri)
    NS_Free(pDestinationUri);

  NS_IF_RELEASE(books);
  NS_IF_RELEASE(addressImport);
  NS_IF_RELEASE(errorLog);
  NS_IF_RELEASE(successLog);
  NS_IF_RELEASE(fieldMap);
  NS_IF_RELEASE(stringBundle);
}

void nsImportGenericAddressBooks::ReportError(const char16_t *pName,
                                              nsString *pStream,
                                              nsIStringBundle* aBundle)
{
  if (!pStream)
    return;
  // load the error string
  char16_t *pFmt = nsImportStringBundle::GetStringByID(IMPORT_ERROR_GETABOOK, aBundle);
  char16_t *pText = nsTextFormatter::smprintf(pFmt, pName);
  pStream->Append(pText);
  nsTextFormatter::smprintf_free(pText);
  NS_Free(pFmt);
  pStream->AppendLiteral(MSG_LINEBREAK);
}

static void ImportAddressThread(void *stuff)
{
  IMPORT_LOG0("In Begin ImportAddressThread\n");

  AddressThreadData *pData = (AddressThreadData *)stuff;
  uint32_t          count = 0;
  uint32_t          i;
  bool              import;
  uint32_t          size;

  nsString          success;
  nsString          error;

  (void) pData->books->GetLength(&count);

  for (i = 0; (i < count) && !(pData->abort); i++) {
    nsCOMPtr<nsIImportABDescriptor> book =
      do_QueryElementAt(pData->books, i);

    if (book) {
      import = false;
      size = 0;
      nsresult rv = book->GetImport(&import);
      if (NS_SUCCEEDED(rv) && import)
        rv = book->GetSize(&size);

      if (NS_SUCCEEDED(rv) && size && import) {
        nsString name;
        book->GetPreferredName(name);

        nsCOMPtr<nsIAddrDatabase> db = pData->dBs->ObjectAt(i);

        bool fatalError = false;
        pData->currentSize = size;
        if (db) {
          char16_t *pSuccess = nullptr;
          char16_t *pError = nullptr;

          /*
          if (pData->fieldMap) {
            int32_t    sz = 0;
            int32_t    mapIndex;
            bool      active;
            pData->fieldMap->GetMapSize(&sz);
            IMPORT_LOG1("**** Field Map Size: %d\n", (int) sz);
            for (int32_t i = 0; i < sz; i++) {
              pData->fieldMap->GetFieldMap(i, &mapIndex);
              pData->fieldMap->GetFieldActive(i, &active);
              IMPORT_LOG3("Field map #%d: index=%d, active=%d\n", (int) i, (int) mapIndex, (int) active);
            }
          }
          */

          rv = pData->addressImport->ImportAddressBook(book,
                                                       db,
                                                       pData->fieldMap,
                                                       pData->ldifService,
                                                       &pError,
                                                       &pSuccess,
                                                       &fatalError);
          if (NS_SUCCEEDED(rv) && pSuccess) {
            success.Append(pSuccess);
            NS_Free(pSuccess);
          }
          if (pError) {
            error.Append(pError);
            NS_Free(pError);
          }
        }
        else {
          nsImportGenericAddressBooks::ReportError(name.get(), &error, pData->stringBundle);
        }

        pData->currentSize = 0;
        pData->currentTotal += size;

        if (db)
          db->Close(true);

        if (fatalError) {
          pData->fatalError = true;
          break;
        }
      }
    }
  }


  nsImportGenericAddressBooks::SetLogs(success, error, pData->successLog, pData->errorLog);

  if (pData->abort || pData->fatalError) {
    // FIXME: do what is necessary to get rid of what has been imported so far.
    // Nothing if we went into an existing address book!  Otherwise, delete
    // the ones we created?
  }

}