/* Copyright (C) 2003-2008 by the 
 * Institute of Global Environment and Society (IGES)
 * See the file COPYRIGHT for more information.   
 *
 *
 * gaReports.cc  - The library's container for station data reports
 *
 * Last modified: $Date: 2008/07/25 20:43:25 $ 
 * Revision for this file: $Revision: 1.6 $
 * Release name: $Name: gadods-2_0 $
 * Original for this file: $Source: /homes/cvsroot/gadods/gadods/src/gaReports.cc,v $
 */

#include <iostream>
#include "gaReports.h"
#include "gaUtils.h"
#include "Grid.h"
#include "Array.h"
#include "BaseType.h"
#include "dods-datatypes.h"
#include "gaBaseTypes.h" 

using namespace std;

gaReports::gaReports(DDS *_data, string _url) :
  data(_data), url(_url) { 

  parseVars(variables, *data, numIVars);
  stidIndex = getVarIndex("stid");
  timeIndex = getVarIndex("time");
  latIndex = getVarIndex("lat");
  lonIndex = getVarIndex("lon");
  levIndex = getVarIndex("lev");
  
  hasLevels = false;
  reports = findOuterSequence(*_data);
  if (reports) {
    levels = findInnerSequence(reports);
    hasLevels = (levels != NULL);
  }

}

gaReports::~gaReports() {}

GADAP_DATASET gaReports::getDataset() {
  return dataset;
}

void gaReports::setDataset(GADAP_DATASET _dataset) {
  dataset = _dataset;
}

GADAP_STN_QUERY *gaReports::getQuery() {
  return query;
}

const char * gaReports::getURL() {
  return url.c_str();
}

void gaReports::setQuery(GADAP_STN_QUERY *_query) {
  query = _query;
}

int gaReports::getNumVars() {
  return variables.size();
}

int gaReports::getNumLIVars() {
  return numIVars;
}

const char *gaReports::getVarName(int index) {
  if (index < 0 || index > variables.size() - 1) {
    return NULL;
  } else {
    return variables[index].name.c_str();
  }
}

int gaReports::getVarIndex(const char *varname) {
  string varname_string(varname);
  for (int i = 0; i < variables.size(); i++) {
    if (variables[i].name == varname_string) {
      return i;
    }
  }  
  return errval(GADAP_ERR_NAME_NOT_FOUND);
}

int gaReports::getNumReports() {
  return reports->number_of_rows();
}

int gaReports::getNumLevels(int rpt_index) {
  if (!hasLevels) {
    return 0;
  }

  Sequence *levels = getLevels(rpt_index);
  if (levels == NULL) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  } else {
    return levels->number_of_rows();
  }
}

int gaReports::getVarDbl(int rpt_index, int lev_index, int var_index, int array_index,
			 double *value) {

  BaseType * var = getVarPtr(rpt_index, lev_index, var_index);
  if (!var) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  }
 	      
  var = unpackArray(var, array_index);
  if (!var) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  }

  if (!var->is_simple_type()) {
    return errval(GADAP_ERR_BAD_VARIABLE_TYPE);
  }

  char * buf = NULL;

  if (var->type() == dods_str_c || var->type() == dods_url_c) {
    // could try to parse string into float here if we wanted (for JGOFS?)
    return errval(GADAP_ERR_BAD_VARIABLE_TYPE);
  } else {
    var->buf2val((void**)&buf);
  }

  // for numeric values, use typedefs from dods-datatypes.h to
  // cast value into a double (which should be big enough for anything in
  // the current DAP menagerie)
  switch (var->type()) {

  case dods_byte_c:
    (*value) = (double)(*((dods_byte *)buf));
    break;
  case dods_int16_c:
    (*value) = (double)(*((dods_int16 *)buf));
    break;
  case dods_uint16_c:
    (*value) = (double)(*((dods_uint16 *)buf));
    break;
  case dods_int32_c:
    (*value) = (double)(*((dods_int32*)buf));
    break;
  case dods_uint32_c:
    (*value) = (double)(*((dods_uint32 *)buf));
    break;
  case dods_float32_c:
    (*value) = (double)(*((dods_float32 *)buf));
    break;
  case dods_float64_c:
    (*value) = (double)(*((dods_float64 *)buf));
    break;
  default:
    return errval(GADAP_ERR_BAD_VARIABLE_TYPE);
  }

  free(buf);

  return GADAP_SUCCESS;  

}

const char * 
gaReports::getVarStr(int rpt_index, int lev_index, int var_index, int array_index) {

  BaseType * var = getVarPtr(rpt_index, lev_index, var_index);
  if (!var) {
    return NULL;
  }

  var = unpackArray(var, array_index);
  if (!var) {
    return NULL;
  }

  if (!var->is_simple_type()) {
    return NULL;
  }
 
  if (var->type() == dods_str_c) {
    //     gaStr * strVar = dynamic_cast<gaStr *>(var);
    //     return strVar->getStringVal()->c_str();

    //  I had some problems with strings here.
    //  If the above code is causing problems the following may work
    //  better. strval needs to be a field of the gaReports class so
    //  it can be deleted later. - jw 
    /*     var->buf2val((void **)&strval); */

    /* AMS; is the user resposibility to deallocate the string */
    /* JMA; this is done by GrADS in dapget() after the call to gadap_r_valstr */
    string *buf = NULL;
    int n = var->buf2val((void **)&buf);
    char *str = (char *) malloc ( n + 1);
    strcpy(str,buf->c_str());
    free(buf);
    return str;

  } else if (var->type() == dods_url_c) {
    //     gaUrl * urlVar = dynamic_cast<gaUrl *>(var);
    //     return urlVar->getStringVal()->c_str();

    //  I had some problems with strings here.
    //  If the above code is causing problems the following may work
    //  better. strval needs to be a field of the gaReports class so
    //  it can be deleted later. - jw 
    /* var->buf2val((void **)&strval); */

    /* AMS; is the user resposibility to deallocate the string */
    /* JMA; this is done by GrADS in dapget() after the call to gadap_r_valstr */
    string *buf = NULL;
    int n = var->buf2val((void **)&buf);
    char *str = (char *) malloc ( n + 1);
    strcpy(str,buf->c_str());
    free(buf);
    return str;

  } else {

    // no need to provide string representation for numeric types - this
    // can easily be done with printf by the client. for us to do it
    // would complicate the memory allocation situation a lot
    return NULL;
  }
}


 
/** Returns the BaseType object at the requested indices */
BaseType * 
gaReports::getVarPtr(int rpt_index, int lev_index, int var_index) {
  if (var_index < 0 || var_index > variables.size() - 1 || 
      rpt_index < 0 || rpt_index > reports->number_of_rows() - 1) {
    return NULL;
  }

  if (var_index < numIVars) { // level independent
    return reports->var_value(rpt_index, var_index);

  } else { // multi-level
    Sequence *levels = getLevels(rpt_index);
    if (var_index >= numIVars && 
	(lev_index < 0 || lev_index > levels->number_of_rows() - 1)) {
      return NULL;
    }   
    return levels->var_value(lev_index, var_index - numIVars);
  } 

}

/** Returns the inner sequence for the report collection, if any */
Sequence *gaReports::getLevels(int rpt_index) {
  if (rpt_index < 0 || rpt_index > reports->number_of_rows() - 1) {
    return NULL;
  } else {
    return dynamic_cast<Sequence*>(reports->var_value(rpt_index, levels->name()));
  }
}

/* Extracts a single element from an Array or Grid variable. The
 * return value is the nth element of the array, or the nth element of
 * the main array for a grid. Multi-dimensional arrays are treated as
 * 1-D vectors using the Vector->var(index) interface. This routine is
 * a no-op for non-array variables.  */
BaseType * gaReports::unpackArray(BaseType * var, int array_index) {

  // extract the main array out of a grid
  if (var->type() == dods_grid_c) {
    Grid * grid = dynamic_cast<Grid *>(var);
    var = grid->array_var();
  }

  // extract the requested element from an array
  if (var->type() == dods_array_c) {
    Array * array = dynamic_cast<Array *>(var);
    if (array_index >= 0 || array_index < array->length()) {
      var = array->var(array_index);
    }
  }

  // var is left as is if not grid or array

  return var;
}


