/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Jean-Francois Ducarroz <ducarroz@netscape.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsMsgSendReport.h"

#include "msgCore.h"
#include "nsIMsgCompose.h"
#include "nsMsgCompCID.h"
#include "nsMsgPrompts.h"
#include "nsNetError.h"
#include "nsComposeStrings.h"
#include "nsIStringBundle.h"
#include "nsServiceManagerUtils.h"

NS_IMPL_ISUPPORTS1(nsMsgProcessReport, nsIMsgProcessReport)

nsMsgProcessReport::nsMsgProcessReport()
{
  Reset();
}

nsMsgProcessReport::~nsMsgProcessReport()
{
}

/* attribute boolean proceeded; */
NS_IMETHODIMP nsMsgProcessReport::GetProceeded(bool *aProceeded)
{
  NS_ENSURE_ARG_POINTER(aProceeded);
  *aProceeded = mProceeded;
  return NS_OK;
}
NS_IMETHODIMP nsMsgProcessReport::SetProceeded(bool aProceeded)
{
  mProceeded = aProceeded;
  return NS_OK;
}

/* attribute nsresult error; */
NS_IMETHODIMP nsMsgProcessReport::GetError(nsresult *aError)
{
  NS_ENSURE_ARG_POINTER(aError);
  *aError = mError;
  return NS_OK;
}
NS_IMETHODIMP nsMsgProcessReport::SetError(nsresult aError)
{
  mError = aError;
  return NS_OK;
}

/* attribute wstring message; */
NS_IMETHODIMP nsMsgProcessReport::GetMessage(PRUnichar * *aMessage)
{
  NS_ENSURE_ARG_POINTER(aMessage);
  *aMessage = ToNewUnicode(mMessage);
  return NS_OK;
}
NS_IMETHODIMP nsMsgProcessReport::SetMessage(const PRUnichar * aMessage)
{
  mMessage = aMessage;
  return NS_OK;
}

/* void Reset (); */
NS_IMETHODIMP nsMsgProcessReport::Reset()
{
  mProceeded = PR_FALSE;
  mError = NS_OK;
  mMessage.Truncate();

  return NS_OK;
}


NS_IMPL_ISUPPORTS1(nsMsgSendReport, nsIMsgSendReport)

nsMsgSendReport::nsMsgSendReport()
{
  PRUint32 i;
  for (i = 0; i <= SEND_LAST_PROCESS; i ++)
    mProcessReport[i] = new nsMsgProcessReport();

  Reset(); 
}

nsMsgSendReport::~nsMsgSendReport()
{
  PRUint32 i;
  for (i = 0; i <= SEND_LAST_PROCESS; i ++)
    mProcessReport[i] = nsnull;
}

/* attribute long currentProcess; */
NS_IMETHODIMP nsMsgSendReport::GetCurrentProcess(PRInt32 *aCurrentProcess)
{
  NS_ENSURE_ARG_POINTER(aCurrentProcess);
  *aCurrentProcess = mCurrentProcess;
  return NS_OK;
}
NS_IMETHODIMP nsMsgSendReport::SetCurrentProcess(PRInt32 aCurrentProcess)
{
  if (aCurrentProcess < 0 || aCurrentProcess > SEND_LAST_PROCESS)
    return NS_ERROR_ILLEGAL_VALUE;

  mCurrentProcess = aCurrentProcess;
  if (mProcessReport[mCurrentProcess])
    mProcessReport[mCurrentProcess]->SetProceeded(PR_TRUE);

  return NS_OK;
}

/* attribute long deliveryMode; */
NS_IMETHODIMP nsMsgSendReport::GetDeliveryMode(PRInt32 *aDeliveryMode)
{
  NS_ENSURE_ARG_POINTER(aDeliveryMode);
  *aDeliveryMode = mDeliveryMode;
  return NS_OK;
}
NS_IMETHODIMP nsMsgSendReport::SetDeliveryMode(PRInt32 aDeliveryMode)
{
  mDeliveryMode = aDeliveryMode;
  return NS_OK;
}

/* void Reset (); */
NS_IMETHODIMP nsMsgSendReport::Reset()
{
  PRUint32 i;
  for (i = 0; i <= SEND_LAST_PROCESS; i ++)
    if (mProcessReport[i])
      mProcessReport[i]->Reset();

  mCurrentProcess = 0;
  mDeliveryMode = 0;
  mAlreadyDisplayReport = PR_FALSE;

  return NS_OK;
}

/* void setProceeded (in long process, in boolean proceeded); */
NS_IMETHODIMP nsMsgSendReport::SetProceeded(PRInt32 process, bool proceeded)
{
  if (process < process_Current || process > SEND_LAST_PROCESS)
    return NS_ERROR_ILLEGAL_VALUE;

  if (process == process_Current)
    process = mCurrentProcess;

  if (!mProcessReport[process])
    return NS_ERROR_NOT_INITIALIZED;

  return mProcessReport[process]->SetProceeded(proceeded);
}

/* void setError (in long process, in nsresult error, in boolean overwriteError); */
NS_IMETHODIMP nsMsgSendReport::SetError(PRInt32 process, nsresult newError, bool overwriteError)
{
  if (process < process_Current || process > SEND_LAST_PROCESS)
    return NS_ERROR_ILLEGAL_VALUE;

  if (process == process_Current)
    process = mCurrentProcess;

  if (!mProcessReport[process])
    return NS_ERROR_NOT_INITIALIZED;

  nsresult currError = NS_OK;
  mProcessReport[process]->GetError(&currError);
  if (overwriteError || currError == NS_OK)
    return mProcessReport[process]->SetError(newError);
  else
    return NS_OK;
}

/* void setMessage (in long process, in wstring message, in boolean overwriteMessage); */
NS_IMETHODIMP nsMsgSendReport::SetMessage(PRInt32 process, const PRUnichar *message, bool overwriteMessage)
{
  if (process < process_Current || process > SEND_LAST_PROCESS)
    return NS_ERROR_ILLEGAL_VALUE;

  if (process == process_Current)
    process = mCurrentProcess;

  if (!mProcessReport[process])
    return NS_ERROR_NOT_INITIALIZED;

  nsString currMessage;
  mProcessReport[process]->GetMessage(getter_Copies(currMessage));
  if (overwriteMessage || currMessage.IsEmpty())
    return mProcessReport[process]->SetMessage(message);
  else
    return NS_OK;
}

/* nsIMsgProcessReport getProcessReport (in long process); */
NS_IMETHODIMP nsMsgSendReport::GetProcessReport(PRInt32 process, nsIMsgProcessReport **_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);
  if (process < process_Current || process > SEND_LAST_PROCESS)
    return NS_ERROR_ILLEGAL_VALUE;

  if (process == process_Current)
    process = mCurrentProcess;

  NS_IF_ADDREF(*_retval = mProcessReport[process]);
  return NS_OK;
}

/* nsresult displayReport (in nsIPrompt prompt, in boolean showErrorOnly, in boolean dontShowReportTwice); */
NS_IMETHODIMP nsMsgSendReport::DisplayReport(nsIPrompt *prompt, bool showErrorOnly, bool dontShowReportTwice, nsresult *_retval)
{
  NS_ENSURE_ARG_POINTER(_retval);

  nsresult currError = NS_OK;
  mProcessReport[mCurrentProcess]->GetError(&currError);
  *_retval = currError;

  if (dontShowReportTwice && mAlreadyDisplayReport)
    return NS_OK;

  if (showErrorOnly && NS_SUCCEEDED(currError))
    return NS_OK;

  nsString currMessage;
  mProcessReport[mCurrentProcess]->GetMessage(getter_Copies(currMessage));

  nsresult rv; // don't step on currError.
  nsCOMPtr<nsIStringBundleService> bundleService(do_GetService("@mozilla.org/intl/stringbundle;1", &rv));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIStringBundle> bundle;
  rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
  if (NS_FAILED(rv))
  {
    //TODO need to display a generic hardcoded message
    mAlreadyDisplayReport = PR_TRUE;
    return NS_OK;  
  }

  nsString dialogTitle;
  nsString dialogMessage;

  if (NS_SUCCEEDED(currError))
  {
    //TODO display a success error message
    return NS_OK;
  }

  //Do we have an explanation of the error? if no, try to build one...
  if (currMessage.IsEmpty())
  {
    switch (currError)
    {
      case NS_BINDING_ABORTED:
      case NS_ERROR_SEND_FAILED:
      case NS_ERROR_SEND_FAILED_BUT_NNTP_OK:
      case NS_MSG_FAILED_COPY_OPERATION:
      case NS_MSG_UNABLE_TO_SEND_LATER:
      case NS_MSG_UNABLE_TO_SAVE_DRAFT:
      case NS_MSG_UNABLE_TO_SAVE_TEMPLATE:
        //Ignore, don't need to repeat ourself.
        break;
      default:
        nsMsgGetMessageByID(currError, currMessage);
        break;
    }
  }

  if (mDeliveryMode == nsIMsgCompDeliverMode::Now || mDeliveryMode == nsIMsgCompDeliverMode::SendUnsent)
  {
    // SMTP is taking care of it's own error message and will return NS_ERROR_BUT_DONT_SHOW_ALERT as error code.
    // In that case, we must not show an alert ourself.
    if (currError == NS_ERROR_BUT_DONT_SHOW_ALERT)
    {
      mAlreadyDisplayReport = PR_TRUE;
      return NS_OK;
    }

    bundle->GetStringFromID(NS_MSG_SEND_ERROR_TITLE, getter_Copies(dialogTitle));

    PRInt32 preStrId = NS_ERROR_SEND_FAILED;
    bool askToGoBackToCompose = false;
    switch (mCurrentProcess)
    {
      case process_BuildMessage :
        preStrId = NS_ERROR_SEND_FAILED;
        askToGoBackToCompose = PR_FALSE;
        break;
      case process_NNTP :
        preStrId = NS_ERROR_SEND_FAILED;
        askToGoBackToCompose = PR_FALSE;
        break;
      case process_SMTP :
        bool nntpProceeded;
        mProcessReport[process_NNTP]->GetProceeded(&nntpProceeded);
        if (nntpProceeded)
          preStrId = NS_ERROR_SEND_FAILED_BUT_NNTP_OK;
        else
          preStrId = NS_ERROR_SEND_FAILED;
        askToGoBackToCompose = PR_FALSE;
        break;
      case process_Copy:
        preStrId = NS_MSG_FAILED_COPY_OPERATION;
        askToGoBackToCompose = (mDeliveryMode == nsIMsgCompDeliverMode::Now);
        break;
      case process_FCC:
        preStrId = NS_MSG_FAILED_COPY_OPERATION;
        askToGoBackToCompose = (mDeliveryMode == nsIMsgCompDeliverMode::Now);
        break;
    }
    bundle->GetStringFromID(preStrId, getter_Copies(dialogMessage));

    //Do we already have an error message?
    if (!askToGoBackToCompose && currMessage.IsEmpty())
    {
      //we don't have an error description but we can put a generic explanation
      bundle->GetStringFromID(NS_MSG_GENERIC_FAILURE_EXPLANATION, getter_Copies(currMessage));
    }

    if (!currMessage.IsEmpty())
    {
      //Don't need to repeat ourself!
      if (!currMessage.Equals(dialogMessage))
      {
        if (!dialogMessage.IsEmpty())
          dialogMessage.Append(PRUnichar('\n'));
        dialogMessage.Append(currMessage);
      }
    }

    if (askToGoBackToCompose)
    {
      bool oopsGiveMeBackTheComposeWindow = true;
      nsString text1;
      bundle->GetStringFromID(NS_MSG_ASK_TO_COMEBACK_TO_COMPOSE, getter_Copies(text1));
      if (!dialogMessage.IsEmpty())
        dialogMessage.AppendLiteral("\n");
      dialogMessage.Append(text1);
      nsMsgAskBooleanQuestionByString(prompt, dialogMessage.get(), &oopsGiveMeBackTheComposeWindow, dialogTitle.get());
      if (!oopsGiveMeBackTheComposeWindow)
        *_retval = NS_OK;
    }
    else
      nsMsgDisplayMessageByString(prompt, dialogMessage.get(), dialogTitle.get());
  }
  else
  {
    PRInt32 titleID;
    PRInt32 preStrId;

    switch (mDeliveryMode)
    {
      case nsIMsgCompDeliverMode::Later:
        titleID = NS_MSG_SENDLATER_ERROR_TITLE;
        preStrId = NS_MSG_UNABLE_TO_SEND_LATER;
        break;

      case nsIMsgCompDeliverMode::AutoSaveAsDraft:
      case nsIMsgCompDeliverMode::SaveAsDraft:
        titleID = NS_MSG_SAVE_DRAFT_TITLE;
        preStrId = NS_MSG_UNABLE_TO_SAVE_DRAFT;
        break;

      case nsIMsgCompDeliverMode::SaveAsTemplate:
        titleID = NS_MSG_SAVE_TEMPLATE_TITLE;
        preStrId = NS_MSG_UNABLE_TO_SAVE_TEMPLATE;
        break;

      default:
        /* This should never happend! */
        titleID = NS_MSG_SEND_ERROR_TITLE;
        preStrId = NS_ERROR_SEND_FAILED;
        break;
    }

    bundle->GetStringFromID(titleID, getter_Copies(dialogTitle));
    // preStrId could be a string ID or it could be an error code...yuck.
    bundle->GetStringFromID(NS_IS_MSG_ERROR(preStrId) ? NS_ERROR_GET_CODE(preStrId) : preStrId, getter_Copies(dialogMessage));

    //Do we have an error message...
    if (currMessage.IsEmpty())
    {
      //we don't have an error description but we can put a generic explanation
      bundle->GetStringFromID(NS_MSG_GENERIC_FAILURE_EXPLANATION, getter_Copies(currMessage));
    }

    if (!currMessage.IsEmpty())
    {
      if (!dialogMessage.IsEmpty())
        dialogMessage.Append(PRUnichar('\n'));
      dialogMessage.Append(currMessage);
    }
    nsMsgDisplayMessageByString(prompt, dialogMessage.get(), dialogTitle.get());
  }

  mAlreadyDisplayReport = PR_TRUE;
  return NS_OK;
}

