/**
 * \brief Deserializes a \c LIGOTimeGPS structure from a VOTable XML document
 *
 * This function takes a VOTable XML document and deserializes (extracts)
 * the \c LIGOTimeGPS structure identified by the given name.
 *
 * \param xmlDocument [in] Pointer to the VOTable XML document containing the structure
 * \param name [in] Unique identifier of the particular \c LIGOTimeGPS structure to be deserialized
 * \param ltg [out] Pointer to an empty \c  LIGOTimeGPS structure to store the deserialized instance
 *
 * \return \c XLAL_SUCCESS if the specified \c LIGOTimeGPS structure could be found and
 * deserialized successfully.
 *
 * \sa XLALVOTXML2LIGOTimeGPSByName
 * \sa XLALGetSingleNodeContentByXPath
 *
 * \author Oliver Bock\n
 * Albert-Einstein-Institute Hannover, Germany
 */
INT4
XLALVOTDoc2LIGOTimeGPSByName ( const xmlDocPtr xmlDocument, const CHAR *name, LIGOTimeGPS *ltg )
{
    /* set up local variables */
    CHAR *nodeContent = NULL;

    /* sanity checks */
    if(!xmlDocument) {
        XLALPrintError("Invalid input parameter: xmlDocument\n");
        XLAL_ERROR(XLAL_EINVAL);
    }
    if(!name || strlen(name) <= 0) {
        XLALPrintError("Invalid input parameter: name\n");
        XLAL_ERROR(XLAL_EINVAL);
    }
    if(!ltg) {
        XLALPrintError("Invalid input parameter: ltg\n");
        XLAL_ERROR(XLAL_EINVAL);
    }

    /* retrieve LIGOTimeGPS.gpsSeconds content */
    if ( (nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "gpsSeconds", VOT_PARAM, VOT_VALUE )) == NULL ) {
      XLAL_ERROR ( XLAL_EFUNC );
    }
    /* parse content */
    if( sscanf ( nodeContent, "%i", &ltg->gpsSeconds) == EOF ) {
      XLALFree(nodeContent);
      XLALPrintError("Invalid node content encountered: %s.gpsSeconds\n", name);
      XLAL_ERROR ( XLAL_EDATA );
    }
    XLALFree(nodeContent);

    /* retrieve LIGOTimeGPS.gpsNanoSeconds content */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "gpsNanoSeconds", VOT_PARAM, VOT_VALUE );
    if( !nodeContent || sscanf( nodeContent, "%i", &ltg->gpsNanoSeconds) == EOF) {
      if ( nodeContent ) XLALFree (nodeContent);
      XLALPrintError("Invalid node content encountered: %s.gpsNanoSeconds\n", name);
      XLAL_ERROR(XLAL_EDATA);
    }

    /* clean up*/
    XLALFree ( nodeContent );

    return XLAL_SUCCESS;

} /* XLALVOTDoc2LIGOTimeGPSByName() */
/**
 * \brief Deserializes a \c PulsarDopplerParams structure from a VOTable XML document
 *
 * This function takes a VOTable XML document and deserializes (extracts)
 * the \c PulsarDopplerParams structure identified by the given name.
 *
 * \param xmlDocument [in] Pointer to the VOTable XML document containing the structure
 * \param name [in] Unique identifier of the particular \c PulsarDopplerParams structure to be deserialized
 * \param pdp [out] Pointer to an empty \c PulsarDopplerParams structure to store the deserialized instance
 *
 * \return \c XLAL_SUCCESS if the specified \c PulsarDopplerParams structure could be found and
 * deserialized successfully.
 *
 * \sa XLALVOTXML2PulsarDopplerParamsByName
 * \sa XLALGetSingleNodeContentByXPath
 *
 * \author Oliver Bock\n
 * Albert-Einstein-Institute Hannover, Germany
 */
INT4
XLALVOTDoc2PulsarDopplerParamsByName ( const xmlDocPtr xmlDocument,
                                       const char *name,
                                       PulsarDopplerParams *pdp
                                       )
{
    /* set up local variables */
    CHAR childNameRefTime[NAMESTR_MAXLEN] = {0};
    CHAR *nodeContent = NULL;

    /* sanity checks */
    if(!xmlDocument) {
        XLALPrintError("Invalid input parameter: xmlDocument\n");
        XLAL_ERROR(XLAL_EINVAL);
    }
    if(!name || strlen(name) <= 0) {
        XLALPrintError("Invalid input parameter: name\n");
        XLAL_ERROR(XLAL_EINVAL);
    }
    if(!pdp) {
        XLALPrintError("Invalid input parameter: pdp\n");
        XLAL_ERROR(XLAL_EINVAL);
    }

    /* compile child name attribute (refTime) */
    if(!strncpy(childNameRefTime, name, NAMESTR_MAXLEN) || !strncat(childNameRefTime, ".refTime", NAMESTR_MAXLEN)) {
        XLALPrintError("Child attribute preparation failed: PulsarDopplerParams.refTime\n");
        XLAL_ERROR(XLAL_EFAILED);
    }

    /* retrieve PulsarDopplerParams.refTime */
    if(XLALVOTDoc2LIGOTimeGPSByName(xmlDocument, childNameRefTime, &pdp->refTime)) {
        XLALPrintError("Error parsing XML document content: %s.refTime\n", childNameRefTime);
        XLAL_ERROR(XLAL_EFAILED);
    }

    /* retrieve PulsarDopplerParams.Alpha */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "Alpha", VOT_PARAM, VOT_VALUE );
    if(!nodeContent || sscanf( nodeContent, "%lf", &pdp->Alpha) == EOF) {
      if(nodeContent) XLALFree(nodeContent);
      XLALPrintError("Invalid node content encountered: %s.Alpha\n", name);
      XLAL_ERROR(XLAL_EDATA);
    }
    XLALFree ( nodeContent );

    /* retrieve PulsarDopplerParams.Delta */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "Delta", VOT_PARAM, VOT_VALUE );
    if(!nodeContent || sscanf( nodeContent, "%lf", &pdp->Delta) == EOF) {
        if(nodeContent) XLALFree(nodeContent);
        XLALPrintError("Invalid node content encountered: %s.Delta\n", name);
        XLAL_ERROR(XLAL_EDATA);
    }
    XLALFree ( nodeContent );

    /* retrieve PulsarDopplerParams.fkdot */
    if ( XLALVOTDoc2PulsarSpinsByName(xmlDocument, name, "fkdot", pdp->fkdot)) {
      XLALPrintError("Error parsing XML document content: %s.fkdot\n", name);
      XLAL_ERROR(XLAL_EFAILED);
    }


    /* compile child name attribute (tp) */
    CHAR childName[NAMESTR_MAXLEN];
    if ( snprintf ( childName, NAMESTR_MAXLEN, "%s.%s", name, "tp") >= NAMESTR_MAXLEN ) {
      XLALPrintError("Child attribute preparation failed: PulsarDopplerParams.tp\n");
      XLAL_ERROR(XLAL_EFAILED);
    }

    /* retrieve PulsarDopplerParams.tp */
    if ( XLALVOTDoc2LIGOTimeGPSByName(xmlDocument, childName, &pdp->tp)) {
      XLALPrintError("Error parsing XML document content: %s.tp\n", childName);
      XLAL_ERROR(XLAL_EFAILED);
    }

    /* retrieve PulsarDopplerParams.argp content */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "argp", VOT_PARAM, VOT_VALUE );
    if( !nodeContent || sscanf( nodeContent, "%lf", &pdp->argp) == EOF) {
      if ( nodeContent ) XLALFree (nodeContent);
      XLALPrintError("Invalid node content encountered: %s.argp\n", name);
      XLAL_ERROR(XLAL_EDATA);
    }
    XLALFree (nodeContent);

    /* retrieve PulsarDopplerParams.asini content */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "asini", VOT_PARAM, VOT_VALUE );
    if( !nodeContent || sscanf( nodeContent, "%lf", &pdp->asini) == EOF) {
      if ( nodeContent ) XLALFree (nodeContent);
      XLALPrintError("Invalid node content encountered: %s.asini\n", name);
      XLAL_ERROR(XLAL_EDATA);
    }
    XLALFree (nodeContent);

    /* retrieve PulsarDopplerParams.ecc content */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "ecc", VOT_PARAM, VOT_VALUE );
    if( !nodeContent || sscanf( nodeContent, "%lf", &pdp->ecc) == EOF) {
      if ( nodeContent ) XLALFree (nodeContent);
      XLALPrintError("Invalid node content encountered: %s.ecc\n", name);
      XLAL_ERROR(XLAL_EDATA);
    }
    XLALFree (nodeContent);

    /* retrieve PulsarDopplerParams.period content */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, name, "period", VOT_PARAM, VOT_VALUE );
    if(!nodeContent || sscanf( nodeContent, "%lf", &pdp->period) == EOF ) {
      if(nodeContent) XLALFree (nodeContent);
      XLALPrintError("Invalid node content encountered: %s.period\n", name);
      XLAL_ERROR(XLAL_EDATA);
    }
    XLALFree(nodeContent);

    return XLAL_SUCCESS;

} /* XLALVOTDoc2PulsarDopplerParamsByName() */
/**
 * \brief Deserializes a \c PulsarSpins array from a VOTable XML document
 *
 * This function takes a VOTable XML document and deserializes (extracts) the \c PulsarSpins array
 * (found as a \c PARAM element with the specified \c RESOURCE parent-path) identified by the given name.
 *
 * \return \c XLAL_SUCCESS if the specified \c PulsarSpins array could be found and
 * deserialized successfully.
 *
 * \sa XLALVOTXML2PulsarDopplerParamsByName
 * \sa XLALGetSingleVOTResourceParamAttribute
 *
 * \author Oliver Bock, Reinhard Prix\n
 * Albert-Einstein-Institute Hannover, Germany
 */
INT4
XLALVOTDoc2PulsarSpinsByName ( const xmlDocPtr xmlDocument,	/**< [in] Pointer to the VOTable XML document containing the array */
                               const char *resourcePath,	/**< [in] (optional) parent RESOURCE path */
                               const char *paramName,		/**< [in] Name of the PulsarSpins PARAM element */
                               PulsarSpins spins		/**< [out] Pointer to an empty \c PulsarSpins array to store the deserialized instance */
                               )
{
    /* set up local variables */
    CHAR *nodeContent = NULL;
    CHAR *nodeContentWorker = NULL;
    int arraySize = 0;
    int i;

    /* sanity check */
    if(!xmlDocument) {
        XLALPrintError("Invalid input parameters: xmlDocument\n");
        XLAL_ERROR(XLAL_EINVAL);
    }
    if(!paramName) {
        XLALPrintError("Invalid input parameters: paramName\n");
        XLAL_ERROR(XLAL_EINVAL);
    }
    if(!spins) {
        XLALPrintError("Invalid input parameters: spins\n");
        XLAL_ERROR(XLAL_EINVAL);
    }

    /* retrieve arraysize (number of pulsar spins) */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_ARRAYSIZE );
    if(!nodeContent || sscanf( nodeContent, "%i", &arraySize) == EOF || arraySize == 0) {
      if(nodeContent) XLALFree(nodeContent);
      XLALPrintError("Invalid node content encountered: %s.%s.arraysize\n", resourcePath, paramName);
      XLAL_ERROR(XLAL_EDATA);
    }
    XLALFree(nodeContent);

    /* retrieve pulsar spin array (string) */
    nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_VALUE );
    if(!nodeContent) {
      XLALPrintError("Invalid node content encountered: %s.%s\n", resourcePath, paramName);
      XLAL_ERROR(XLAL_EDATA);
    }

    /* finally, parse and store individual spin values */
    nodeContentWorker = strtok(nodeContent, " ");
    for(i = 0; i < arraySize; i++ )
      {
        /* scan current item */
        if(sscanf((char*)nodeContentWorker, "%lf", &spins[i]) == EOF) {
          XLALFree(nodeContent);
          XLALPrintError("Invalid node content encountered: %s.%s[%i]\n", resourcePath, paramName, i);
          XLAL_ERROR(XLAL_EDATA);
        }

        /* advance to next item */
        nodeContentWorker = strtok(NULL, " ");
      } /* for i < arraySize */

    /* sanity check */
    if(i != arraySize) {
      XLALFree(nodeContent);
      XLALPrintError("Invalid node content encountered: %s.%s (found %i of %i items)\n", resourcePath, paramName, i, arraySize);
      XLAL_ERROR(XLAL_EDATA);
    }

    /* clean up*/
    XLALFree(nodeContent);

    return XLAL_SUCCESS;

} /* XLALVOTDoc2PulsarSpinsByName() */
/**
 * \brief Deserializes a \c gsl_matrix from a VOTable XML document
 *
 * This function takes a VOTable XML document and deserializes (extracts) the \c gsl_matrix
 * (found as a \c PARAM element in the specified \c RESOURCE element) identified by the given name.
 *
 * \return \c pointer to deserialized \c gsl_matrix, which is allocated by this function.
 *
 * \author Reinhard Prix\n
 * Albert-Einstein-Institute Hannover, Germany
 */
gsl_matrix *
XLALVOTDoc2gsl_matrixByName ( const xmlDocPtr xmlDocument,	/**< [in] Pointer to the VOTable XML document containing the array */
                              const char *resourcePath,		/**< [in] (optional) parent RESOURCE path */
                              const char *paramName,		/**< [in] name of of the particular PARAM \c gsl_vector to be deserialized */
                              const CHAR *unitName		/**< [in] Optional unit-name. If != NULL, it MUST agree with the units specified in XML */
                              )
{
  CHAR *nodeContent = NULL;
  CHAR *nodeContentWorker = NULL;
  int row, col, numRows, numCols;
  gsl_matrix *matrix = NULL;
  double el;

  /* input sanity check */
  if( !xmlDocument ) {
    XLALPrintError ( "%s: Invalid input parameters: xmlDocument\n", __func__);
    XLAL_ERROR_NULL (XLAL_EINVAL);
  }
  if( !paramName ) {
    XLALPrintError("%s: Invalid input parameters: paramName\n", __func__);
    XLAL_ERROR_NULL (XLAL_EINVAL);
  }

  /* retrieve arraysize (number of matrixor elements) */
  nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_ARRAYSIZE );
  if(!nodeContent || sscanf( nodeContent, "%dx%d", &numCols, &numRows) == EOF || numRows == 0 || numCols == 0 ) {
    if(nodeContent) XLALFree(nodeContent);
    XLALPrintError("%s: Invalid node content encountered: %s.%s.arraysize\n", __func__, resourcePath, paramName);
    XLAL_ERROR_NULL (XLAL_EDATA);
  }
  XLALFree(nodeContent);

  /* retrieve and check unitName, if given by caller */
  if ( unitName )
    {
      if ( (nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_UNIT )) == NULL ) {
	XLALPrintError("%s: failed to find %s.%s.unit, but caller requested '%s'.\n\n", __func__, resourcePath, paramName, unitName );
	XLAL_ERROR_NULL ( XLAL_EDATA );
      }
      /* check that unit in XML agrees with caller's requirement */
      if ( strcmp ( unitName, nodeContent ) ) {
	XLALPrintError("%s: %s.%s.unit = '%s', but caller requested '%s'. Sorry, I can't do unit conversions.\n",
		       __func__, resourcePath, paramName, nodeContent, unitName );
	XLALFree(nodeContent);
	XLAL_ERROR_NULL ( XLAL_EDATA );
      }
      XLALFree(nodeContent);
    } /* check unitName */


  /* retrieve pulsar gsl_matrix array (string) */
  if ( (nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_VALUE )) == NULL ) {
    XLALPrintError("%s: Invalid node content encountered: %s.%s\n", __func__, resourcePath, paramName);
    XLAL_ERROR_NULL (XLAL_EDATA);
  }

  /* allocate output gsl_matrix */
  if ( (matrix = gsl_matrix_calloc ( numRows, numCols )) == NULL ) {
    XLALPrintError ("%s: failed to gsl_matrix_calloc(%d,%d).\n", __func__, numRows, numCols );
    XLAL_ERROR_NULL (XLAL_ENOMEM);
  }

  /* finally, parse and store individual matrix elements */
  if ( (nodeContentWorker = strtok(nodeContent, " ;")) == NULL ) {
    gsl_matrix_free ( matrix );
    XLALFree(nodeContent);
    XLALPrintError ("%s: failed to parse first array element in node: %s.%s.\n",
		    __func__, resourcePath, paramName );
    XLAL_ERROR_NULL (XLAL_EDATA);
  }
  for (row = 0; row < numRows; row++)
    {
      for (col=0; col < numCols; col ++ )
	{
	  /* scan current item */
	  if ( sscanf( nodeContentWorker, "%lf", &el) == EOF) {
	    XLALFree(nodeContent);
	    gsl_matrix_free ( matrix );
	    XLALPrintError("%s: Invalid node content encountered: %s.%s[%d,%d] = '%s'\n", __func__, resourcePath, paramName, row, col, nodeContentWorker );
	    XLAL_ERROR_NULL (XLAL_EDATA);
	  }

	  gsl_matrix_set ( matrix, row, col, el );

	  /* advance to next item */
	  if ( (row < numRows-1) || (col < numCols-1) )
	    {
	      if ( (nodeContentWorker = strtok(NULL, " ;")) == NULL ) {
		gsl_matrix_free ( matrix );
		XLALFree(nodeContent);
		XLALPrintError ("%s: failed to parse array (%d,%d) element in node: %s.%s'.\n", __func__, row, col, resourcePath, paramName );
		XLAL_ERROR_NULL (XLAL_EDATA);
	      }
	    } /* not parsed last element yet: advance to next one */

	} /* for col < numCols */

    } /* for row < numRows */

  /* clean up*/
  XLALFree(nodeContent);

  return matrix;

} /* XLALVOTDoc2gsl_matrixByName() */
/**
 * \brief Deserializes a \c gsl_vector from a VOTable XML document
 *
 * This function takes a VOTable XML document and deserializes (extracts) the \c gsl_vector
 * (found as a \c PARAM element in the specified \c RESOURCE element) identified by the given name.
 *
 * \return \c pointer to deserialized \c gsl_vector, which is allocated by this function.
 *
 * \author Reinhard Prix\n
 * Albert-Einstein-Institute Hannover, Germany
 */
gsl_vector *
XLALVOTDoc2gsl_vectorByName ( const xmlDocPtr xmlDocument,	/**< [in] Pointer to the VOTable XML document containing the array */
                              const char *resourcePath,		/**< [in] (optional) parent RESOURCE path */
                              const char *paramName,		/**< [in] name of of the particular PARAM \c gsl_vector to be deserialized */
                              const CHAR *unitName		/**< [in] Optional unit-name. If != NULL, it MUST agree with the units specified in XML */
                              )
{
  CHAR *nodeContent = NULL;
  CHAR *nodeContentWorker = NULL;
  int i, dim;
  gsl_vector *vect = NULL;
  double el;

  /* input sanity check */
  if( !xmlDocument ) {
    XLALPrintError ( "%s: Invalid input parameters: xmlDocument\n", __func__);
    XLAL_ERROR_NULL (XLAL_EINVAL);
  }

  if( !paramName ) {
    XLALPrintError("%s: Invalid input parameters: paramName\n", __func__);
    XLAL_ERROR_NULL (XLAL_EINVAL);
  }

  /* retrieve arraysize (number of vector elements) */
  nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_ARRAYSIZE);
  if(!nodeContent || sscanf(nodeContent, "%d", &dim) == EOF || dim == 0) {
    if(nodeContent) XLALFree(nodeContent);
    XLALPrintError("%s: Invalid node content encountered: %s.%s.arraysize\n", __func__, resourcePath, paramName);
    XLAL_ERROR_NULL (XLAL_EDATA);
  }
  XLALFree(nodeContent);

  /* retrieve and check unitName, if given by caller */
  if ( unitName )
    {
      if ( (nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_UNIT )) == NULL ) {
	XLALPrintError("%s: failed to find %s.%s.unit, but caller requested '%s'.\n\n", __func__, resourcePath, paramName, unitName );
	XLAL_ERROR_NULL ( XLAL_EDATA );
      }
      /* check that unit in XML agrees with caller's requirement */
      if ( strcmp ( unitName, nodeContent ) ) {
	XLALPrintError("%s: %s.%s.unit = '%s', but caller requested '%s'. Sorry, I can't do unit conversions.\n",
		       __func__, resourcePath, paramName, nodeContent, unitName );
	XLALFree (nodeContent);
	XLAL_ERROR_NULL ( XLAL_EDATA );
      }

      XLALFree (nodeContent);
    } /* check unitName */

  /* retrieve pulsar gsl_vector array (string) */
  if ( (nodeContent = XLALReadVOTAttributeFromNamedElement ( xmlDocument, resourcePath, paramName, VOT_PARAM, VOT_VALUE )) == NULL ) {
    XLALPrintError("%s: Invalid node content encountered: %s.%s\n", __func__, resourcePath, paramName);
    XLAL_ERROR_NULL (XLAL_EDATA);
  }

  /* allocate output gsl_vector */
  if ( (vect = gsl_vector_calloc ( dim )) == NULL ) {
    XLALPrintError ("%s: failed to gsl_vector_calloc(%d).\n", __func__, dim );
    XLAL_ERROR_NULL (XLAL_ENOMEM);
  }

  /* finally, parse and store individual vector elements */
  if ( (nodeContentWorker = strtok(nodeContent, " ")) == NULL ) {
    gsl_vector_free ( vect );
    XLALFree(nodeContent);
    XLALPrintError ("%s: failed to parse first array element in node: %s.%s.\n", __func__, resourcePath, paramName );
    XLAL_ERROR_NULL (XLAL_EDATA);
  }
  for(i = 0; i < dim; i++)
    {
      /* scan current item */
      if ( sscanf( nodeContentWorker, "%lf", &el) == EOF) {
	XLALFree(nodeContent);
	gsl_vector_free ( vect );
	XLALPrintError("%s: Invalid node content encountered: %s.%s[%i] = '%s'\n", __func__, resourcePath, paramName, i, nodeContentWorker );
	XLAL_ERROR_NULL (XLAL_EDATA);
      }

      gsl_vector_set ( vect, i, el );

      /* advance to next item */
      if ( i < dim-1 )
	{
	  if ( (nodeContentWorker = strtok(NULL, " ")) == NULL ) {
	    gsl_vector_free ( vect );
	    XLALFree(nodeContent);
	    XLALPrintError ("%s: failed to parse %d-th array element in node: %s.%s'.\n", __func__, i, resourcePath, paramName );
	    XLAL_ERROR_NULL (XLAL_EDATA);
	  }
	} /* if not last element yet: advance */

    } /* for i < dim */

  /* clean up*/
  XLALFree(nodeContent);

  return vect;

} /* XLALVOTDoc2gsl_vectorByName() */