/* Copyright (C) 2003-2008 by the 
 * Institute of Global Environment and Society (IGES)
 * See the file COPYRIGHT for more information.   
 *
 *
 * gaConnect.cc  - an implementation of the OPeNDAP Connect class
 *
 * Last modified: $Date: 2008/07/25 20:43:25 $ 
 * Revision for this file: $Revision: 1.8 $
 * Release name: $Name: gadods-2_0 $
 * Original for this file: $Source: /homes/cvsroot/gadods/gadods/src/gaConnect.cc,v $
 */

#include "gadap.h"
#include "gaConnect.h"
#include "gaUtils.h"
#include "DataDDS.h"

#include <cstdlib>
#include <iostream>
#include <strstream>
#include <algorithm>

gaConnect::gaConnect(string name, string uname, string password) 
    : Connect(name, uname, password), d_dds(0) {

  BaseTypeFactory factory;
  d_dds.set_factory(&factory); // to avoid seg faults

  url = name;
  request_dds(d_dds);
  request_das(d_das);
  parseVars(variables, d_dds, numIVars);
  buildAttrList();

}

/*****************************************************************
 * Identifying variables 
 */

/* Returns the index of the given variable, or
 * GADAP_ERR_NAME_NOT_FOUND Uses the fully qualified variable name
 * (including inner and outer seq. names) */
int 
gaConnect::getLongVarIndex(const char *longvarname) {
  string varname_string(longvarname);
  for (int i = 0; i < variables.size(); i++) {
    if (variables[i].longname == varname_string) {
      return i;
    }
  }  
  return errval(GADAP_ERR_NAME_NOT_FOUND);
}

/* Returns the index of the given variable, or GADAP_ERR_NAME_NOT_FOUND */
int 
gaConnect::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);
}

/* Returns the name of the nth variable */
const char *
gaConnect::getVarName(int index) {
  if (index < 0 || index > variables.size() - 1) {
    return NULL;
  } else {
    return variables[index].name.c_str();
  }
}

/* Returns the type of the given variable, or an error if not found */
GADAP_VAR_TYPE gaConnect::getVarType(int index) {
  if (index < 0 || index > variables.size() - 1) {
    return GADAP_INVALID_TYPE;
  } else {
    return variables[index].type;
  }
}

/* Returns the type of the given variable, or an error if not found */
int gaConnect::getVarLen(int index) {
  if (index < 0 || index > variables.size() - 1) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  } else {
    return variables[index].length;
  }
}

/* Total number of variables in the dataset */
int 
gaConnect::getNumVars() {
  return variables.size();
}

/* Total number of level-dependent variables in the dataset */
int 
gaConnect::getNumLIVars() {
  return numIVars;
}

/*****************************************************************
 * Obtaining metadata 
 */

void gaConnect::buildAttrList() {
  // initialize 2D array of attributes, with one extra element for globals
  attributes = vector<vector<gadap_attrib > >(variables.size() + 1);

  AttrTable *table;
  AttrTable::Attr_iter p = d_das.var_begin();
  while (p != d_das.var_end()) {
    parseAttrTable(d_das.get_table(p), d_das.get_name(p), variables.size());
    p++;
  }
    
}

void gaConnect::parseAttrTable(AttrTable *table, string base_name, int varindex) {
  // variables.size() is index used for global variables in attributes array
  string new_base_name;
  if (varindex == variables.size()) {
    // no variable matched yet
    if (((varindex = getLongVarIndex(base_name.c_str())) >= 0) 
	|| ((varindex = getVarIndex(base_name.c_str())) >= 0)) {
      // got a match so delete base_name up to here
      new_base_name = "";
    } else {
      varindex = variables.size();
      if (base_name.find("_GLOBAL") == base_name.size() - string("_GLOBAL").size()) {
	base_name = "";
      } else {
	new_base_name = base_name + ".";
      }
    }
  }

  gadap_attrib next;
  string attr_name;
  string attr_value;
  AttrTable::Attr_iter p = table->attr_begin();
  while (p != table->attr_end()) {
    attr_name = new_base_name + table->get_name(p);
    if (table->is_container(p)) {
      parseAttrTable(table->get_attr_table(p), 
		     attr_name, 
		     varindex);
    } else {
      next.name = attr_name;
      vector<string> *values = table->get_attr_vector(p);
      next.value = removeQuotes((*values)[0]);
      for (int i = 1; i < (*values).size(); i++) {
	next.value += " " + removeQuotes((*values)[i]);
      }
      //      attributes[varindex].push_back(make_pair(attr_name, attr_value));
      attributes[varindex].push_back(next);
      
    }
    p++;
  }
}

int gaConnect::convertGlobalIndex(int varIndex) {
  if (varIndex < 0) {
    return variables.size();
  } else {
    return varIndex;
  }
}

/* Short (DAP) name of the dataset */
const char *
gaConnect::getName() {
  return d_dds.get_dataset_name().c_str();
}

const char *
gaConnect::getFullName() {
  // needs to be fixed
  return d_dds.get_dataset_name().c_str();
}

/* URL used to open this dataset */
const char *
gaConnect::getURL() {
  return url.c_str();
}

/* Number of metadata attributes associated with this dataset */
int 
gaConnect::getNumAttrs(int varindex) {
  varindex = convertGlobalIndex(varindex);
  if (varindex < 0 || varindex > variables.size()) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  }

  return attributes[varindex].size();
}

/* Returns string containing the nth attribute */
const char *
gaConnect::getAttrName(int varindex, int index) {
  varindex = convertGlobalIndex(varindex);
  if (varindex < 0 || varindex > variables.size()) {
    return NULL;
  }
  if (index < 0 || index > attributes[varindex].size() - 1) {
    return NULL;
  } else {
    return attributes[varindex][index].name.c_str();
  }
}

/* Returns the index of the attribute with the given name 
   or GADAP_NO_VALUE if not found */
int 
gaConnect::getAttrIndex(int varindex, const char *attrname) {
  varindex = convertGlobalIndex(varindex);
  if (varindex < 0 || varindex > variables.size()) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  }
  string attrname_str(attrname);
  for (int i = 0; i < attributes[varindex].size(); i++) {
    if (attributes[varindex][i].name == attrname_str) {
      return i;
    }
  }
  return errval(GADAP_ERR_NAME_NOT_FOUND);
}

/* Returns string containing the value of the named attribute */
const char *
gaConnect::getAttr(int varindex, int index) {
  varindex = convertGlobalIndex(varindex);
  if (varindex < 0 || varindex > variables.size()) {
    return NULL;
  }
  if (index < 0 || index > attributes[varindex].size() - 1) {
    return NULL;
  } else {
    return attributes[varindex][index].value.c_str();
  }
}

/* Returns GADAP_SUCCESS if the attribute has a numeric value, and places
   that value in *value. */
GADAP_STATUS 
gaConnect::getAttrDbl(int varindex, int index, double *value) {
  varindex = convertGlobalIndex(varindex);
  if (varindex < 0 || varindex > variables.size()) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  }
  if (index < 0 || index > attributes[varindex].size() - 1) {
    return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
  } 

  const char *valueString = attributes[varindex][index].value.c_str();

  char * unconverted;
  double converted = strtod(valueString, &unconverted);
  if (unconverted == valueString) {
    return errval(GADAP_ERR_BAD_NUMERIC_FORMAT);
  } else {
    *value = converted;
    return GADAP_SUCCESS;
  }
}


/*****************************************************************
 * Retrieving data 
 */
const char *
gaConnect::getURL(GADAP_STN_QUERY *query) {
 if (query == NULL) {
    return NULL;
  }
  
  // start with base request url
  string request_url = url + ".dods?";
  int base_url_len = request_url.length();

  // build constraint
  ostrstream constraint;
  bool firstval = true;

  //DBG cout << "building projection" << endl;

  // projection expression
  if (query->varflags != NULL) {
    for (int i = 0; i < variables.size(); i++) {
      if (query->varflags[i]) {
	if (firstval) {
	  firstval = false;
	} else {
	  constraint << ",";
	} 
	constraint << variables[i].name; 
	//	constraint << variables[i].longname; 
      }
    }
    if (firstval) {
      return NULL;
    }
  }
  
  //DBG cout << "building selection" << endl;
  
  // selection expression 

  if (query->usebounds) {
    //DBG cout << "adding bounds" << endl;
    constraint << "&bounds(" 
	       << query->minlon << "," << query->maxlon << ","
	       << query->minlat << "," << query->maxlat << ","
	       << query->minlev << "," << query->maxlev << ","
	       << query->mintime << "," << query->maxtime << ")";
  }

  if (query->stid) {
    constraint << "&stid(\"" << query->stid << "\")";
  }

  if (query->extra) {
    constraint << query->extra;
  }   

  // end of string
  constraint << ends;

  // copy string and return memory ownership to ostrstream object
  request_url +=  constraint.str();
  constraint.freeze(false);

  // create a regular c string and copy the C++ string contents into it
  if (query->url) {
    free(query->url);
  }
  query->url = (char *) malloc(sizeof(char) * (request_url.length() + 1));
  strcpy(query->url, request_url.c_str());

  // set constraint to appropriate index in URL string
  query->constraint = query->url + base_url_len;

  return query->url;

}

gaReports *
gaConnect::getReports(GADAP_STN_QUERY *query) {

  BaseTypeFactory factory;

  if (query == NULL) {
    return NULL;
  }

  // build request URL
  getURL(query);
 
  // send request
  // Where is request_dds deleted? jhrg
  DataDDS *request_dds = new DataDDS(&factory);
  //Changed to new call. jhrg
  request_data(*request_dds, string(query->constraint));

  // parse response
  return new gaReports(request_dds, query->url);

}
 
