/*
 * 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 "flockLucene.h"
#include "flockLuceneImpl.h"

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


static flockLucene *gLuceneService = nsnull;

static nsIThread *gLuceneThread = nsnull;


NS_IMPL_THREADSAFE_ISUPPORTS2(flockLucene, flockILucene, nsIObserver)

flockLucene::flockLucene()
{
}

flockLucene::~flockLucene()
{
  gLuceneService = nsnull;
}

flockLucene*
flockLucene::GetSingleton()
{
  if (!gLuceneService) {
    gLuceneService = new flockLucene();
    if (!gLuceneService) {
      return nsnull;
    }
  }

  NS_ADDREF(gLuceneService);
  return gLuceneService;
}

NS_IMETHODIMP
flockLucene::AddDocument(const char *aURI,
                         const char *aType,
                         const nsAString& aURL,
                         const nsAString& aTitle,
                         const nsAString& aTags,
                         const nsAString& aDescription,
                         const nsAString& aText,
                         flockILuceneIndexListener *aLuceneIndexListener)
{
  NS_ENSURE_TRUE(gLuceneThread, NS_ERROR_NOT_INITIALIZED);

  nsCOMPtr<flockILuceneIndexListener> indexListenerProxy;
  nsresult rv = GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
                                  NS_GET_IID(flockILuceneIndexListener),
                                  aLuceneIndexListener,
                                  NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
                                  getter_AddRefs(indexListenerProxy));
  NS_ENSURE_SUCCESS(rv, rv);

  return mProxyAsync->AddDocument(aURI, aType, aURL, aTitle, aTags,
                                  aDescription, aText, indexListenerProxy);
}

NS_IMETHODIMP
flockLucene::AddDocumentSync(const char *aURI,
                             const char *aType,
                             const nsAString& aURL,
                             const nsAString& aTitle,
                             const nsAString& aTags,
                             const nsAString& aDescription,
                             const nsAString& aText)
{
  NS_ENSURE_TRUE(gLuceneThread, NS_ERROR_NOT_INITIALIZED);

  return mProxySync->AddDocumentSync(aURI, aType, aURL, aTitle, aTags,
                                     aDescription, aText);
}

NS_IMETHODIMP
flockLucene::DeleteDocument(const char *aURI,
                            flockILuceneIndexListener *aLuceneIndexListener)
{
  NS_ENSURE_TRUE(gLuceneThread, NS_ERROR_NOT_INITIALIZED);

  nsCOMPtr<flockILuceneIndexListener> indexListenerProxy;
  nsresult rv = GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
                                  NS_GET_IID(flockILuceneIndexListener),
                                  aLuceneIndexListener,
                                  NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
                                  getter_AddRefs(indexListenerProxy));
  NS_ENSURE_SUCCESS(rv, rv);

  return mProxyAsync->DeleteDocument(aURI, indexListenerProxy);
}

NS_IMETHODIMP
flockLucene::DeleteDocumentSync(const char *aURI)
{
  NS_ENSURE_TRUE(gLuceneThread, NS_ERROR_NOT_INITIALIZED);

  return mProxySync->DeleteDocumentSync(aURI);
}

NS_IMETHODIMP
flockLucene::Search(const nsAString& aQuery,
                    const char *aType,
                    PRInt32 aMaxResults,
                    flockILuceneSearchCallback *aLuceneSearchCallback)
{
  NS_ENSURE_TRUE(gLuceneThread, NS_ERROR_NOT_INITIALIZED);

  nsCOMPtr<flockILuceneSearchCallback> searchCallbackProxy;
  nsresult rv = GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
                                  NS_GET_IID(flockILuceneSearchCallback),
                                  aLuceneSearchCallback,
                                  NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
                                  getter_AddRefs(searchCallbackProxy));
  NS_ENSURE_SUCCESS(rv, rv);

  return mProxyAsync->Search(aQuery, aType, aMaxResults, searchCallbackProxy);
}

NS_IMETHODIMP
flockLucene::Init(nsIFile *aIndexDir)
{
  nsresult rv;

  if (gLuceneThread) {
    return NS_OK;
  }

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

  mLuceneImpl = new flockLuceneImpl();
  if (!mLuceneImpl) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  rv = mLuceneImpl->Init(aIndexDir);
  if (NS_FAILED(rv)) {
    mLuceneImpl = nsnull;
    return rv;
  }

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

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

  rv = GetProxyForObject(gLuceneThread,
                         NS_GET_IID(flockILucene),
                         mLuceneImpl,
                         NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
                         getter_AddRefs(mProxyAsync));
  NS_ENSURE_SUCCESS(rv, rv);

  rv = GetProxyForObject(gLuceneThread,
                         NS_GET_IID(flockILucene),
                         mLuceneImpl,
                         NS_PROXY_SYNC | NS_PROXY_ALWAYS,
                         getter_AddRefs(mProxySync));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIObserverService> observerService(do_GetService("@mozilla.org/observer-service;1"));
  if (!observerService) {
    return NS_ERROR_FAILURE;
  }

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

  return NS_OK;
}

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

  return NS_OK;
}

nsresult
flockLucene::Shutdown()
{
  if (!gLuceneThread) {
    return NS_OK;
  }

  nsIThread *doomedThread = gLuceneThread;
  gLuceneThread = nsnull;

  mProxyAsync = nsnull;
  mProxySync = nsnull;

  mLuceneImpl->mShuttingDown = PR_TRUE;

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

  mLuceneImpl = nsnull;

  return NS_OK; 
}

nsresult
flockLucene::GetProxyForObject(nsIEventTarget *aTarget, REFNSIID aIID,
                               nsISupports *aObject, PRInt32 aProxyType,
                               void **aProxy)
{
  return mProxyManager->GetProxyForObject(aTarget, aIID, aObject,
                                          aProxyType, aProxy);
}
