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

#include "config.h"
#include "gadap.h"
#include "gaConnect.h"
#include "gaReports.h"

// #include "Connections.h"
#include "Connections.cc"

#include <vector>

/** Static data */

// List of open datasets
Connections<gaConnect*> datasets(GADAP_MAX_DATASETS);
int numDatasets = 0;

// List of open report collections (query results)
std::vector<gaReports*> reports;

/** Internal utility functions */

int
bad_d_handle(GADAP_DATASET d_handle) {
  return d_handle < 0 
    || d_handle > GADAP_MAX_DATASETS 
    || datasets[d_handle] == NULL;
}

int
bad_r_handle(GADAP_RPTCOL r_handle) {
  return r_handle < 0 
    || r_handle > reports.size() 
    || reports[r_handle] == NULL;
}


/*****************************************************************
 * Return library name and version number (taken from libdap)
 */
extern "C"
const char *
libgadap_version() {
  return PACKAGE_STRING;
}

/*****************************************************************
 * Opening and closing datasets 
 */

int
gadap_open(const char *url, GADAP_DATASET *d_handle) {
  try {
    gaConnect *newDataset = new gaConnect(url);
    *d_handle = datasets.add_connect(newDataset);
    numDatasets++;
    return GADAP_SUCCESS;
  } catch (const Error& error) {
    cerr << "DAP error: " << error.get_error_message() << endl;
    *d_handle = -1;
    return errval(GADAP_ERR_DAP_ERROR);
  }
}


int
gadap_numopen() {
  return numDatasets;
}


int 
gadap_handle(GADAP_DATASET *d_handle, int d_index) {
  if (d_index >= 0 && d_index < numDatasets) {
    int counter = -1;
    for (int i = 0; i < GADAP_MAX_DATASETS; i++) {
      if (datasets[i] != NULL) {
	counter++;
      }
      if (counter == d_index) {
	*d_handle = i;
	return GADAP_SUCCESS;
      }
    }
  }
  return errval(GADAP_ERR_INDEX_OUT_OF_RANGE);
}


void
gadap_close(GADAP_DATASET d_handle, int free_rptcols) {
  if (bad_d_handle(d_handle)) {
    return;
  } 

  for (int i = 0; i < reports.size() && free_rptcols; i++) {
    if (reports[i] && reports[i]->getDataset() == d_handle) {
      gadap_r_free(i);
    }
  }

  delete datasets[d_handle];
  datasets.del_connect(d_handle);
  numDatasets--;
}



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


int 
gadap_d_varindex(GADAP_DATASET d_handle, const char *varname) {
  if (bad_d_handle(d_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return datasets[d_handle]->getVarIndex(varname);
  }
}


const char *
gadap_d_varname(GADAP_DATASET d_handle, int var_index) {
  if (bad_d_handle(d_handle)) {
    return NULL;
  } else {
    return datasets[d_handle]->getVarName(var_index);
  }
}


int
gadap_d_varlen(GADAP_DATASET d_handle, int var_index) {
  if (bad_d_handle(d_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return datasets[d_handle]->getVarLen(var_index);
  }
}

GADAP_VAR_TYPE
gadap_d_vartype(GADAP_DATASET d_handle, int var_index) {
  if (bad_d_handle(d_handle)) {
    return GADAP_INVALID_TYPE;
  } else {
    return datasets[d_handle]->getVarType(var_index);
  }
}

int 
gadap_d_numvars(GADAP_DATASET d_handle) {
  if (bad_d_handle(d_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return datasets[d_handle]->getNumVars();
  }
}


int 
gadap_d_numlivars(GADAP_DATASET d_handle) {
  if (bad_d_handle(d_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return datasets[d_handle]->getNumLIVars();
  }
}

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


const char *
gadap_d_name(GADAP_DATASET d_handle) {
  if (bad_d_handle(d_handle)) {
    return NULL;
  } else {
    return datasets[d_handle]->getName();
  }
}


const char *
gadap_d_fullname(GADAP_DATASET d_handle) {
  if (bad_d_handle(d_handle)) {
    return NULL;
  } else {
    return datasets[d_handle]->getFullName();
  }
}


const char *
gadap_d_url(GADAP_DATASET d_handle) {
  if (bad_d_handle(d_handle)) {
    return NULL;
  } else {
    return datasets[d_handle]->getURL();
  }
}


int 
gadap_d_numattrs(GADAP_DATASET d_handle, int var_index) {
  if (bad_d_handle(d_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return datasets[d_handle]->getNumAttrs(var_index);
  }
}


const char *
gadap_d_attrname(GADAP_DATASET d_handle, int var_index, int attr_index) {
  if (bad_d_handle(d_handle)) {
    return NULL;
  } else {
    return datasets[d_handle]->getAttrName(var_index, attr_index);
  }
}


int 
gadap_d_attrindex(GADAP_DATASET d_handle, int var_index, const char *attrname) {
  if (bad_d_handle(d_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return datasets[d_handle]->getAttrIndex(var_index, attrname);
  }
}


const char *
gadap_d_attrstr(GADAP_DATASET d_handle, int var_index, int attr_index) {
  if (bad_d_handle(d_handle)) {
    return NULL;
  } else {
    return datasets[d_handle]->getAttr(var_index, attr_index);
  }
}


int
gadap_d_attrdbl(GADAP_DATASET d_handle, int var_index, int attr_index, 
		 double *value) {
  if (bad_d_handle(d_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return datasets[d_handle]->getAttrDbl(var_index, attr_index, value);
  }
}

/*****************************************************************
 * Metadata shorthand functions
 */

const char *
gadap_d_title(GADAP_DATASET d_handle) {
  return gadap_d_attrstr(d_handle, -1, gadap_d_attrindex(d_handle, -1, "title"));
} 

int
gadap_d_fill(GADAP_DATASET d_handle, int var_index, double *value) {
  if (gadap_d_attrdbl(d_handle, var_index,
		       gadap_d_attrindex(d_handle, var_index, "missing_value"), 
		       value) == GADAP_SUCCESS) {
    return GADAP_SUCCESS;
  } else {
	return gadap_d_attrdbl(d_handle, var_index, 
				 gadap_d_attrindex(d_handle, var_index, 
						    "_FillValue"), value);
  }
} 

const char *
gadap_d_longname(GADAP_DATASET d_handle, int var_index) {
  return gadap_d_attrstr(d_handle, var_index, 
		       gadap_d_attrindex(d_handle, var_index, "long_name"));
} 


/*****************************************************************
 * Sending a query 
 */


GADAP_STN_QUERY *
gadap_sq_new(GADAP_DATASET d_handle) {
  if (bad_d_handle(d_handle)) {
    return NULL;
  } 
  // Create and clear query
  GADAP_STN_QUERY *new_query = new GADAP_STN_QUERY();
  memset(new_query, 0, sizeof(GADAP_STN_QUERY));

  // Set dataset handle
  new_query->d_handle = d_handle;

  // Create var array
  int numVars = datasets[d_handle]->getNumVars();
  new_query->varflags = (int *)calloc(numVars, sizeof(int));

  return new_query;
}
  

const char *  
gadap_sq_url(GADAP_STN_QUERY *query) {
    GADAP_DATASET d_handle = query->d_handle;
    if (bad_d_handle(d_handle)) {
      return NULL;
    }
    return datasets[d_handle]->getURL(query);
}  

GADAP_STATUS
gadap_sq_send(GADAP_STN_QUERY *query,
	       GADAP_RPTCOL *r_handle) {
  try {
    GADAP_DATASET d_handle = query->d_handle;
    if (bad_d_handle(d_handle)) {
      return errval(GADAP_ERR_INVALID_HANDLE);
    }

    //DBG cout << "sending query..." << endl;

    // Get reports back for query
    gaReports *newReports = datasets[d_handle]->getReports(query);

    if (newReports == NULL) {
      return errval(GADAP_ERR_INVALID_QUERY);
    }
    newReports->setDataset(d_handle);
    newReports->setQuery(new GADAP_STN_QUERY(*query));
    
    // Find next empty slot in reports vector
    int nextFree = 0;
    while (nextFree < reports.size() && reports[nextFree] != NULL) {
      nextFree++;
    }
    if (nextFree == reports.size()) {
      reports.push_back(newReports);
    } else {
      reports[nextFree] = newReports;
    }
    // Return handle
    *r_handle = nextFree;
    return GADAP_SUCCESS;
    
  } catch (const Error& error) {
    cout << "DAP error: " << error.get_error_message() << endl;
    *r_handle = -1;
    return errval(GADAP_ERR_DAP_ERROR);
  }


}


void
gadap_sq_free(GADAP_STN_QUERY *query) {
  if (query->url) {
    free(query->url);
  }
  if (query->varflags) {
    free(query->varflags);
  }
  delete query;
}


/*****************************************************************
 * Managing report collections 
 */


GADAP_STATUS
gadap_r_dataset(GADAP_RPTCOL r_handle, GADAP_DATASET *d_handle) {
  if (bad_r_handle(r_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    *d_handle = reports[r_handle]->getDataset();
    return GADAP_SUCCESS;
  }
}  


GADAP_STN_QUERY *
gadap_r_query(GADAP_RPTCOL r_handle) {
  if (bad_r_handle(r_handle)) {
    return NULL;
  } else {
    return reports[r_handle]->getQuery();
  }
}

const char *
gadap_r_url(GADAP_RPTCOL r_handle) {
  if (bad_r_handle(r_handle)) {
    return NULL;
  } else {
    return reports[r_handle]->getURL();
  }
}

int 
gadap_r_numvars(GADAP_RPTCOL r_handle) {
  if (bad_r_handle(r_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return reports[r_handle]->getNumVars();
  }
}  


int 
gadap_r_numlivars(GADAP_RPTCOL r_handle) {
  if (bad_r_handle(r_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return reports[r_handle]->getNumLIVars();
  }
}


const char *
gadap_r_varname(GADAP_RPTCOL r_handle, int var_index) {
  if (bad_r_handle(r_handle)) {
    return NULL;
  } else {
    return reports[r_handle]->getVarName(var_index);
  }
}


int
gadap_r_varindex(GADAP_RPTCOL r_handle, const char *varname) {
  if (bad_r_handle(r_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return reports[r_handle]->getVarIndex(varname);
  }
}

int 
gadap_r_numrpts(GADAP_RPTCOL r_handle) {
  if (bad_r_handle(r_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return reports[r_handle]->getNumReports();
  }
}


int 
gadap_r_numlev(GADAP_RPTCOL r_handle, int rpt_index) {
  if (bad_r_handle(r_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return reports[r_handle]->getNumLevels(rpt_index);
  }
}


void
gadap_r_free(GADAP_RPTCOL r_handle) {
  if (bad_r_handle(r_handle)) {
    return;
  } 
  delete reports[r_handle];
  reports[r_handle] = NULL;
}

/*****************************************************************
 * Retrieving data from report collections 
 */


GADAP_STATUS
gadap_r_valdbl(GADAP_RPTCOL r_handle, 
		int rpt_index, int lev_index, 
		int var_index, int array_index,
		double *value) {
  if (bad_r_handle(r_handle)) {
    return errval(GADAP_ERR_INVALID_HANDLE);
  } else {
    return reports[r_handle]->getVarDbl(rpt_index, lev_index, 
					var_index, array_index, 
					value);
  }
}  

const char *
gadap_r_valstr(GADAP_RPTCOL r_handle, 
		int rpt_index, int lev_index, 
		int var_index, int array_index) {
  if (bad_r_handle(r_handle)) {
    return NULL;
  } else {
    return reports[r_handle]->getVarStr(rpt_index, lev_index, 
					var_index, array_index);
  }
}  

