/**********************************************************************
 *
 * recptprototools.cpp -- 
 * Copyright (C) 1999-2008  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 "recptprototools.h"
#include <assert.h>


// get_info does a protocol call and returns (in response) the metadata 
// associated with OID. Metadata should be loaded with whatever
// metadata fields are to be requested.

// THIS FILE IS A CANDIDATE FOR REFACTORING: merge get_info methods

// overloaded, to allow "custom" filter options.
bool get_info (const text_t &OID, const text_t &collection, const text_t &lang,
	       const text_tset &metadata, bool getParents,
	       recptproto *collectproto, FilterResponse_t &response, 
	       ostream &logout, int filterResultOptions)
{
  response.clear();

  comerror_t err = noError;
  FilterRequest_t request;
  request.clear();

  request.filterName = "NullFilter";
  request.filterLang = lang;
  request.filterResultOptions = filterResultOptions | FRmetadata;
  request.getParents = getParents;
  request.fields = metadata;
  request.docSet.push_back (OID);
  assert (collectproto != NULL);

  collectproto->filter (collection, request, response, err, logout);
  if (err != noError)
  {
    outconvertclass text_t2ascii;
    logout << text_t2ascii
	   << "Error: call to filter failed for " << OID
	   << " in recptprototools::get_info (" 
	   << get_comerror_string (err) << ")\n";
    return false;
  }
  
  return true;
}

bool get_oai_info (const text_t &OID, const text_t &collection, const text_t &lang,
	       const text_tset &metadata, bool getParents,
	       recptproto *collectproto, FilterResponse_t &response, 
		   ostream &logout)
{
  // set filteroption to FROAI
  return get_info(OID, collection, lang, metadata, getParents, collectproto, response, logout, FROAI);
}

bool get_info (const text_t &OID, const text_t &collection, const text_t &lang,
	       const text_tset &metadata, const OptionValue_tarray &options,
	       bool getParents,
	       recptproto *collectproto, FilterResponse_t &response, 
	       ostream &logout)
{
  response.clear();

  comerror_t err = noError;
  FilterRequest_t request;

  request.filterName = "NullFilter";
  request.filterLang = lang;
  request.filterResultOptions = FRmetadata;
  request.getParents = getParents;
  request.filterOptions = options;
  request.fields = metadata;
  request.docSet.push_back (OID);

  assert (collectproto != NULL);
  collectproto->filter (collection, request, response, err, logout);
  if (err != noError)
  {
    outconvertclass text_t2ascii;
    logout << text_t2ascii
	   << "Error: call to filter failed for " << OID
	   << " in recptprototools::get_info (" 
	   << get_comerror_string (err) << ")\n";
    return false;
  }
  
  return true;
}


bool get_info (const text_tarray &OIDs, const text_t &collection, const text_t &lang,
	       const text_tset &metadata, bool getParents,
	       recptproto *collectproto, FilterResponse_t &response, 
	       ostream &logout)
{
  response.clear();
  if (OIDs.empty()) return true;

  comerror_t err = noError;
  FilterRequest_t request;

  request.filterName = "NullFilter";
  request.filterLang = lang;
  request.filterResultOptions = FRmetadata;
  request.getParents = getParents;
  request.fields = metadata;

  request.docSet = OIDs;
  
  collectproto->filter (collection, request, response, err, logout);
  if (err != noError)
  {
    outconvertclass text_t2ascii;
    logout << text_t2ascii
	   << "Error: call to filter failed in recptprototools::get_info (" 
	   << get_comerror_string (err) << ")\n";
    return false;
  }

  return true;
}


// has_children returns true if OID has children
bool has_children (const text_t &OID, const text_t &collection, const text_t &lang,
		   recptproto *collectproto, ostream &logout) {

  FilterResponse_t response;
  text_tset metadata;
  metadata.insert ("haschildren");

  if (get_info (OID, collection, lang, metadata, true, collectproto,	response, logout))
  {
    if (response.docInfo[0].metadata["haschildren"].values[0] == "1")
    {
      return true;
    }
  }

  return false;
}


// get_children does a protocol call and returns (in response) the OIDs and 
// metadata of all the children of OID. The metadata set should be loaded
// with whatever metadata fields are to be requested.

bool get_children (const text_t &OID, const text_t &collection, const text_t &lang,
		   const text_tset &metadata, bool getParents,
		   recptproto *collectproto, FilterResponse_t &response, 
		   ostream &logout, int filterResultOptions)
{
  response.clear();

  comerror_t err = noError;
  FilterRequest_t request;
  OptionValue_t option;

  option.name = "ParentNode";
  option.value = OID;
  request.filterOptions.push_back (option);
  request.filterName = "BrowseFilter";
  request.filterLang = lang;
  request.filterResultOptions = filterResultOptions | FROID;

  // Efficiency improvement: only get the filter to retrieve metadata if some has been requested
  // Otherwise, the filter makes an unnecessary request to the database for each child node
  // By Michael Dewsnip, DL Consulting Ltd
  if (metadata.size() > 0)
  {
    request.filterResultOptions |= FRmetadata;
  }

  request.fields = metadata;
  request.getParents = getParents;

  collectproto->filter (collection, request, response, err, logout);

  if (err != noError)
  {
    outconvertclass text_t2ascii;
    logout << text_t2ascii
	   << "Error: call to filter failed for " << OID 
	   << " in recptprototools::get_children (" 
	   << get_comerror_string (err) << ")\n";
    return false;
  }

  return true;
}


static void recurse_contents (ResultDocInfo_t section, const bool &is_classify, 
			      const text_t &collection, const text_t &lang,
			      const text_tset &metadata, 
			      recptproto *collectproto, FilterResponse_t &response, 
			      ostream &logout)
{
  int haschildren = section.metadata["haschildren"].values[0].getint();
  const text_t &doctype = section.metadata["doctype"].values[0];

  if ((haschildren == 1) && ((!is_classify) || (doctype == "classify")))
  {
    FilterResponse_t tmp;
    bool getParents = true;
    get_children (section.OID, collection, lang, metadata, getParents, collectproto, tmp, logout);
    ResultDocInfo_tarray::iterator thisdoc = tmp.docInfo.begin();
    ResultDocInfo_tarray::iterator lastdoc = tmp.docInfo.end();
    while (thisdoc != lastdoc)
    {
      response.docInfo.push_back (*thisdoc);
      recurse_contents (*thisdoc, is_classify, collection, lang, metadata, collectproto, response, logout);
      ++thisdoc;
    }
  }
}


// get_contents returns OIDs and metadata of all contents 
// below (and including) OID.
void get_contents (const text_t &topOID, const bool &is_classify, 
		   text_tset &metadata, const text_t &collection, const text_t &lang,
		   recptproto *collectproto, FilterResponse_t &response, 
		   ostream &logout)
{
  if (topOID.empty()) return;
  response.clear();

  metadata.insert ("haschildren");
  metadata.insert ("doctype");

  // get topOIDs info
  if (get_info (topOID, collection, lang, metadata, true, collectproto, response, logout))
  {
    recurse_contents (response.docInfo[0], is_classify, collection, lang, metadata, collectproto, response, logout);
  }
}


bool get_metadata_values (const text_t metadata_elements, const text_t metadata_value_filter,
			  const text_t metadata_value_grouping_expression, const text_t &collection,
			  recptproto *collectproto, FilterResponse_t &response, ostream &logout)
{
  response.clear();

  comerror_t err = noError;
  FilterRequest_t request;
  request.clear();

  request.filterName = "SQLBrowseFilter";
  request.requestParams = "GetMetadataValues";

  OptionValue_t request_option;
  request_option.name = "MetadataElements";
  request_option.value = metadata_elements;
  request.filterOptions.push_back (request_option);
  request_option.name = "MetadataValueFilter";
  request_option.value = metadata_value_filter;
  request.filterOptions.push_back (request_option);
  request_option.name = "MetadataValueGroupingExpression";
  request_option.value = metadata_value_grouping_expression;
  request.filterOptions.push_back (request_option);

  assert (collectproto != NULL);
  collectproto->filter (collection, request, response, err, logout);
  if (err != noError)
  {
    outconvertclass text_t2ascii;
    logout << text_t2ascii
	   << "Error: call to filter failed for " << metadata_elements
	   << " in recptprototools::get_metadata_values (" 
	   << get_comerror_string (err) << ")\n";
    return false;
  }
  
  return true;
}


bool get_documents_with_metadata_value (const text_t metadata_elements, const text_t metadata_value,
					const text_t sort_by_metadata_element_name, const text_t &collection,
					recptproto *collectproto, FilterResponse_t &response, ostream &logout)
{
  response.clear();

  comerror_t err = noError;
  FilterRequest_t request;
  request.clear();

  request.filterName = "SQLBrowseFilter";
  request.requestParams = "GetDocumentsWithMetadataValue";

  OptionValue_t request_option;
  request_option.name = "MetadataElements";
  request_option.value = metadata_elements;
  request.filterOptions.push_back (request_option);
  request_option.name = "MetadataValue";
  request_option.value = metadata_value;
  request.filterOptions.push_back (request_option);
  request_option.name = "SortByMetadataElement";
  request_option.value = sort_by_metadata_element_name;
  request.filterOptions.push_back (request_option);

  assert (collectproto != NULL);
  collectproto->filter (collection, request, response, err, logout);
  if (err != noError)
  {
    outconvertclass text_t2ascii;
    logout << text_t2ascii
	   << "Error: call to filter failed for " << metadata_elements << " = " << metadata_value
	   << " in recptprototools::get_documents_with_metadata_value (" 
	   << get_comerror_string (err) << ")\n";
    return false;
  }
  
  return true;
}
