/* -*- 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 "nsMessengerUnixIntegration.h"
#include "nsIMsgAccountManager.h"
#include "nsIMsgMailSession.h"
#include "nsIMsgIncomingServer.h"
#include "nsIMsgIdentity.h"
#include "nsIMsgAccount.h"
#include "nsIMsgFolder.h"
#include "nsIMsgWindow.h"
#include "nsCOMPtr.h"
#include "nsMsgBaseCID.h"
#include "nsMsgFolderFlags.h"
#include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIDirectoryService.h"
#include "nsIWindowWatcher.h"
#include "nsIWindowMediator.h"
#include "mozIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsIDocShell.h"
#include "nsIBaseWindow.h"
#include "MailNewsTypes.h"
#include "nsIMessengerWindowService.h"
#include "prprf.h"
#include "nsIWeakReference.h"
#include "nsIStringBundle.h"
#include "nsIAlertsService.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsISupportsPrimitives.h"
#include "nsIMsgDatabase.h"
#include "nsIMsgHdr.h"
#include "nsAutoPtr.h"
#include "prmem.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsIWeakReferenceUtils.h"

#include "nsNativeCharsetUtils.h"
#include "nsToolkitCompsCID.h"
#include "nsMsgUtils.h"
#include "msgCore.h"
#include "nsCOMArray.h"
#include "nsIMutableArray.h"
#include "nsArrayUtils.h"
#include "nsMemory.h"
#include "mozilla/Services.h"
#include "mozilla/mailnews/MimeHeaderParser.h"

#define ALERT_CHROME_URL "chrome://messenger/content/newmailalert.xul"
#define NEW_MAIL_ALERT_ICON "chrome://messenger/skin/icons/new-mail-alert.png"
#define SHOW_ALERT_PREF "mail.biff.show_alert"
#define SHOW_ALERT_PREVIEW_LENGTH "mail.biff.alert.preview_length"
#define SHOW_ALERT_PREVIEW_LENGTH_DEFAULT 40
#define SHOW_ALERT_PREVIEW "mail.biff.alert.show_preview"
#define SHOW_ALERT_SENDER  "mail.biff.alert.show_sender"
#define SHOW_ALERT_SUBJECT "mail.biff.alert.show_subject"
#define SHOW_ALERT_SYSTEM  "mail.biff.use_system_alert"

using namespace mozilla::mailnews;

static void openMailWindow(const nsACString& aFolderUri)
{
  nsresult rv;
  nsCOMPtr<nsIMsgMailSession> mailSession ( do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv));
  if (NS_FAILED(rv))
    return;

  nsCOMPtr<nsIMsgWindow> topMostMsgWindow;
  rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(topMostMsgWindow));
  if (topMostMsgWindow)
  {
    if (!aFolderUri.IsEmpty())
    {
      nsCOMPtr<nsIMsgWindowCommands> windowCommands;
      topMostMsgWindow->GetWindowCommands(getter_AddRefs(windowCommands));
      if (windowCommands)
        windowCommands->SelectFolder(aFolderUri);
    }

    nsCOMPtr<mozIDOMWindowProxy> domWindow;
    topMostMsgWindow->GetDomWindow(getter_AddRefs(domWindow));
    if (domWindow) {
      nsCOMPtr<nsPIDOMWindowOuter> privateWindow = nsPIDOMWindowOuter::From(domWindow);
      privateWindow->Focus();
    }
  }
  else
  {
    // the user doesn't have a mail window open already so open one for them...
    nsCOMPtr<nsIMessengerWindowService> messengerWindowService =
      do_GetService(NS_MESSENGERWINDOWSERVICE_CONTRACTID);
    // if we want to preselect the first account with new mail,
    // here is where we would try to generate a uri to pass in
    // (and add code to the messenger window service to make that work)
    if (messengerWindowService)
      messengerWindowService->OpenMessengerWindowWithUri(
                                "mail:3pane", nsCString(aFolderUri).get(), nsMsgKey_None);
  }
}

nsMessengerUnixIntegration::nsMessengerUnixIntegration()
{
  mBiffStateAtom = MsgGetAtom("BiffState");
  mNewMailReceivedAtom = MsgGetAtom("NewMailReceived");
  mAlertInProgress = false;
  mFoldersWithNewMail = do_CreateInstance(NS_ARRAY_CONTRACTID);
}

NS_IMPL_ISUPPORTS(nsMessengerUnixIntegration, nsIFolderListener, nsIObserver,
                   nsIMessengerOSIntegration, nsIUrlListener)

nsresult
nsMessengerUnixIntegration::Init()
{
  nsresult rv;

  nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);
  return mailSession->AddFolderListener(this, nsIFolderListener::intPropertyChanged);
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemPropertyChanged(nsIMsgFolder *, nsIAtom *, char const *, char const *)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemUnicharPropertyChanged(nsIMsgFolder *, nsIAtom *, const char16_t *, const char16_t *)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemRemoved(nsIMsgFolder *, nsISupports *)
{
  return NS_OK;
}

nsresult nsMessengerUnixIntegration::GetStringBundle(nsIStringBundle **aBundle)
{
  NS_ENSURE_ARG_POINTER(aBundle);
  nsCOMPtr<nsIStringBundleService> bundleService =
    mozilla::services::GetStringBundleService();
  NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
  nsCOMPtr<nsIStringBundle> bundle;
  bundleService->CreateBundle("chrome://messenger/locale/messenger.properties",
                              getter_AddRefs(bundle));
  bundle.swap(*aBundle);
  return NS_OK;
}

bool
nsMessengerUnixIntegration::BuildNotificationTitle(nsIMsgFolder *aFolder, nsIStringBundle *aBundle, nsString &aTitle)
{
  nsString accountName;
  aFolder->GetPrettiestName(accountName);

  int32_t numNewMessages = 0;
  aFolder->GetNumNewMessages(true, &numNewMessages);

  if (!numNewMessages)
    return false;

  nsAutoString numNewMsgsText;
  numNewMsgsText.AppendInt(numNewMessages);

  const char16_t *formatStrings[] =
  {
    accountName.get(), numNewMsgsText.get()
  };

  aBundle->FormatStringFromName(numNewMessages == 1 ?
                                  u"newMailNotification_message" :
                                  u"newMailNotification_messages",
                                formatStrings, 2, getter_Copies(aTitle));
  return true;
}

/* This comparator lets us sort an nsCOMArray of nsIMsgDBHdr's by
 * their dateInSeconds attributes in ascending order.
 */
static int
nsMsgDbHdrTimestampComparator(nsIMsgDBHdr *aElement1,
                              nsIMsgDBHdr *aElement2,
                              void *aData)
{
  uint32_t aElement1Timestamp;
  nsresult rv = aElement1->GetDateInSeconds(&aElement1Timestamp);
  if (NS_FAILED(rv))
    return 0;

  uint32_t aElement2Timestamp;
  rv = aElement2->GetDateInSeconds(&aElement2Timestamp);
  if (NS_FAILED(rv))
    return 0;

  return aElement1Timestamp - aElement2Timestamp;
}


bool
nsMessengerUnixIntegration::BuildNotificationBody(nsIMsgDBHdr *aHdr,
                                                  nsIStringBundle *aBundle,
                                                  nsString &aBody)
{
  nsAutoString alertBody;

  bool showPreview = true;
  bool showSubject = true;
  bool showSender = true;
  int32_t previewLength = SHOW_ALERT_PREVIEW_LENGTH_DEFAULT;

  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
  if (!prefBranch)
    return false;

  prefBranch->GetBoolPref(SHOW_ALERT_PREVIEW, &showPreview);
  prefBranch->GetBoolPref(SHOW_ALERT_SENDER, &showSender);
  prefBranch->GetBoolPref(SHOW_ALERT_SUBJECT, &showSubject);
  prefBranch->GetIntPref(SHOW_ALERT_PREVIEW_LENGTH, &previewLength);

  nsCOMPtr<nsIMsgFolder> folder;
  aHdr->GetFolder(getter_AddRefs(folder));

  if (!folder)
    return false;

  nsCString msgURI;
  folder->GetUriForMsg(aHdr, msgURI);

  bool localOnly;

  size_t msgURIIndex = mFetchingURIs.IndexOf(msgURI);
  if (msgURIIndex == mFetchingURIs.NoIndex)
  {
    localOnly = false;
    mFetchingURIs.AppendElement(msgURI);
  }
  else
    localOnly = true;

  nsMsgKey messageKey;
  if (NS_FAILED(aHdr->GetMessageKey(&messageKey)))
    return false;

  bool asyncResult = false;
  nsresult rv = folder->FetchMsgPreviewText(&messageKey, 1,
                                            localOnly, this,
                                            &asyncResult);
  // If we're still waiting on getting the message previews,
  // bail early.  We'll come back later when the async operation
  // finishes.
  if (NS_FAILED(rv) || asyncResult)
    return false;

  // If we got here, that means that we've retrieved the message preview,
  // so we can stop tracking it with our mFetchingURIs array.
  if (msgURIIndex != mFetchingURIs.NoIndex)
    mFetchingURIs.RemoveElementAt(msgURIIndex);

  nsCString utf8previewString;
  if (showPreview &&
      NS_FAILED(aHdr->GetStringProperty("preview", getter_Copies(utf8previewString))))
    return false;

  // need listener that mailbox is remote such as IMAP
  // to generate preview message
  nsString previewString;
  CopyUTF8toUTF16(utf8previewString, previewString);

  nsString subject;
  if (showSubject && NS_FAILED(aHdr->GetMime2DecodedSubject(subject)))
    return false;

  nsString author;
  if (showSender)
  {
    nsString fullHeader;
    if (NS_FAILED(aHdr->GetMime2DecodedAuthor(fullHeader)))
      return false;

    ExtractName(DecodedHeader(fullHeader), author);
  }

  if (showSubject && showSender)
  {
    nsString msgTitle;
    const char16_t *formatStrings[] =
    {
      subject.get(), author.get()
    };
    aBundle->FormatStringFromName(u"newMailNotification_messagetitle",
        formatStrings, 2, getter_Copies(msgTitle));
    alertBody.Append(msgTitle);
  }
  else if (showSubject)
    alertBody.Append(subject);
  else if (showSender)
    alertBody.Append(author);

  if (showPreview && (showSubject || showSender))
  {
    alertBody.AppendLiteral("\n");
  }

  if (showPreview)
    alertBody.Append(StringHead(previewString, previewLength));

  if (alertBody.IsEmpty())
    return false;

  aBody.Assign(alertBody);
  return true;
}

nsresult nsMessengerUnixIntegration::ShowAlertMessage(const nsAString& aAlertTitle, const nsAString& aAlertText, const nsACString& aFolderURI)
{
  nsresult rv;
  // if we are already in the process of showing an alert, don't try to show another....
  if (mAlertInProgress)
    return NS_OK;

  mAlertInProgress = true;

  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS(rv, rv);

  // determine if we should use libnotify or the built-in alert system
  bool useSystemAlert = true;
  prefBranch->GetBoolPref(SHOW_ALERT_SYSTEM, &useSystemAlert);

  if (useSystemAlert) {
    nsCOMPtr<nsIAlertsService> alertsService(do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID, &rv));
    if (NS_SUCCEEDED(rv)) {
      rv = alertsService->ShowAlertNotification(NS_LITERAL_STRING(NEW_MAIL_ALERT_ICON),
                                                aAlertTitle,
                                                aAlertText,
                                                false,
                                                NS_ConvertASCIItoUTF16(aFolderURI),
                                                this,
                                                EmptyString(),
                                                NS_LITERAL_STRING("auto"),
                                                EmptyString(),
                                                EmptyString(),
                                                nullptr,
                                                false,
                                                false);
      if (NS_SUCCEEDED(rv))
        return rv;
    }
  }
  AlertFinished();
  rv = ShowNewAlertNotification(false);

  if (NS_FAILED(rv)) // go straight to showing the system tray icon.
    AlertFinished();

  return rv;
}

// Opening Thunderbird's new mail alert notification window for not supporting libnotify
// aUserInitiated --> true if we are opening the alert notification in response to a user action
//                    like clicking on the biff icon
nsresult nsMessengerUnixIntegration::ShowNewAlertNotification(bool aUserInitiated)
{

  nsresult rv;

  // if we are already in the process of showing an alert, don't try to show another....
  if (mAlertInProgress)
    return NS_OK;

  nsCOMPtr<nsIMutableArray> argsArray = do_CreateInstance(NS_ARRAY_CONTRACTID);
  if (!argsArray)
    return NS_ERROR_FAILURE;

  // pass in the array of folders with unread messages
  nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  ifptr->SetData(mFoldersWithNewMail);
  ifptr->SetDataIID(&NS_GET_IID(nsIArray));
  argsArray->AppendElement(ifptr, false);

  // pass in the observer
  ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIMessengerOSIntegration*>(this));
  ifptr->SetData(supports);
  ifptr->SetDataIID(&NS_GET_IID(nsIObserver));
  argsArray->AppendElement(ifptr, false);

  // pass in the animation flag
  nsCOMPtr<nsISupportsPRBool> scriptableUserInitiated (do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS(rv, rv);
  scriptableUserInitiated->SetData(aUserInitiated);
  argsArray->AppendElement(scriptableUserInitiated, false);

  nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
  nsCOMPtr<mozIDOMWindowProxy> newWindow;

  mAlertInProgress = true;
  rv = wwatch->OpenWindow(0, ALERT_CHROME_URL, "_blank",
                          "chrome,dialog=yes,titlebar=no,popup=yes", argsArray,
                          getter_AddRefs(newWindow));

  if (NS_FAILED(rv))
    AlertFinished();

  return rv;
}

nsresult nsMessengerUnixIntegration::AlertFinished()
{
  mAlertInProgress = false;
  return NS_OK;
}

nsresult nsMessengerUnixIntegration::AlertClicked()
{
  nsCString folderURI;
  GetFirstFolderWithNewMail(folderURI);
  openMailWindow(folderURI);
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
  if (strcmp(aTopic, "alertfinished") == 0)
    return AlertFinished();
  if (strcmp(aTopic, "alertclickcallback") == 0)
    return AlertClicked();

  return NS_OK;
}

void nsMessengerUnixIntegration::FillToolTipInfo()
{
  nsresult rv;
  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS(rv,);

  bool showAlert = true;
  prefBranch->GetBoolPref(SHOW_ALERT_PREF, &showAlert);
  if (!showAlert)
    return;

  nsCString folderUri;
  GetFirstFolderWithNewMail(folderUri);

  uint32_t count = 0;
  NS_ENSURE_SUCCESS_VOID(mFoldersWithNewMail->GetLength(&count));

  nsCOMPtr<nsIWeakReference> weakReference;
  nsCOMPtr<nsIMsgFolder> folder = nullptr;
  nsCOMPtr<nsIMsgFolder> folderWithNewMail = nullptr;

  uint32_t i;
  for (i = 0; i < count && !folderWithNewMail; i++)
  {
    weakReference = do_QueryElementAt(mFoldersWithNewMail, i);
    folder = do_QueryReferent(weakReference);
    folder->GetChildWithURI(folderUri, true, true,
                            getter_AddRefs(folderWithNewMail));
  }

  if (folder && folderWithNewMail)
  {
    nsCOMPtr<nsIStringBundle> bundle;
    GetStringBundle(getter_AddRefs(bundle));

    if (!bundle)
      return;

    // Create the notification title
    nsString alertTitle;
    if (!BuildNotificationTitle(folder, bundle, alertTitle))
      return;

    // Let's get the new mail for this folder
    nsCOMPtr<nsIMsgDatabase> db;
    if (NS_FAILED(folderWithNewMail->GetMsgDatabase(getter_AddRefs(db))))
      return;

    uint32_t numNewKeys = 0;
    uint32_t *newMessageKeys;
    db->GetNewList(&numNewKeys, &newMessageKeys);

    // If we had new messages, we *should* have new keys, but we'll
    // check just in case.
    if (numNewKeys <= 0) {
      NS_Free(newMessageKeys);
      return;
    }

    // Find the rootFolder that folder belongs to, and find out
    // what MRUTime it maps to.  Assign this to lastMRUTime.
    uint32_t lastMRUTime = 0;
    if (NS_FAILED(GetMRUTimestampForFolder(folder, &lastMRUTime)))
      lastMRUTime = 0;

    // Next, add the new message headers to an nsCOMArray.  We
    // only add message headers that are newer than lastMRUTime.
    nsCOMArray<nsIMsgDBHdr> newMsgHdrs;
    for (unsigned int i = 0; i < numNewKeys; ++i) {
      nsCOMPtr<nsIMsgDBHdr> hdr;
      if (NS_FAILED(db->GetMsgHdrForKey(newMessageKeys[i], getter_AddRefs(hdr))))
        continue;

      uint32_t dateInSeconds = 0;
      hdr->GetDateInSeconds(&dateInSeconds);

      if (dateInSeconds > lastMRUTime)
        newMsgHdrs.AppendObject(hdr);

    }

    // At this point, we don't need newMessageKeys any more,
    // so let's free it.
    NS_Free(newMessageKeys);

    // If we didn't happen to add any message headers, bail out
    if (!newMsgHdrs.Count())
      return;

    // Sort the message headers by dateInSeconds, in ascending
    // order
    newMsgHdrs.Sort(nsMsgDbHdrTimestampComparator, nullptr);

    nsString alertBody;

    // Build the body text of the notification.
    if (!BuildNotificationBody(newMsgHdrs[0], bundle, alertBody))
      return;

    // Show the notification
    ShowAlertMessage(alertTitle, alertBody, EmptyCString());

    // Find the last, and therefore newest message header
    // in our nsCOMArray
    nsCOMPtr<nsIMsgDBHdr> lastMsgHdr = newMsgHdrs[newMsgHdrs.Count() - 1];

    uint32_t dateInSeconds = 0;
    if (NS_FAILED(lastMsgHdr->GetDateInSeconds(&dateInSeconds)))
      return;

    // Write the newest message timestamp to the appropriate
    // mapping in our hashtable of MRUTime's.
    PutMRUTimestampForFolder(folder, dateInSeconds);
  } // if we got a folder
}

// Get the first top level folder which we know has new mail, then enumerate over
// all the subfolders looking for the first real folder with new mail.
// Return the folderURI for that folder.
nsresult nsMessengerUnixIntegration::GetFirstFolderWithNewMail(nsACString& aFolderURI)
{
  NS_ENSURE_TRUE(mFoldersWithNewMail, NS_ERROR_FAILURE);

  nsCOMPtr<nsIMsgFolder> folder;
  nsCOMPtr<nsIWeakReference> weakReference;

  uint32_t count = 0;
  nsresult rv = mFoldersWithNewMail->GetLength(&count);
  if (NS_FAILED(rv) || !count)  // kick out if we don't have any folders with new mail
    return NS_OK;

  uint32_t i;
  for(i = 0; i < count; i++)
  {
    weakReference = do_QueryElementAt(mFoldersWithNewMail, i);
    folder = do_QueryReferent(weakReference);

    // We only want to find folders which haven't been notified
    // yet.  This is specific to Thunderbird.  In Seamonkey, we
    // just return 0, and we don't care about timestamps anymore.
    uint32_t lastMRUTime = 0;
    rv = GetMRUTimestampForFolder(folder, &lastMRUTime);
    if (NS_FAILED(rv))
      lastMRUTime = 0;

    if (!folder)
      continue;
    // enumerate over the folders under this root folder till we find one with new mail....
    nsCOMPtr<nsIMsgFolder> msgFolder;
    nsCOMPtr<nsIArray> allFolders;
    rv = folder->GetDescendants(getter_AddRefs(allFolders));
    NS_ENSURE_SUCCESS(rv, rv);

    uint32_t subfolderCount = 0;
    allFolders->GetLength(&subfolderCount);
    uint32_t j;
    for (j = 0; j < subfolderCount; j++)
    {
      nsCOMPtr<nsIMsgFolder> msgFolder = do_QueryElementAt(allFolders, j);

      if (!msgFolder)
        continue;

      uint32_t flags;
      rv = msgFolder->GetFlags(&flags);

      if (NS_FAILED(rv))
        continue;

      // Unless we're dealing with an Inbox, we don't care
      // about Drafts, Queue, SentMail, Template, or Junk folders
      if (!(flags & nsMsgFolderFlags::Inbox) &&
           (flags & (nsMsgFolderFlags::SpecialUse & ~nsMsgFolderFlags::Inbox)))
        continue;

      nsCString folderURI;
      msgFolder->GetURI(folderURI);
      bool hasNew = false;
      rv = msgFolder->GetHasNewMessages(&hasNew);

      if (NS_FAILED(rv))
        continue;

      // Grab the MRUTime property from the folder
      nsCString dateStr;
      msgFolder->GetStringProperty(MRU_TIME_PROPERTY, dateStr);
      uint32_t MRUTime = (uint32_t) dateStr.ToInteger(&rv, 10);
      if (NS_FAILED(rv))
        MRUTime = 0;

      if (hasNew && MRUTime > lastMRUTime)
      {
        rv = msgFolder->GetURI(aFolderURI);
        NS_ENSURE_SUCCESS(rv, rv);
        return NS_OK;
      }
    }  // if we have more potential folders to enumerate
  }

  // If we got here, then something when pretty wrong.
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, uint32_t oldFlag, uint32_t newFlag)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemAdded(nsIMsgFolder *, nsISupports *)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemBoolPropertyChanged(nsIMsgFolder *aItem,
                                                         nsIAtom *aProperty,
                                                         bool aOldValue,
                                                         bool aNewValue)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemEvent(nsIMsgFolder *, nsIAtom *)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnItemIntPropertyChanged(nsIMsgFolder *aItem, nsIAtom *aProperty, int64_t aOldValue, int64_t aNewValue)
{
  nsCString atomName;
  // if we got new mail show an icon in the system tray
  if (mBiffStateAtom == aProperty && mFoldersWithNewMail)
  {
    nsCOMPtr<nsIWeakReference> weakFolder = do_GetWeakReference(aItem);
    uint32_t indexInNewArray;
    nsresult rv = mFoldersWithNewMail->IndexOf(0, weakFolder, &indexInNewArray);
    bool folderFound = NS_SUCCEEDED(rv);

    if (aNewValue == nsIMsgFolder::nsMsgBiffState_NewMail)
    {
      // only show a system tray icon iff we are performing biff
      // (as opposed to the user getting new mail)
      bool performingBiff = false;
      nsCOMPtr<nsIMsgIncomingServer> server;
      aItem->GetServer(getter_AddRefs(server));
      if (server)
        server->GetPerformingBiff(&performingBiff);
      if (!performingBiff)
        return NS_OK; // kick out right now...

      if (!folderFound)
        mFoldersWithNewMail->AppendElement(weakFolder, false);
      // now regenerate the tooltip
      FillToolTipInfo();
    }
    else if (aNewValue == nsIMsgFolder::nsMsgBiffState_NoMail)
    {
      if (folderFound) {
        mFoldersWithNewMail->RemoveElementAt(indexInNewArray);
      }
    }
  } // if the biff property changed
  else if (mNewMailReceivedAtom == aProperty)
  {
    FillToolTipInfo();
  }

  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnStartRunningUrl(nsIURI *aUrl)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMessengerUnixIntegration::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
{
  if (NS_SUCCEEDED(aExitCode))
    // preview fetch is done.
    FillToolTipInfo();
  return NS_OK;
}

nsresult
nsMessengerUnixIntegration::GetMRUTimestampForFolder(nsIMsgFolder *aFolder,
                                                     uint32_t *aLastMRUTime)
{
  nsCOMPtr<nsIMsgFolder> rootFolder = nullptr;
  nsresult rv = aFolder->GetRootFolder(getter_AddRefs(rootFolder));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCString rootFolderURI;
  rootFolder->GetURI(rootFolderURI);
  if (!mLastMRUTimes.Get(rootFolderURI, aLastMRUTime))
    aLastMRUTime = 0;

  return NS_OK;
}

nsresult
nsMessengerUnixIntegration::PutMRUTimestampForFolder(nsIMsgFolder *aFolder,
                                                     uint32_t aLastMRUTime)
{
  nsresult rv;
  nsCOMPtr<nsIMsgFolder> rootFolder = nullptr;
  rv = aFolder->GetRootFolder(getter_AddRefs(rootFolder));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCString rootFolderURI;
  rootFolder->GetURI(rootFolderURI);
  mLastMRUTimes.Put(rootFolderURI, aLastMRUTime);

  return NS_OK;
}
