/*
 * BEGIN FLOCK GPL
 *
 * Copyright Flock Inc. 2005-2009
 * http://flock.com
 *
 * This file may be used under the terms of the
 * GNU General Public License Version 2 or later (the "GPL"),
 * http://www.gnu.org/licenses/gpl.html
 *
 * 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.
 *
 * END FLOCK GPL
 */

#include "flockAsyncImageScaler.h"
#include "flockAsyncImageScalerImpl.h"

#include "nsXPCOM.h"
#include "nsMemory.h"
#include "nsCRTGlue.h"

#include "nsCOMPtr.h"

#include "nsThreadUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsIProxyObjectManager.h"
#include "nsISupportsPriority.h"
#include "nsIObserverService.h"


static flockAsyncImageScaler *gAsyncImageScaler = nsnull;

static nsIThread *gAsyncImageScalerThread = nsnull;


NS_IMPL_ISUPPORTS2(flockAsyncImageScaler, flockIAsyncImageScaler, nsIObserver)

flockAsyncImageScaler::flockAsyncImageScaler()
{
}

flockAsyncImageScaler::~flockAsyncImageScaler()
{
  gAsyncImageScaler = nsnull;
}

flockAsyncImageScaler*
flockAsyncImageScaler::GetSingleton()
{
  if (!gAsyncImageScaler) {
    gAsyncImageScaler = new flockAsyncImageScaler();
    if (!gAsyncImageScaler) {
      return nsnull;
    }

    NS_ADDREF(gAsyncImageScaler);
    if (NS_FAILED(gAsyncImageScaler->Initialize())) {
      NS_RELEASE(gAsyncImageScaler);
      return nsnull;
    }

    return gAsyncImageScaler;
  }

  NS_ADDREF(gAsyncImageScaler);
  return gAsyncImageScaler;
}

NS_IMETHODIMP
flockAsyncImageScaler::DoAsyncMagick(const char **aParams,
                                     PRUint32 aCount,
                                     flockIAsyncImageScalerListener *aImageScalerListener)
{
  NS_ENSURE_TRUE(gAsyncImageScalerThread, NS_ERROR_NOT_INITIALIZED);

  nsresult rv;

  nsCOMPtr<nsIProxyObjectManager> manager
    (do_GetService("@mozilla.org/xpcomproxy;1", &rv));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<flockIAsyncImageScalerListener> listenerProxy;
  rv = manager->GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
                                  NS_GET_IID(flockIAsyncImageScalerListener),
                                  aImageScalerListener,
                                  NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
                                  getter_AddRefs(listenerProxy));
  NS_ENSURE_SUCCESS(rv, rv);

  char **argv = (char **)NS_Alloc(sizeof(char *) * aCount);
  if (!argv) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  for (PRUint32 i = 0; i < aCount; i++) {
    char *arg = NS_strdup(aParams[i]);
    if (!arg) {
      NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, argv);
      return NS_ERROR_OUT_OF_MEMORY;
    }
    argv[i] = arg;
  }

  rv = mProxy->DoAsyncMagick(argv, aCount, listenerProxy);

  if (NS_FAILED(rv)) {
    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(aCount, argv);
  }

  return rv;
}

nsresult
flockAsyncImageScaler::Initialize()
{
  nsresult rv;

  mProxyManager = do_GetService("@mozilla.org/xpcomproxy;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  mAsyncImageScalerImpl = new flockAsyncImageScalerImpl();
  if (!mAsyncImageScalerImpl) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  rv = NS_NewThread(&gAsyncImageScalerThread);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsISupportsPriority> priorityThread =
    do_QueryInterface(gAsyncImageScalerThread);
  if (priorityThread) {
    priorityThread->SetPriority(nsISupportsPriority::PRIORITY_LOW);
  }

  rv =
    mProxyManager->GetProxyForObject(gAsyncImageScalerThread,
                                     NS_GET_IID(flockIAsyncImageScalerInternal),
                                     mAsyncImageScalerImpl,
                                     NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
                                     getter_AddRefs(mProxy));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIObserverService> observerService
    (do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS(rv, rv);

  observerService->AddObserver(this, "xpcom-shutdown-threads", PR_FALSE);

  return NS_OK;
}

NS_IMETHODIMP
flockAsyncImageScaler::Observe(nsISupports *aSubject,
                               const char *aTopic,
                               const PRUnichar *aData)
{
  if (!strcmp(aTopic, "xpcom-shutdown-threads")) {
    Shutdown();
  }

  return NS_OK;
}

nsresult
flockAsyncImageScaler::Shutdown()
{
  if (!gAsyncImageScalerThread) {
    return NS_OK;
  }

  nsIThread *doomedThread = gAsyncImageScalerThread;
  gAsyncImageScalerThread = nsnull;

  mProxy = nsnull;

  mAsyncImageScalerImpl->mShuttingDown = PR_TRUE;

  doomedThread->Shutdown();
  NS_RELEASE(doomedThread);

  mAsyncImageScalerImpl = nsnull;

  return NS_OK;
}
