/* vim: ts=2 sw=2 expandtab
 * 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 "flockStreamUtils.h"

#include "nsServiceManagerUtils.h"
#include "nsIRDFService.h"
#include "nsIRDFContainerUtils.h"
#include "nsNetUtil.h"

#include "flockIRDFContainer.h"


#define NS_RDFSERVICE_CONTRACTID "@mozilla.org/rdf/rdf-service;1"


class flockStreamEnumerator : public nsISimpleEnumerator
{
  NS_DECL_ISUPPORTS
  NS_DECL_NSISIMPLEENUMERATOR

  flockStreamEnumerator(nsIRDFDataSource* aDataSource,
                        nsIRDFResource* aResource,
                        PRBool aEnumerateBackwards,
                        PRInt32 aParam)
    : mDataSource(aDataSource), mResource(aResource),
      mEnumerateBackwards(aEnumerateBackwards), mParam(aParam) { }

private:
  ~flockStreamEnumerator() { }

  nsresult FillNext();

  nsCOMPtr<nsIRDFDataSource> mDataSource;
  nsCOMPtr<nsIRDFResource> mResource;

  PRBool mEnumerateBackwards;
  PRInt32 mParam;

  nsCOMPtr<nsISupports> mNextHolder;

  nsCOMPtr<nsISimpleEnumerator> mSourceEnumerator;

protected:
  friend class flockStreamUtils;

  nsresult Setup();
};

NS_IMPL_ISUPPORTS1(flockStreamEnumerator, nsISimpleEnumerator)

nsresult
flockStreamEnumerator::Setup()
{
  nsresult rv;

  mNextHolder = NULL;

  nsCOMPtr<nsIRDFContainerUtils> rdfc =
    do_GetService("@mozilla.org/rdf/container-utils;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  PRBool isRDFContainer;
  rv = rdfc->IsContainer(mDataSource, mResource, &isRDFContainer);
  NS_ENSURE_SUCCESS(rv, rv);

  if (!isRDFContainer)
    return NS_ERROR_FAILURE;

  nsCOMPtr<nsIRDFContainer> container =
    do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = container->Init(mDataSource, mResource);
  NS_ENSURE_SUCCESS(rv, rv);

  if (mEnumerateBackwards) {
    nsCOMPtr<flockIRDFContainer> flockContainer =
      do_QueryInterface(container, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = flockContainer->GetElementsBackwards(getter_AddRefs(mSourceEnumerator));
  } else {
    rv = container->GetElements(getter_AddRefs(mSourceEnumerator));
  }

  return rv;
}

nsresult
flockStreamEnumerator::FillNext()
{
  nsresult rv;

  // Get a handle to the RDFService
  nsCOMPtr<nsIRDFService> rdfService;
  rdfService = do_GetService (NS_RDFSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  // This is the unseen resource that we are trying to get the literal from
  nsCOMPtr<nsIRDFResource> unseen;
  NS_NAMED_LITERAL_CSTRING(unseenProp, "http://flock.com/rdf#unseen");
  rv = rdfService->GetResource(unseenProp, getter_AddRefs(unseen));
  NS_ENSURE_SUCCESS(rv, rv);

  while (1) {
    PRBool hasMoreTargets = PR_FALSE;
    mSourceEnumerator->HasMoreElements(&hasMoreTargets);
    if (!hasMoreTargets)
      break;

    nsCOMPtr<nsISupports> isupports;
    mSourceEnumerator->GetNext(getter_AddRefs(isupports));
    if (!isupports)
      break;

    nsCOMPtr<nsIRDFResource> subject = do_QueryInterface(isupports);
    if (!subject)
      continue;

    nsCOMPtr<nsIRDFNode> valueNode;
    mDataSource->GetTarget(subject, unseen, PR_TRUE, getter_AddRefs(valueNode));

    nsCOMPtr<nsIRDFLiteral> lit(do_QueryInterface(valueNode));
    if (lit) {
      const PRUnichar *s;
      lit->GetValueConst(&s);

      nsDependentString val(s);
      if (((mParam & flockIStreamUtils::STREAMS_SEEN)   && val.EqualsLiteral("true")) ||
          ((mParam & flockIStreamUtils::STREAMS_UNSEEN) && val.EqualsLiteral("false"))) {
        mNextHolder = subject;
        break;
      }
    }
  }

  return NS_OK;
}

NS_IMETHODIMP
flockStreamEnumerator::GetNext(nsISupports **_retval)
{
  nsresult rv;

  if (mNextHolder == NULL) {
    rv = FillNext();
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if (mNextHolder) {
    *_retval = mNextHolder;
    NS_ADDREF(*_retval);
    mNextHolder = NULL;
    return NS_OK;
  } else {
    return NS_ERROR_FAILURE;
  }
}

NS_IMETHODIMP
flockStreamEnumerator::HasMoreElements(PRBool *_retval)
{
  nsresult rv;

  if (mNextHolder == NULL) {
    rv = FillNext();
    NS_ENSURE_SUCCESS(rv, rv);
  }

  *_retval = (mNextHolder != NULL);
  return NS_OK;
}


NS_IMPL_ISUPPORTS1(flockStreamUtils, flockIStreamUtils)

flockStreamUtils::flockStreamUtils()
{
}

flockStreamUtils::~flockStreamUtils()
{
}

NS_IMETHODIMP 
flockStreamUtils::EnumerateStreams(nsIRDFDataSource *aDataSource,
                                   nsIRDFResource *aResource,
                                   PRBool aEnumerateBackwards, 
                                   PRInt32 aParam,
                                   nsISimpleEnumerator **_retval)
{ 
  nsresult rv;

  NS_ENSURE_ARG_POINTER(aDataSource);

  flockStreamEnumerator *enumerator =
    new flockStreamEnumerator(aDataSource, aResource, aEnumerateBackwards,
                              aParam);
  if (!enumerator)
    return NS_ERROR_OUT_OF_MEMORY;

  rv = enumerator->Setup();
  NS_ENSURE_SUCCESS(rv, rv);
  
  NS_ADDREF(*_retval = enumerator);
  return NS_OK;
}
