/**********************************************************************
 *
 * listsetsaction.cpp --
 *
 * Copyright (C) 2004-2010  The New Zealand Digital Library Project
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#include "listsetsaction.h"

#include "resumptiontoken.h"
#include "recptprototools.h"
#include "oaitools.h"


bool listsetsaction::validateAction(recptproto *protocol, oaiargs &params)
{
  // ----------------------------------------------------------------------------
  //  1. Check for invalid arguments
  // ----------------------------------------------------------------------------
  bool invalid_argument_supplied = false;
  text_tmap::const_iterator param_iterator = params.begin();
  while (param_iterator != params.end())
  {
    // Check for arguments that aren't valid for this action
    if (param_iterator->first != "verb" &&
	param_iterator->first != "resumptionToken")
    {
      // We've found an invalid argument
      invalid_argument_supplied = true;

      // Delete the invalid argument from the list so it doesn't end up in the <request> tag that is returned
      params.erase(param_iterator->first);
    }

    param_iterator++;
  }

  // If we found an invalid argument it's an error, so don't go any further 
  if (invalid_argument_supplied)
  {
    this->errorType = "badArgument";
    return false;
  }

  // ----------------------------------------------------------------------------
  //  2. Handle any exclusive arguments
  // ----------------------------------------------------------------------------

  //  The resumptionToken argument is exclusive
  if (params["resumptionToken"] != "")
  {
    // This argument is exclusive, so no other arguments are allowed (except "verb" of course)
    if (params.getSize() != 2)
    {
      this->errorType = "badArgument";
      return false;
    }

    // Check the resumption token is valid
    ResumptionToken token(params["resumptionToken"]);
    if (token.isValid())
    {
      // Everything is fine, and we don't continue further because this is an exclusive argument
      this->errorType = "";
      return true;
    }
    else
    {
      // There was an error with the resumption token
      this->errorType = "badResumptionToken";
      return false;
    }
  }

  // ----------------------------------------------------------------------------
  //  3. Handle any required arguments
  // ----------------------------------------------------------------------------

  // None!

  // ----------------------------------------------------------------------------
  // 4. Check any remaining arguments
  // ----------------------------------------------------------------------------

  // None!

  // If we've reached here everything must be fine
  this->errorType = "";
  return true;
}


bool listsetsaction::output_content(ostream &output, recptproto *protocol, oaiargs &params)
{
  // Reset variables
  this->setsOutput = 0;

  text_t position = "";

  // Process the resumptionToken if there is one
  if (params["resumptionToken"] != "")
  {
    ResumptionToken resumption_token(params["resumptionToken"]);
    position = resumption_token.getPosition();
  }

  // if no resumption token, output the super colls. Otherwise, start from 
  // the collection list correct position.
  // Assume for now that super coll list is always first in the list of sets,
  // and we never issue a resumption token part way through the list of super colls
  // TODO handle resumption tokens properly.
  text_t set_name ="";
  if (position == "") {
    text_tarray& supercolls = this->configuration->getSuperCollectionsList();
    text_tarray::iterator supercoll_iterator = supercolls.begin();
    while (supercoll_iterator != supercolls.end()) {
      set_name = (*supercoll_iterator);
      output_content_for_supercol(output, set_name);
      supercoll_iterator++;
    }
  }


  // Get a list of the collections available
  text_tarray& collections = this->configuration->getCollectionsList();
  if (collections.size() == 0)
  {
    if (this->setsOutput ==0) {
      // we had no super colls either
      return false;
    }
    return true;
  }

  // Get the current collection from the position value
  text_t collection_name = "";
  oaiclassifier::toGSDL(collection_name, position);

  // Find the starting collection
  text_tarray::iterator collection_iterator = collections.begin();
  while (collection_iterator != collections.end())
  {
    if (collection_name == "" || collection_name == *collection_iterator)
    {
      break;
    }

    collection_iterator++;
  }

  // Now loop through the remaining collections
  while (collection_iterator != collections.end())
  {
    collection_name = (*collection_iterator);

    // If we've output the desired number of records return a resumptionToken and we're done
    if (this->setsOutput == this->configuration->resumeAfter())
    {
      ResumptionToken resumption_token("", "", "", "", "", collection_name);

      // Don't add any whitespace around the resumption token as it can confuse harvesters/validators
      output << "  <resumptionToken>" << resumption_token.getResumptionTokenString() << "</resumptionToken>" << endl;
      return true;
    }

    // If output_content_for_col() returns false a resumption token has been output, so it's time to stop
    if (output_content_for_col(output, protocol, params, collection_name) == false)
    {
      return true;
    }

    collection_iterator++;
  }

  return true;
}

bool listsetsaction::output_content_for_supercol(ostream &output, text_t supercoll) {
    utf8outconvertclass utf8convert;
    output << utf8convert << "  <set>\n";
    output << utf8convert << "    <setSpec>" << supercoll << "</setSpec>\n";
    if (this->configuration->getSetName(supercoll) != "")
    {
      output << utf8convert << "    <setName>" << this->configuration->getSetName(supercoll) << "</setName>\n";
    }
    else
    {
      output << utf8convert << "    <setName>" << supercoll << "</setName>\n";
    }
    if (this->configuration->getSetDescription(supercoll) != "")
    {
      output << utf8convert << "    <setDescription>" << this->configuration->getSetDescription(supercoll) << "</setDescription>\n";
    }
    output << utf8convert << "  </set>\n";
    this->setsOutput++;
    return true;
}
  



bool listsetsaction::output_content_for_col(ostream &output, recptproto *protocol, oaiargs &params, text_t collection)
{
  utf8outconvertclass utf8convert;
  text_t position = "";

  // Process the resumptionToken if there is one
  if (params["resumptionToken"] != "")
  {
    ResumptionToken resumption_token(params["resumptionToken"]);
    position = resumption_token.getPosition();
  }

  // Get the list of sets in this collection
  // Collections should not contain too many sets otherwise this will use a lot of time and memory
  text_tset metadata;  // Must be empty for efficiency
  FilterResponse_t sets_response;
  get_children("browse", collection, "", metadata, false, protocol, sets_response, *this->logout);

  // Find the starting position, if necessary
  ResultDocInfo_tarray::iterator set_iterator = sets_response.docInfo.begin();
  if (this->setsOutput == 0)
  {
    while (set_iterator != sets_response.docInfo.end())
    {
      if (position == "" || position == (collection + ":" + (*set_iterator).OID))
      {
	break;
      }

      set_iterator++;
    }
  }

  // Output the collection as a set
  if (position == "" || position == collection)
  {
    output << utf8convert << "  <set>\n";
    output << utf8convert << "    <setSpec>" << collection << "</setSpec>\n";
    if (this->configuration->getSetName(collection) != "")
    {
      output << utf8convert << "    <setName>" << this->configuration->getSetName(collection) << "</setName>\n";
    }
    else
    {
      output << utf8convert << "    <setName>" << collection << "</setName>\n";
    }
    if (this->configuration->getSetDescription(collection) != "")
    {
      output << utf8convert << "    <setDescription>" << this->configuration->getSetDescription(collection) << "</setDescription>\n";
    }
    output << utf8convert << "  </set>\n";
    this->setsOutput++;
  }

  // Now loop through displaying the next matching records
  while (set_iterator != sets_response.docInfo.end())
  {
    text_t set = (*set_iterator).OID;

    // Only classifiers with supportsmemberof become OAI sets, for reasons I don't really understand
    text_tset set_metadata;
    set_metadata.insert("supportsmemberof");
    set_metadata.insert("Title");
    FilterResponse_t set_response;
    get_info(set, collection, "", set_metadata, false, protocol, set_response, *this->logout);

    if (set_response.docInfo[0].metadata["supportsmemberof"].values.size() > 0 && set_response.docInfo[0].metadata["supportsmemberof"].values[0] == "true")
    {
      // If we've output the desired number of records return a resumptionToken and we're done
      if (this->setsOutput == this->configuration->resumeAfter())
      {
	ResumptionToken resumption_token("", "", "", "", "", collection + ":" + set);

	// Don't add any whitespace around the resumption token as it can confuse harvesters/validators
	output << "  <resumptionToken>" << resumption_token.getResumptionTokenString() << "</resumptionToken>" << endl;
	return false;
      }

      // Otherwise output this set and increment the count
      output << utf8convert << "  <set>\n";
      output << utf8convert << "    <setSpec>" << collection << ":" << set << "</setSpec>\n";
      if (this->configuration->getSetName(collection + ":" + set) != "")
      {
	output << utf8convert << "    <setName>" << this->configuration->getSetName(collection + ":" + set) << "</setName>\n";
      }
      else
      {
	output << utf8convert << "    <setName>" << set_response.docInfo[0].metadata["Title"].values[0] << "</setName>\n";
      }
      if (this->configuration->getSetDescription(collection + ":" + set) != "")
      {
	output << utf8convert << "    <setDescription>" << this->configuration->getSetDescription(collection + ":" + set) << "</setDescription>\n";
      }
      output << utf8convert << "  </set>\n";
      this->setsOutput++;
    }

    set_iterator++;
  }

  return true;
}
