/**
   * Get a HashMap of address to RecipientEntry that contains all contact information for a contact
   * with the provided address, if one exists. This may block the UI, so run it in an async task.
   *
   * @param context Context.
   * @param inAddresses Array of addresses on which to perform the lookup.
   * @return HashMap<String,RecipientEntry>
   */
  public static HashMap<String, RecipientEntry> getMatchingRecipients(
      Context context, ArrayList<String> inAddresses, int addressType) {
    Queries.Query query;
    if (addressType == QUERY_TYPE_EMAIL) {
      query = Queries.EMAIL;
    } else {
      query = Queries.PHONE;
    }
    int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
    String[] addresses = new String[addressesSize];
    StringBuilder bindString = new StringBuilder();
    // Create the "?" string and set up arguments.
    for (int i = 0; i < addressesSize; i++) {
      Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
      addresses[i] = (tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
      bindString.append("?");
      if (i < addressesSize - 1) {
        bindString.append(",");
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
    }

    HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
    Cursor c =
        context
            .getContentResolver()
            .query(
                query.getContentUri(),
                query.getProjection(),
                query.getProjection()[Queries.Query.DESTINATION]
                    + " IN ("
                    + bindString.toString()
                    + ")",
                addresses,
                null);

    if (c != null) {
      try {
        if (c.moveToFirst()) {
          do {
            String address = c.getString(Queries.Query.DESTINATION);
            recipientEntries.put(
                address,
                RecipientEntry.constructTopLevelEntry(
                    c.getString(Queries.Query.NAME),
                    c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
                    c.getString(Queries.Query.DESTINATION),
                    c.getInt(Queries.Query.DESTINATION_TYPE),
                    c.getString(Queries.Query.DESTINATION_LABEL),
                    c.getLong(Queries.Query.CONTACT_ID),
                    c.getLong(Queries.Query.DATA_ID),
                    c.getString(Queries.Query.PHOTO_THUMBNAIL_URI)));
            if (Log.isLoggable(TAG, Log.DEBUG)) {
              Log.d(
                  TAG,
                  "Received reverse look up information for "
                      + address
                      + " RESULTS: "
                      + " NAME : "
                      + c.getString(Queries.Query.NAME)
                      + " CONTACT ID : "
                      + c.getLong(Queries.Query.CONTACT_ID)
                      + " ADDRESS :"
                      + c.getString(Queries.Query.DESTINATION));
            }
          } while (c.moveToNext());
        }
      } finally {
        c.close();
      }
    }
    return recipientEntries;
  }
  /**
   * Get a HashMap of address to RecipientEntry that contains all contact information for a contact
   * with the provided address, if one exists. This may block the UI, so run it in an async task.
   *
   * @param context Context.
   * @param inAddresses Array of addresses on which to perform the lookup.
   * @param callback RecipientMatchCallback called when a match or matches are found.
   */
  public static void getMatchingRecipients(
      Context context,
      BaseRecipientAdapter adapter,
      ArrayList<String> inAddresses,
      int addressType,
      Account account,
      RecipientMatchCallback callback) {
    Queries.Query query;
    if (addressType == QUERY_TYPE_EMAIL) {
      query = Queries.EMAIL;
    } else {
      query = Queries.PHONE;
    }
    int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
    HashSet<String> addresses = new HashSet<String>();
    StringBuilder bindString = new StringBuilder();
    // Create the "?" string and set up arguments.
    for (int i = 0; i < addressesSize; i++) {
      Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
      addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
      bindString.append("?");
      if (i < addressesSize - 1) {
        bindString.append(",");
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
    }

    String[] addressArray = new String[addresses.size()];
    addresses.toArray(addressArray);
    HashMap<String, RecipientEntry> recipientEntries = null;
    Cursor c = null;

    try {
      c =
          context
              .getContentResolver()
              .query(
                  query.getContentUri(),
                  query.getProjection(),
                  query.getProjection()[Queries.Query.DESTINATION]
                      + " IN ("
                      + bindString.toString()
                      + ")",
                  addressArray,
                  null);
      recipientEntries = processContactEntries(c, null /* directoryId */);
      callback.matchesFound(recipientEntries);
    } finally {
      if (c != null) {
        c.close();
      }
    }
    //
    final Set<String> matchesNotFound = new HashSet<String>();

    getMatchingRecipientsFromDirectoryQueries(
        context, recipientEntries, addresses, account, matchesNotFound, query, callback);

    getMatchingRecipientsFromExtensionMatcher(adapter, matchesNotFound, callback);
  }
  /**
   * Get a HashMap of address to RecipientEntry that contains all contact information for a contact
   * with the provided address, if one exists. This may block the UI, so run it in an async task.
   *
   * @param context Context.
   * @param inAddresses Array of addresses on which to perform the lookup.
   * @param callback RecipientMatchCallback called when a match or matches are found.
   * @return HashMap<String,RecipientEntry>
   */
  public static void getMatchingRecipients(
      Context context,
      ArrayList<String> inAddresses,
      int addressType,
      Account account,
      RecipientMatchCallback callback) {
    Queries.Query query;
    if (addressType == QUERY_TYPE_EMAIL) {
      query = Queries.EMAIL;
    } else {
      query = Queries.PHONE;
    }
    int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
    HashSet<String> addresses = new HashSet<String>();
    StringBuilder bindString = new StringBuilder();
    // Create the "?" string and set up arguments.
    for (int i = 0; i < addressesSize; i++) {
      Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
      addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
      bindString.append("?");
      if (i < addressesSize - 1) {
        bindString.append(",");
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
    }

    String[] addressArray = new String[addresses.size()];
    addresses.toArray(addressArray);
    HashMap<String, RecipientEntry> recipientEntries = null;
    Cursor c = null;

    try {
      c =
          context
              .getContentResolver()
              .query(
                  query.getContentUri(),
                  query.getProjection(),
                  query.getProjection()[Queries.Query.DESTINATION]
                      + " IN ("
                      + bindString.toString()
                      + ")",
                  addressArray,
                  null);
      recipientEntries = processContactEntries(c);
      callback.matchesFound(recipientEntries);
    } finally {
      if (c != null) {
        c.close();
      }
    }
    // See if any entries did not resolve; if so, we need to check other
    // directories
    final Set<String> matchesNotFound = new HashSet<String>();
    if (recipientEntries.size() < addresses.size()) {
      final List<DirectorySearchParams> paramsList;
      Cursor directoryCursor = null;
      try {
        directoryCursor =
            context
                .getContentResolver()
                .query(DirectoryListQuery.URI, DirectoryListQuery.PROJECTION, null, null, null);
        paramsList = BaseRecipientAdapter.setupOtherDirectories(context, directoryCursor, account);
      } finally {
        if (directoryCursor != null) {
          directoryCursor.close();
        }
      }
      // Run a directory query for each unmatched recipient.
      HashSet<String> unresolvedAddresses = new HashSet<String>();
      for (String address : addresses) {
        if (!recipientEntries.containsKey(address)) {
          unresolvedAddresses.add(address);
        }
      }

      matchesNotFound.addAll(unresolvedAddresses);

      Cursor directoryContactsCursor = null;
      for (String unresolvedAddress : unresolvedAddresses) {
        for (int i = 0; i < paramsList.size(); i++) {
          try {
            directoryContactsCursor =
                doQuery(
                    unresolvedAddress,
                    1,
                    paramsList.get(i).directoryId,
                    account,
                    context.getContentResolver(),
                    query);
          } finally {
            if (directoryContactsCursor != null && directoryContactsCursor.getCount() == 0) {
              directoryContactsCursor.close();
              directoryContactsCursor = null;
            } else {
              break;
            }
          }
        }
        if (directoryContactsCursor != null) {
          try {
            final Map<String, RecipientEntry> entries =
                processContactEntries(directoryContactsCursor);

            for (final String address : entries.keySet()) {
              matchesNotFound.remove(address);
            }

            callback.matchesFound(entries);
          } finally {
            directoryContactsCursor.close();
          }
        }
      }
    }

    callback.matchesNotFound(matchesNotFound);
  }