
/*------------------------------------------------------------------------------*
 * File Name:	NetCDFManager.h			 										*
 * Creation: 																	*
 * Purpose: OriginC Header file													*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER				*
 *	Sophy 9/11/2009 QA80-13128 NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
 *	Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE	*
 *	Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE			*
 *------------------------------------------------------------------------------*/
#ifndef	_NETCDFMANAGER_H_
#define	_NETCDFMANAGER_H_

#include "netcdf.h"

#define	NC_BAD -1
#define	STR_VARNAME_ATTRIB			"VarName"
#define	STR_VAR_ID_ATTRIB			"VarID"
#define	STR_LONGNAME_ATTRIB			"long_name"
#define	STR_FILLINGVALUE_ATTRIB		"filling_value"
#define	STR_MISSINGVALUE_ATTRIB		"missing_value"
#define	STR_MISSINGDATA_ATTRIB_A	"missing_data"
#define	STR_MISSINGDATA_ATTRIB_B	"missing-data"
#define	STR_VAR_TYPE_ATTRIB			"TypeValue"

#define	STR_UNITS_ATTRIB			"units"
#define	STR_VARTREE_NAME			"NCVars"
#define	STR_DIMTREE_NAME			"NCDims"
#define	STR_ATTTREE_NAME			"NCAtts"

#define	STR_VAR_DIM_IDS				"DimIDs"
#define	STR_VAR_DIM_LENS			"DimLens"
#define	STR_VAR_DIM_NUM				"DimNum"
#define	STR_VAR_UNITS				"Units"
#define	STR_VAR_DESCRIPT			"Descript"
#define	STR_VAR_COMMENTS			"Comments"
#define	STR_VAR_MISSVALUE			"MissValue"
#define	STR_VAR_FILLVALUE			"FillValue"

///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
#define	STR_DIM_NAMES				"DimNames"
#define	STR_DIMENSIONALITY			"Dimensionality"
#define	STR_DIM_NAMES_INFO			"Dimension Names"
#define	STR_DIM_SIZE				"Dimension Size"				
#define	STR_OC_TYPE					"OCType"
#define	STR_DATA_TYPE				"Data Type"
#define	STR_DIMS_SEP				", "

#define	STR_VAR_INFO				"Variable Information"
///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER

enum{
	NC_FAIL = NC_BAD, //-1
	NC_SUCCESS = 0,
	NC_OPENED = 1
};

enum {
	NC_CONVERT_DOUBLE = 0x00000001,	// convert filling and missing value of netcdf file into Origin missing value
};


typedef nc_type NCTYPE;
//---------------- NetCDF Declaration ----------------//
class NetCDF
{
public:
	NetCDF(){ m_ncID = NC_BAD;}
	~NetCDF(){ if (m_ncID != NC_BAD) Close(); }
	int		Open(LPCSTR lpcszFile, TreeNode* ptrNCHeader = NULL);//OpenNetcdf
	int		Close();
	int		GetVarsInfo(TreeNode* ptrVars); //netcdf_get_vars_info
	int		GetOneVarInfo(LPCSTR lpcszVar, int* pDim, int* pnDataType, string* pstrUnit = NULL, string* pstrLongName = NULL, string* pstrComments = NULL, bool* pbHasMissing = NULL, double* pdMissing = NULL);//netcdf_get_var_info
	
	//from netcdf_utils.h
	bool	ImportXDMat(LPCSTR lpcszVar, MatrixLayer& ml, int nDim = 0, int nMoStart = 0, int nMoEnd = -1); //netcdf_xd_mat
	///Sophy 9/11/2009 QA80-13128 NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
	//since we change the import structure in result page, so better provide new interface instead of modifying existing one
	bool	ImportXDMatEx(LPCSTR lpcszVar, MatrixLayer& ml); //this method import a variable into only one layer, even though it has dimensionality larger than 3
	///end NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
	bool	ImportOneDCol(LPCSTR lpcszVar, Worksheet& wks, int nCol); //netcdf_1d_col
	//int		ImportNetCDF(Page& pgTarget, LPCSTR lpcszFile, TreeNode& trFilter); //import_netcdf

private:
	bool	addOneDimInfo(int nDimID, TreeNode& trParent);
	bool	addOneVarInfo(int nVarID, TreeNode& trParent); //netcdf_get_var & netcdf_var_info
	bool	addOneAttribInfo(int nVarID, int nAttribID, TreeNode& trParent, string* pstrDescript); //netcdf_get_attribute
	
	//from netcdf.cpp
	bool	constructVarTree();//netcdf_get_var_list netcdf.cpp 
	bool	constructDimTree();
	bool	constructAttTree(); //attributes tree
	int		getAttribString(int nVarID, char* attName, string& strVal); //netcdf_get_att_str
	int		getAttribDouble(int nVarID, char* attName, double* attVal); //netcdf_get_att_double
	int		getAttribValues(string& strVal, NCTYPE ncType, const vector& vVals);//netcdf_get_att_vals_as_string
	int		getFillValue(int nVarID, double* dFillVal); //netcdf_get_fillvalue
	//int		getVarFillValue(int nVarID, char* varName, double* dVal); //netcdf_get_var_fillvalue
	int		getMissValue(int nVarID, double* dMissVal); //netcdf_get_missing_value
	
	//from netcdf_utils.h
	int		importTwoDMat(LPCSTR lpcszVar, MatrixLayer& ml, int nIndex, vector<int>& vnRef, int nType); //netcdf_2d_mat
	//int		importOneDToWorksheet(Worksheet& wks, TreeNode& trVars); //import_netcdf_1d_into_wkssheet
	//int		importToMatrixsheet(MatrixPage& mp, string& strVar); //import_netcdf_into_matrix_sheet
	//bool	importGetVarDlg(TreeNode& trFilter, bool bGetOneD = false); //netcdf_import_GetVarDialog
	
	//from netcdf.cpp
	int		getVector(LPCSTR lpcszVar, Column& col, DWORD dwCntrl = NC_CONVERT_DOUBLE);//netcdf_get_vector
	int		getAndPutVarData(TreeNode& trVar, Column& col, DWORD dwCntrl = NC_CONVERT_DOUBLE);//netcdf_get_and_put_var_data
	int		getAndPutVarData(TreeNode& trVar, Column& col, DWORD dwCntrl, const size_t *pStart, const size_t *pCount, int nSize);
	int		putVarData(TreeNode& trVar, Column& col, DWORD dwCntrl, vectorbase& vData); //netcdf_put_data
	int		updateStart(const vector<int>& vnDims, int nNumDims, size_t *pStart, const size_t *pStride);//update_start
	TreeNode	getVarNode(LPCSTR lpcszVar);
	int		getVarData(LPVOID lpData, LPCSTR lpcszVar, int* pDim, int nObjDims, int nFSIType);//netcdf_get_var_data
	int		setStartAndCount(TreeNode& trVar, size_t *pStart, size_t *pCount, int *pDim, int nReadDim);//netcdf_set_start_and_count
	
	///Sophy 9/11/2009 QA80-13128 NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
	bool	getXDMatrixes(LPCSTR lpcszVar, MatrixLayer& ml, const vector<int>& vnDims, vector<int>& vnRef, int nDepth);
	bool	getOneMatrix(LPCSTR lpcszVar, MatrixLayer& ml, int iMatObj, const vector<int>& vnRef);
	int		setStartAndCount(vector<size_t>& vStart, vector<size_t>& vCount, const vector<int>& vnDims, const vector<int>& vnRef);
	///end NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
	
	//AUX util
	bool	isNumericType(NCTYPE ncType); //netcdf_is_numeric_type
	bool	checkValidFile(LPCSTR lpcszFile); //check if a valid netcdf file
	LPCSTR	getTypeName(NCTYPE ncType);//netcdf_get_type_name
	int		getOCType(NCTYPE ncType); //netcdf_get_oc_type
	void	removeIdleNodes(TreeNode& trVar);
	
	//user info
	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	bool	updateUserTree(OriginObject& obj, LPCSTR lpcszVarName, LPCSTR lpcszLabel);
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	///Sophy 11/9/2009 QA80-13130-P6 UPDATE_COLUMN_FORMAT_ACCORDING_TO_SOURCE_FILE_DATATYPE
	bool	updateColumnFormat(Column& colObj, int nOCType);
	///end UPDATE_COLUMN_FORMAT_ACCORDING_TO_SOURCE_FILE_DATATYPE
	
private:
	int		m_ncID;
	string	m_strFileName;
	
	int		m_nNumVars; //number of vars
	int		m_nNumAttribs; //number of global attributes
	int		m_nNumDims; //number of dims
	
	Tree	m_trNetCDFHeader; //keep info of the netcdf file
};

//---------------- Implementation NetCDF ----------------//

int		NetCDF::Open(LPCSTR lpcszFile, TreeNode* ptrNCHeader) //NULL
{
	if ( !checkValidFile(lpcszFile) )
		return NC_FAIL; //not a valid netcdf file
	
	if ( NC_BAD != m_ncID )
	{
		printf("Close opened netcdf file before open it again!");
		return NC_OPENED;
	}
	
	m_ncID = ncopen(lpcszFile, NC_NOWRITE);
	if ( NC_BAD == m_ncID )
		return NC_BAD;
	
	m_strFileName.Format("%s", lpcszFile);
	
	constructVarTree();
	
	constructDimTree();
	
	constructAttTree();
	
	if ( m_nNumVars < 1 ) //no vars
		return NC_FAIL;
	
	ASSERT(m_trNetCDFHeader.NCAtts.Children.Count() == m_nNumAttribs);
	ASSERT(m_trNetCDFHeader.NCDims.Children.Count() == m_nNumDims);
	
	return NC_SUCCESS;
}

int		NetCDF::Close()
{
	if ( NC_BAD == m_ncID )
		return NC_BAD;
	
	int nID = m_ncID;
	m_ncID = NC_BAD;
	
	return ncclose(nID);
}

bool	NetCDF::checkValidFile(LPCSTR lpcszFile)
{
	file	ff;
	bool	bValid = false;
	
	if ( ff.Open(lpcszFile, file::modeRead | file::shareDenyWrite) )
	{
		char cBuffer[4];
		ff.SeekToBegin();
		ff.Read(cBuffer, 3);
		cBuffer[3] = '\0';
		
		bValid = strcmp((const char*)cBuffer, "CDF") == 0 || strcmp((const char*)cBuffer, "FDC") == 0;
		ff.Close();
	}
	return bValid;
}

int		NetCDF::GetVarsInfo(TreeNode* ptrVars)
{
	if ( !ptrVars || !ptrVars->IsValid() )
		return 0;
	
	TreeNode tr1D, tr2D, tr3D, tr4D;
	int		nRealDims;
	vector<int> vnDimLens;
	
	TreeNode	trVars = m_trNetCDFHeader.GetNode(STR_VARTREE_NAME);
	if ( !trVars )
		return 0;

	int nNumVars = 0;
	foreach ( TreeNode trOneVar in trVars.Children )
	{	
		if ( !trOneVar )
			continue;
		
		TreeNode trDimLens = trOneVar.GetNode(STR_VAR_DIM_LENS);
		vnDimLens = trDimLens.nVals;
		if ( vnDimLens.GetSize() > 0 && 0 == vnDimLens[0] )
			continue;
		
		TreeNode trDimNum = trOneVar.GetNode(STR_VAR_DIM_NUM);
		nRealDims = trDimNum.nVal;
		
		if ( vnDimLens.GetSize() > 0 && 1 == vnDimLens[0] )
			nRealDims--;
		
		TreeNode trVar = trOneVar.Clone(true);
		
		removeIdleNodes(trVar);
		
		switch(nRealDims)
		{
		case 0:
		case 1:
			if ( !tr1D.IsValid() )
				tr1D = ptrVars->AddNode(IMPTREE_NODE_1D, 0);
			
			tr1D.AddNode(trVar);
			break;
			
		case 2:
			if ( !tr2D.IsValid() )
				tr2D = ptrVars->AddNode(IMPTREE_NODE_2D, 0);
			
			tr2D.AddNode(trVar);
			break;
			
		case 3:
			if ( !tr3D.IsValid() )
				tr3D = ptrVars->AddNode(IMPTREE_NODE_3D, 0);
			
			tr3D.AddNode(trVar);
			break;
			
		case 4:
			if ( !tr4D.IsValid() )
				tr4D = ptrVars->AddNode(IMPTREE_NODE_4D, 0);
			
			tr4D.AddNode(trVar);
			break;
			
		default:
			break; //more than 4D, not support yet
		}
		
		nNumVars++;
	}
	
	return nNumVars;
}

#define CONVERT_DOUBLE_TO_NUMERIC(_TYPE_, _DOUBLE_, _PBUFFER_)  *( (_TYPE_* )_PBUFFER_) = (_TYPE_) _DOUBLE_;

int		NetCDF::GetOneVarInfo(LPCSTR lpcszVar, int* pDim, int* pnDataType, string* pstrUnit, string* pstrLongName, string* pstrComments, bool* pbHasMissing, double* pdMissing)//NULL, NULL, NULL, NULL, NULL
{
	TreeNode trVars = m_trNetCDFHeader.GetNode(STR_VARTREE_NAME);
	if ( !trVars )
		return 0;
	
	TreeNode trOneVar = trVars.GetNode(lpcszVar);
	if ( !trOneVar )
		return 0;
	
	if ( pnDataType )
	{
		int ncType = -1;
		if ( trOneVar.GetNode(STR_OC_TYPE) )
			trOneVar.GetNode(STR_OC_TYPE).GetAttribute(STR_VAR_TYPE_ATTRIB, ncType);
		
		*pnDataType = getOCType(ncType);
	}
	
	TreeNode trDimNum = trOneVar.GetNode(STR_VAR_DIM_NUM);
	int nDims = trDimNum.nVal;
	
	TreeNode trDimLens = trOneVar.GetNode(STR_VAR_DIM_LENS);
	vector<int> vnDimLens;
	vnDimLens = trDimLens.nVals;
	
	if ( pDim )
	{
		ASSERT(vnDimLens.GetSize() == nDims);
		for ( int ii = 0; ii < nDims; ii++ )
			*(pDim + ii ) = vnDimLens[ii];
	}
	
	if ( pstrUnit )
	{
		TreeNode trUnits = trOneVar.GetNode(STR_VAR_UNITS);
		if ( trUnits )
			*pstrUnit = trUnits.strVal;
	}
	
	if ( pstrLongName )
	{
		TreeNode trDescript = trOneVar.GetNode(STR_VAR_DESCRIPT);
		if ( trDescript )
			*pstrLongName = trDescript.strVal;
	}
	
	if ( pstrComments )
	{
		TreeNode trComments = trOneVar.GetNode(STR_VAR_COMMENTS);
		if ( trComments )
			*pstrComments = trComments.strVal;
	}
	
	if ( pbHasMissing )
	{
		TreeNode trMissVal = trOneVar.GetNode(STR_VAR_MISSVALUE);
		double dMissVal;
		if ( trMissVal )
		{
			*pbHasMissing = trMissVal.nVal;
			trMissVal.GetAttribute(STR_MISSINGVALUE_ATTRIB, dMissVal);
		}
		if ( (*pbHasMissing) )
		{
			ASSERT(pdMissing != NULL);
			if ( pdMissing == NULL )
				return -1; //NC_FAIL, error;
			
			switch( *pnDataType )
			{
			case FSI_REAL:
				CONVERT_DOUBLE_TO_NUMERIC(float, dMissVal, pdMissing)
				break;
				
			case FSI_DOUBLE:
				memcpy(pdMissing, &dMissVal, sizeof(double));
				break;
				
			case FSI_LONG:
				CONVERT_DOUBLE_TO_NUMERIC(long, dMissVal, pdMissing)
				break;
				
			case FSI_SHORT:
				CONVERT_DOUBLE_TO_NUMERIC(short, dMissVal, pdMissing)
				break;
				
			default:
				return -1; //error
			}
		}
	}
	
	return nDims;
}

#define CHECK_MATOBJ_WITH_VARDIM(_vnDim, _i, _n1, _n2)	\
	if( _n1 < 0 ) _n1 = 0;								\
	if( _n1 >= _vnDim[_i] ) _n1 = _vnDim[_i] - 1;		\
	if( _n2 < 0 || _n2 > _vnDim[_i] ) _n2 = _vnDim[_i];
	
bool	NetCDF::ImportXDMat(LPCSTR lpcszVar, MatrixLayer& ml, int nDim, int nMoStart, int nMoEnd) //0, 0, -1
{
	if ( !lpcszVar || *lpcszVar == '\0' || !ml )
		return false;
	
	int			nType;
	vector<int>	vnDim(NC_MAX_VAR_DIMS);
	vnDim = 0; //reset all dims
	
	int nNumDims = GetOneVarInfo(lpcszVar, vnDim, &nType);
	if ( nNumDims <= 0 )
		return false;
	
	vector<int>	vnRef(NC_MAX_VAR_DIMS);
	vnRef = 0; //reset
	
	if ( !ml.SetInternalDataType(nType) )
		return false;
	
	switch(nNumDims)
	{
	case 2:
		if ( !ml.SetSize(1, vnDim[nNumDims - 2], vnDim[nNumDims - 1], 0) )
			return false;
		importTwoDMat(lpcszVar, ml, nMoStart, vnRef, nType);
		break;
		
	case 3:
		CHECK_MATOBJ_WITH_VARDIM(vnDim, 0, nMoStart, nMoEnd)
		
		if ( !ml.SetSize(nMoEnd - nMoStart, vnDim[nNumDims - 2], vnDim[nNumDims - 1], 0) )
			return false;
		
		for ( int ii = nMoStart; ii < nMoEnd; ii++ )
		{
			vnRef[0] = ii + 1;
			importTwoDMat(lpcszVar, ml, ii, vnRef, nType);
		}
		break;
		
	case 4:
		ASSERT(nDim >= 0 && nDim < vnDim[0]);
		CHECK_MATOBJ_WITH_VARDIM(vnDim, 1, nMoStart, nMoEnd)
		if ( !ml.SetSize(nMoEnd - nMoStart, vnDim[nNumDims - 2], vnDim[nNumDims - 1], 0) )
			return false;
		
		vnRef[0] = nDim + 1;
		for ( int ii = nMoStart; ii < nMoEnd; ii++ )
		{
			vnRef[1] = ii + 1;
			importTwoDMat(lpcszVar, ml, ii, vnRef, nType);
		}
		break;
		
	default:
		printf("Error : %d dimensions not supported.\n", nNumDims);
		return false;
	}
	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	updateUserTree(ml, lpcszVar, STR_VAR_INFO);
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	return true;
}


///Sophy 9/11/2009 QA80-13128 NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
//since we change the import structure in result page, so better provide new interface instead of modifying existing one
bool	NetCDF::ImportXDMatEx(LPCSTR lpcszVar, MatrixLayer& ml) //this method import a variable into only one layer, even though it has dimensionality larger than 3
{
	if ( !lpcszVar || *lpcszVar == '\0' || !ml )
		return false;
	
	int			nType;
	vector<int>	vnDims(NC_MAX_VAR_DIMS);
	vnDims = 0; //reset all dims
	
	int nNumDims = GetOneVarInfo(lpcszVar, vnDims, &nType);
	if ( nNumDims <= 0 )
		return false;
	
	vnDims.SetSize(nNumDims);
	vector<int>	vnRef(nNumDims - 2);
	
	if ( !ml.SetInternalDataType(nType) )
		return false;
	
	int nMatObjs = 1; //calculate the number of matrix objects needed.
	for ( int ii = 0; ii < vnRef.GetSize(); ii++ )
		nMatObjs *= vnDims[ii];
	
	ml.SetSize(nMatObjs, vnDims[nNumDims - 2], vnDims[nNumDims - 1]);

	bool bRet = getXDMatrixes(lpcszVar, ml, vnDims, vnRef, 0);

	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	updateUserTree(ml, lpcszVar, STR_VAR_INFO);
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	return bRet;
}
///end NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
	
int		NetCDF::importTwoDMat(LPCSTR lpcszVar, MatrixLayer& ml, int nIndex, vector<int>& vnRef, int nType)
{
	if ( !lpcszVar || *lpcszVar == '\0' || !ml )
		return NC_FAIL; //error
	
	int	nNCStatus;
	//centralize netcdf_get_var_data
	switch( nType )
	{
	case FSI_REAL: //NC_FLOAT
		Matrix<float>	mm(ml, nIndex);
		nNCStatus = getVarData((float*)mm, lpcszVar, vnRef, 2, nType);
		break;
		
	case FSI_DOUBLE: //NC_DOUBLE
		Matrix<double>	mm(ml, nIndex);
		nNCStatus = getVarData((double*)mm, lpcszVar, vnRef, 2, nType);
		break;
		
	case FSI_LONG: //NC_INT
		Matrix<int>		mm(ml, nIndex);
		nNCStatus = getVarData((int*)mm, lpcszVar, vnRef, 2, nType);
		break;
		
	case FSI_SHORT: //NC_SHORT
		Matrix<short>	mm(ml, nIndex);
		nNCStatus = getVarData((short*)mm, lpcszVar, vnRef, 2, nType);
		break;
		
	case FSI_CHAR: //NC_CHAR
		Matrix<byte>	mm(ml, nIndex);
		nNCStatus = getVarData((byte*)mm, lpcszVar, vnRef, 2, nType);
		break;
		
	default:
		ASSERT(false);
		nNCStatus = NC_FAIL;
	}
	
	return nNCStatus;
}

bool		NetCDF::ImportOneDCol(LPCSTR lpcszVar, Worksheet& wks, int nCol) //netcdf_1d_col
{
	if ( !lpcszVar || *lpcszVar == '\0' || !wks )
		return false;
	
	string	strUnits, strLongName, strComments;
	int		nType;
	
	vector<int>	vnDim(NC_MAX_VAR_DIMS);
	vnDim = 0; //reset
	
	int		nNumDims = GetOneVarInfo(lpcszVar, vnDim, &nType, &strUnits, &strLongName, &strComments);
	bool	bIs1DVar = (nNumDims < 2 || (2 == nNumDims && 1 == vnDim[0]));
	if ( !bIs1DVar )
		return false; //not supported.
	
	if ( nCol >= wks.GetNumCols() )
		wks.SetSize(-1, nCol + 1);
	
	//prepare range string
	Column	col(wks, nCol);
	///Sophy 11/9/2009 QA80-13130-P6 UPDATE_COLUMN_FORMAT_ACCORDING_TO_SOURCE_FILE_DATATYPE
	updateColumnFormat(col, nType);
	///end UPDATE_COLUMN_FORMAT_ACCORDING_TO_SOURCE_FILE_DATATYPE
	string	strRange;
	int		nNCStatus;
	nNCStatus = getVector(lpcszVar, col);
	
	Tree	trVarsInfo;
	GetVarsInfo(&trVarsInfo);
	
	TreeNode	tr1DVars = trVarsInfo.GetNode(IMPTREE_NODE_1D, false);
	TreeNode	trVar = tr1DVars.GetNode(lpcszVar, false);
	
	if ( trVar )
	{
		strComments = strLongName;
		strLongName = trVar.tagName;
		
		///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
		//set_user_info(col, (string)"ColumnInfo", trVar);
		///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
		
		string	strDimName;
		strDimName = trVar.GetNode(STR_DIM_NAMES).strVal;
		strDimName.TrimLeft('(');
		strDimName.TrimRight(')');
		
		if ( strDimName == trVar.tagName ) //x column
			col.SetType(OKDATAOBJ_DESIGNATION_X);
		else
			col.SetType(OKDATAOBJ_DESIGNATION_Y);
	}
	
	col.SetUnits(strUnits);
	col.SetLongName(strLongName);
	col.SetComments(strComments);
	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	updateUserTree(col, lpcszVar, STR_VAR_INFO);
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	return true;
}

int		NetCDF::getVector(LPCSTR lpcszVar, Column& col, DWORD dwCntrl)//netcdf_get_vector
{
	TreeNode trVar = getVarNode(lpcszVar);
	if ( !trVar )
		return NC_FAIL;
	
	int nUnlimitedDimID = NC_BAD;
	if ( NC_BAD != m_ncID )
		ncinquire(m_ncID, NULL, NULL, NULL, &nUnlimitedDimID);
	
	LPVOID lpData = NULL;
	
	TreeNode trDims = trVar.GetNode(STR_VAR_DIM_NUM);
	int nNumDims = trDims.nVal;
	
	vector<int>	vnDimIDs;
	vector<int>	vnDimLens;
	TreeNode	trDimIDs = trVar.GetNode(STR_VAR_DIM_IDS);
	TreeNode	trDimLens = trVar.GetNode(STR_VAR_DIM_LENS);
	vnDimIDs = trDimIDs.nVals;
	vnDimLens = trDimLens.nVals;
	
	if ( nNumDims == 0 || vnDimIDs[0] != nUnlimitedDimID || vnDimLens[0] != 0 )
		return getAndPutVarData(trVar, col, dwCntrl);
		
	return NC_FAIL;
}

int		NetCDF::getAndPutVarData(TreeNode& trVar, Column& col, DWORD dwCntrl)//netcdf_get_and_put_var_data
{
	if ( !trVar )
		return NC_FAIL;
	
	size_t	Start[NC_MAX_DIMS];
	size_t	Count[NC_MAX_DIMS];
	size_t	Stride[NC_MAX_DIMS];
	
	int		nNCStatus;
	
	TreeNode	trNumDims = trVar.GetNode(STR_VAR_DIM_NUM);
	int			nRank = trNumDims.nVal;
	size_t		nTotalNumElements = 1;
	
	TreeNode	trDimLens = trVar.GetNode(STR_VAR_DIM_LENS);
	vector<int>	vnDimLens;
	vnDimLens = trDimLens.nVals;
	
	int nType;
	trVar.GetNode(STR_OC_TYPE).GetAttribute(STR_VAR_TYPE_ATTRIB, nType);
	
	if ( nRank < 3 )
	{
		if ( nRank != 0 )
		{
			if ( nRank == 1 ) //one dimension data, will be put into column
			{
				nTotalNumElements = vnDimLens[0];
				Start[0] = 0;
				Count[0] = nTotalNumElements;
			}
			else //two dimension data, will be put into worksheet or matrix
			{
				nTotalNumElements = vnDimLens[0] * vnDimLens[1];
				Start[0] = 0;
				Count[0] = vnDimLens[0];
				Start[1] = 0;
				Count[1] = vnDimLens[1];
			}
		}
		else //only one data, will be put into cell
		{
			Start[0] = 0;
			Count[0] = 1;
		}
		
		nNCStatus = getAndPutVarData(trVar, col, dwCntrl, Start, Count, nTotalNumElements);
		
		return nNCStatus;
	}
	//////////////////////////////////////////////////////////////////////////////////////
	// For more than two dimension nc variable, every read will get two dimension data
	//////////////////////////////////////////////////////////////////////////////////////
	// Total number of values for variable
	for ( int ii = 0; ii < nRank; ii++ )
	{
		Start[ii] = 0;
		Count[ii] = 1;
		Stride[ii] = 0;
		nTotalNumElements *= vnDimLens[ii];
	}
	 //every read will get two dimension data
	 Stride[nRank - 3] = 1; //nRank > 2
	 Count[nRank - 2] = vnDimLens[nRank - 2];
	 Count[nRank - 1] = vnDimLens[nRank - 1];
	 
	 size_t	nNumGet = vnDimLens[nRank - 1] * vnDimLens[nRank - 2];
	 int	nNumRuns = nTotalNumElements / nNumGet;
	 
	 for ( int ir = 0; ir < nNumRuns; ir++ )
	 {
	 	nNCStatus = getAndPutVarData(trVar, col, dwCntrl, Start, Count, nNumGet);
	 	if ( ir < nNumRuns - 1 )
	 	{
	 		if ( updateStart(vnDimLens, nRank, Start, Stride) == NC_FAIL )
	 			break;
	 	}
	 	if ( nNCStatus != NC_SUCCESS )
	 		break;
	 }
	 
	 return nNCStatus;
}

int		NetCDF::getAndPutVarData(TreeNode& trVar, Column& col, DWORD dwCntrl, const size_t *pStart, const size_t *pCount, int nSize)
{
	int		nNCStatus = NC_FAIL;
	int		nVarID;
	trVar.GetAttribute(STR_VAR_ID_ATTRIB, nVarID);
	int		nType;
	trVar.GetNode(STR_OC_TYPE).GetAttribute(STR_VAR_TYPE_ATTRIB, nType);
	
	switch( nType )
	{
	case NC_CHAR:
		vector<char>	vcData(nSize);
		nNCStatus = nc_get_vara_text(m_ncID, nVarID, pStart, pCount, vcData);
		if ( col )
			putVarData(trVar, col, dwCntrl, vcData);
		break;
		
	case NC_BYTE:
		vector<byte>	vbData(nSize);
		nNCStatus = nc_get_vara_schar(m_ncID, nVarID, pStart, pCount, (byte*)vbData);
		if ( col )
			putVarData(trVar, col, dwCntrl, vbData);
		break;
		
	case NC_SHORT:
		vector<short>	vshData(nSize);
		nNCStatus = nc_get_vara_short(m_ncID, nVarID, pStart, pCount, vshData);
		if ( col )
			putVarData(trVar, col, dwCntrl, vshData);
		break;
		
	case NC_INT:
		vector<int>		vnData(nSize);
		nNCStatus = nc_get_vara_long(m_ncID, nVarID, pStart, pCount, vnData);
		if ( col )
			putVarData(trVar, col, dwCntrl, vnData);
		break;
		
	case NC_FLOAT:
		vector<float>	vfData(nSize);
		nNCStatus = nc_get_vara_float(m_ncID, nVarID, pStart, pCount, vfData);
		if ( col )
			putVarData(trVar, col, dwCntrl, vfData);
		break;
		
	case NC_DOUBLE:
		vector<double>	vData(nSize);
		nNCStatus = nc_get_vara_double(m_ncID, nVarID, pStart, pCount, vData);
		if ( col )
			putVarData(trVar, col, dwCntrl, vData);
		break;
		
	default:
		ASSERT(false);
		break;
	}
	
	return nNCStatus;
}

int		NetCDF::putVarData(TreeNode& trVar, Column& col, DWORD dwCntrl, vectorbase& vData) //netcdf_put_data
{
	int nType;
	trVar.GetNode(STR_OC_TYPE).GetAttribute(STR_VAR_TYPE_ATTRIB, nType);
	if ( isNumericType(nType) )
	{
		vector	vdData;
		if ( dwCntrl & NC_CONVERT_DOUBLE )
		{

			TreeNode trMissValue = trVar.GetNode(STR_VAR_MISSVALUE);
			TreeNode trFillValue = trVar.GetNode(STR_VAR_FILLVALUE);
			int		nHasMiss = trMissValue.nVal;
			int		nHasFill = trFillValue.nVal;
			
			double	dMissVal, dFillVal;
			trMissValue.GetAttribute(STR_MISSINGVALUE_ATTRIB, dMissVal);
			trFillValue.GetAttribute(STR_FILLINGVALUE_ATTRIB, dFillVal);
			
			vdData = vData;
			if ( nHasMiss )
				vdData.Replace(dMissVal, NANUM, MATREPL_TEST_EQUAL);
			if ( nHasFill )
				vdData.Replace(dFillVal, NANUM, MATREPL_TEST_EQUAL);

		}
		else
			vdData = vData;

		vectorbase& vInternal = col.GetDataObject();
		vInternal = vdData;
	}
	
	return NC_SUCCESS;
}


bool	NetCDF::isNumericType(NCTYPE ncType)//netcdf_is_numeric_type
{
	switch( ncType )
	{
	case NC_NAT:
	case NC_CHAR:
		return false;
	case NC_BYTE:
	case NC_SHORT:
	case NC_INT:
	case NC_FLOAT:
	case NC_DOUBLE:
		return true;
	default:
		ASSERT(false);//should never come here
		return false;
	}
	
	ASSERT(false); //should never come here
	return false;
}

int		NetCDF::updateStart(const vector<int>& vnDims, int nNumDims, size_t *pStart, const size_t *pStride)//update_start
{
	for ( int ii = nNumDims - 2; ii > 0; ii-- )
	{
		pStart[ii] += pStride[ii];
		if ( pStart[ii] >= vnDims[ii] )
		{
			pStart[ii - 1]++;
			pStart[ii] -= vnDims[ii];
		}
	}
	pStart[0] += pStride[0];
	return (pStart[0] >= vnDims[0]) ? NC_FAIL : NC_SUCCESS;
}

TreeNode	NetCDF::getVarNode(LPCSTR lpcszVar)
{
	TreeNode trVar;
	
	TreeNode trVars = m_trNetCDFHeader.GetNode(STR_VARTREE_NAME);
	if ( trVars )
		trVar = trVars.GetNode(lpcszVar);
	
	return trVar;
}

int		NetCDF::getVarData(LPVOID lpData, LPCSTR lpcszVar, int* pDim, int nObjDims, int nFSIType)
{
	if ( !lpData )
		return NC_FAIL;
	
	TreeNode trVar = getVarNode(lpcszVar);
	if ( !trVar )
		return NC_FAIL;
	
	int ncType;
	trVar.GetNode(STR_OC_TYPE).GetAttribute(STR_VAR_TYPE_ATTRIB, ncType);
	if ( nFSIType != getOCType(ncType) )
		return NC_FAIL;
	
	size_t	Start[NC_MAX_DIMS];
	size_t	Count[NC_MAX_DIMS];
	setStartAndCount(trVar, Start, Count, pDim, nObjDims);
	
	int	nNCStatus;
	
	int nVarID;
	trVar.GetAttribute(STR_VAR_ID_ATTRIB, nVarID);
	//centralize netcdf_get_var_data, netcdf_get_vara
	switch( nFSIType )
	{
	case FSI_REAL: //NC_FLOAT
		nNCStatus = nc_get_vara_float(m_ncID, nVarID, Start, Count, (float*)lpData);
		break;
		
	case FSI_DOUBLE: //NC_DOUBLE
		nNCStatus = nc_get_vara_double(m_ncID, nVarID, Start, Count, (double*)lpData);
		break;
		
	case FSI_LONG: //NC_INT
		nNCStatus = nc_get_vara_long(m_ncID, nVarID, Start, Count, (long *)lpData);
		break;
		
	case FSI_SHORT: //NC_SHORT
		nNCStatus = nc_get_vara_short(m_ncID, nVarID, Start, Count, (short*)lpData);
		break;
		
	case FSI_CHAR:
		if ( NC_CHAR == ncType ) //NC_CHAR
			nNCStatus = nc_get_vara_text(m_ncID, nVarID, Start, Count, (char*)lpData);
		else //NC_BYTE
			nNCStatus = nc_get_vara_schar(m_ncID, nVarID, Start, Count, (char*)lpData);
		break;
	default:
		ASSERT(false);
		nNCStatus = NC_FAIL;
		break;
	}
	
	return nNCStatus;
}

int		NetCDF::setStartAndCount(TreeNode& trVar, size_t *pStart, size_t *pCount, int *pDim, int nReadDim)
{
	ASSERT(trVar && pStart && pCount && nReadDim >= 0);
	
	TreeNode trNumDims = trVar.GetNode(STR_VAR_DIM_NUM);
	int nDims = trNumDims.nVal;
	
	vector<int> vnDimLen;
	TreeNode trDimLens = trVar.GetNode(STR_VAR_DIM_LENS);
	vnDimLen = trDimLens.nVals;
	if ( pDim )
	{

		for ( int ii = 0; ii < nDims; ii++ )
		{
			*(pStart + ii) = 0;
			*(pCount + ii) = 1;
		}
		ii = 0;
		while ( *(pDim + ii ) != 0 )
		{
			*(pStart + ii ) = *(pDim + ii ) -1; //netcdf dimension starts from 0;
			ii++;
		}
		
		if ( ii != nDims - nReadDim )
			return NC_FAIL; //pDim is wrong according to nReadDim.
		
		for ( ; ii < nDims; ii++ )
			*(pCount + ii ) = vnDimLen[ii];
	}
	else
	{
		ASSERT(nDims == 1);
		*pStart = 0;
		*pCount = vnDimLen[0];
	}
	return NC_SUCCESS;
}

///Sophy 9/11/2009 QA80-13128 NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS
bool	NetCDF::getXDMatrixes(LPCSTR lpcszVar, MatrixLayer& ml, const vector<int>& vnDims, vector<int>& vnRef, int nDepth)
{
	bool bRet = true;
	if ( nDepth == vnRef.GetSize() )
	{
		int iMatObj;
		if ( vnRef.GetSize() > 0 )
		{
			iMatObj = vnRef[nDepth - 1];
			for ( int iDim = nDepth - 2; iDim >= 0; iDim -- )
				iMatObj += vnRef[iDim] * vnDims[iDim + 1];
		}
		else
			iMatObj = 0; //lpcszVar is 2D, imported into the first matrixobject.
		
		bRet =  getOneMatrix(lpcszVar, ml, iMatObj, vnRef);
	}
	else if ( nDepth < vnRef.GetSize() )
	{
		for ( int ii = 0; ii < vnDims[nDepth]; ii++ )
		{
			vnRef[nDepth] = ii;
			bRet = getXDMatrixes(lpcszVar, ml, vnDims, vnRef, nDepth + 1);//init next dimension of vnRef.
			if ( !bRet )
				break; //if any exception, break the operation.
		}
	}
	else
	{
		ASSERT(FALSE); //misused.
		bRet = false;
	}	
	
	return bRet;
}

bool	NetCDF::getOneMatrix(LPCSTR lpcszVar, MatrixLayer& ml, int iMatObj, const vector<int>& vnRef)
{
	TreeNode trVar = getVarNode(lpcszVar);
	
	vector<int> vnDimLen;
	TreeNode trDimLens = trVar.GetNode(STR_VAR_DIM_LENS);
	vnDimLen = trDimLens.nVals;
	int nDims = vnDimLen.GetSize();
	
	vector<size_t> vStart(nDims);
	vector<size_t> vCount(nDims);
	setStartAndCount(vStart, vCount, vnDimLen, vnRef);
	
	int nVarID;
	trVar.GetAttribute(STR_VAR_ID_ATTRIB, nVarID);

	int ncType, nFSIType;
	trVar.GetNode(STR_OC_TYPE).GetAttribute(STR_VAR_TYPE_ATTRIB, ncType);
	nFSIType = getOCType(ncType);

	int nNCStatus = NC_SUCCESS;
	switch( nFSIType )
	{
	case FSI_REAL: //NC_FLOAT
		Matrix<float> mo(ml, iMatObj);
		nNCStatus = nc_get_vara_float(m_ncID, nVarID, vStart, vCount, (float*)mo);
		break;
		
	case FSI_DOUBLE: //NC_DOUBLE
		Matrix<double> mo(ml, iMatObj);
		nNCStatus = nc_get_vara_double(m_ncID, nVarID, vStart, vCount, (double*)mo);
		break;
		
	case FSI_LONG: //NC_INT
		Matrix<long> mo(ml, iMatObj);
		nNCStatus = nc_get_vara_long(m_ncID, nVarID, vStart, vCount, (long *)mo);
		break;
		
	case FSI_SHORT: //NC_SHORT
		Matrix<short> mo(ml, iMatObj);
		nNCStatus = nc_get_vara_short(m_ncID, nVarID, vStart, vCount, (short*)mo);
		break;
		
	case FSI_CHAR:
		Matrix<byte> mo(ml, iMatObj);
		if ( NC_CHAR == ncType ) //NC_CHAR
			nNCStatus = nc_get_vara_text(m_ncID, nVarID, vStart, vCount, (char*)(byte*)mo);
		else //NC_BYTE
			nNCStatus = nc_get_vara_schar(m_ncID, nVarID, vStart, vCount, (char*)(byte*)mo);
		break;
	default:
		ASSERT(false);
		nNCStatus = NC_FAIL;
		break;
	}
	
	return (nNCStatus == NC_SUCCESS);
}

int		NetCDF::setStartAndCount(vector<size_t>& vStart, vector<size_t>& vCount, const vector<int>& vnDims, const vector<int>& vnRef)
{
	if ( vnDims.GetSize() - vnRef.GetSize() != 2 )
		return NC_FAIL;
	
	int ii = 0;
	for ( ii = 0; ii < vnRef.GetSize(); ii++ )
	{
		vStart[ii] = vnRef[ii];
		vCount[ii] = 1;
	}
	for ( ; ii < vnDims.GetSize(); ii++ )
	{
		vStart[ii] = 0;
		vCount[ii] = vnDims[ii];
	}
	return NC_SUCCESS;
}

///end NC_SUPPORT_IMPORT_MULTIPLE_DIMS_AS_SINGLE_SHEET_WITH_MULTIPLE_OBJS

bool	NetCDF::constructVarTree()
{
	char	varName[NC_MAX_NAME];
	int		varID;
	int		nNCStatus;
	if ( (nNCStatus = nc_inq_nvars(m_ncID, &m_nNumVars) != NC_NOERR) )
		return false;
	
	TreeNode	trVars = tree_check_get_node(m_trNetCDFHeader, STR_VARTREE_NAME);
	for ( varID = 0; varID < m_nNumVars; varID++ )
	{
		if ( !addOneVarInfo(varID, trVars) )
			return false;
	}
	return true;
}

bool	NetCDF::constructDimTree()
{
	if ( m_ncID == NC_BAD )
		return false;

	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	//if ( ncinquire(m_ncID, &m_nNumDims, NULL, NULL, NULL) != NC_NOERR )
	if ( ncinquire(m_ncID, &m_nNumDims, NULL, NULL, NULL) != m_ncID ) //the API successfully executed if return input NC ID
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
		return false;
	
	TreeNode trDims = tree_check_get_node(m_trNetCDFHeader, STR_DIMTREE_NAME);
	TreeNode trOneDim;
	//NCDIM structure
	char	dimName[NC_MAX_NAME];
	size_t	nSize;
	for ( int nDimID = 0; nDimID < m_nNumDims; nDimID++ )
	{
		///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
		//if ( ncdiminq(m_ncID, nDimID, dimName, (long*)&nSize) == NC_NOERR )
		if ( ncdiminq(m_ncID, nDimID, dimName, (long*)&nSize) == nDimID ) //return source data ID on no error. By look into netcdf source code
		///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
		{
			string strDimName(dimName);
			strDimName.MakeValidCName();
			trOneDim = tree_check_get_node(trDims, strDimName);
			trOneDim.SetAttribute(STR_LABEL_ATTRIB, dimName);
			trOneDim.nVal = nSize;
		}
	}
	return true;
}

bool	NetCDF::constructAttTree()
{
	if ( m_ncID == NC_BAD )
		return false;
	
	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	//if ( ncinquire(m_ncID, NULL, NULL, &m_nNumAttribs, NULL) != NC_NOERR )
	if ( ncinquire(m_ncID, NULL, NULL, &m_nNumAttribs, NULL) != m_ncID ) //the API successfully executed when return input NC ID
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
		return false;
	
	if ( m_nNumAttribs > 0 )
	{
		TreeNode trAtts = tree_check_get_node(m_trNetCDFHeader, STR_ATTTREE_NAME);
		for ( int nAttID = 0; nAttID < m_nNumAttribs; nAttID++ )
			addOneAttribInfo(NC_GLOBAL, nAttID, trAtts, NULL);
	}
	return true;
}

bool	NetCDF::addOneVarInfo(int nVarID, TreeNode& trParent)
{
	if ( !trParent.IsValid() )
		return false;
	
	NCTYPE	nType;
	int		nDims;
	int		nAttribs;
	int		DimIDs[NC_MAX_DIMS];
	char	DimName[NC_MAX_NAME];
	char	VarName[NC_MAX_NAME];
	int		nHasFill = 0;
	double	dFillVal = NC_FILL_DOUBLE;
	int		nHasMiss = 0;
	double	dMissVal = NC_FILL_DOUBLE;
	
	char	dimString[256];
	
	double	dMin = NC_FILL_DOUBLE;
	double	dMax = NC_FILL_DOUBLE;
	double	dDelta = 0;
	
	int		nNCStatus;
	///////////////////////
	nNCStatus = nc_inq_var(m_ncID, nVarID, VarName, &nType, &nDims, DimIDs, &nAttribs);
	if ( nNCStatus != NC_NOERR )
		return false;
	
	size_t	nDimLen;
	vector<size_t> vnDims(nDims);
	for ( int ii = 0; ii < nDims; ii++ )
	{
		if ( (nNCStatus = nc_inq_dimlen(m_ncID, DimIDs[ii], &nDimLen)) != NC_NOERR )
			return false;
		
		vnDims[ii] = nDimLen;
	}

	strcpy(dimString, "(");
	
	vector<int> vnDimIDs(nDims);
	for ( ii = 0; ii < nDims; ii++ )
	{
		if ( (nNCStatus = nc_inq_dimname(m_ncID, DimIDs[ii], DimName)) != NC_NOERR )
			return false;
		
		vnDimIDs[ii] = DimIDs[ii];
		
		strcat(dimString, DimName);
		if ( ii != nDims - 1 )
			strcat(dimString, ", ");
	}
	strcat(dimString, ")");
	
	//attributes
	string	strDescript;
	if ( getAttribString(nVarID, STR_LONGNAME_ATTRIB, strDescript) == 0 )
	{
		strDescript = "";
	}
	
	string	strUnits;
	if ( getAttribString(nVarID, STR_UNITS_ATTRIB, strUnits) == 0 )
	{
		strUnits = "None";
	}
	
	nHasFill = getFillValue(nVarID, &dFillVal);
	nHasMiss = getMissValue(nVarID, &dMissVal);
	
	dMin = NC_FILL_DOUBLE;
	dMax = NC_FILL_DOUBLE;
	dDelta = 0;

	//above code is as netcdf_get_var(LPCSTR lpcszNCFile, int ncid, LPCSTR varname)
	//add to tree, as netcdf_var_info(NCVAR *pvar, TreeNode &trParent)
	
	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	//TreeNode trVar = tree_check_get_node(trParent, VarName);
	string strVarName(VarName);
	strVarName.MakeValidCName();
	TreeNode trVar = tree_check_get_node(trParent, strVarName);
	trVar.SetAttribute(STR_LABEL_ATTRIB, VarName);
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	trVar.SetAttribute(STR_VAR_ID_ATTRIB, nVarID);

	TreeNode trType = tree_check_get_node(trVar, STR_OC_TYPE);
	trType.SetAttribute(STR_LABEL_ATTRIB, STR_DATA_TYPE);	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	trType.SetAttribute(STR_VAR_TYPE_ATTRIB, nType); //for GetOneVarInfo to get type value
	trType.strVal = getTypeName(nType);
	
	string strDim(dimString);
	if ( 0 == lstrcmpi(dimString, "()") )
		strDim.Insert(1, STR_NETCDF_SCALAR_VARAILBE_DIMENSION_LABEL);
	
	TreeNode trDims = tree_check_get_node(trVar, STR_DIM_NAMES);
	trDims.SetAttribute(STR_LABEL_ATTRIB, STR_DIM_NAMES_INFO);	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	trDims.strVal = strDim;
	////Idle nodes, to keep some params for later use internally.
	TreeNode trDimIDs = tree_check_get_node(trVar, STR_VAR_DIM_IDS);
	trDimIDs.nVals = vnDimIDs;
	
	TreeNode trDimLens = tree_check_get_node(trVar, STR_VAR_DIM_LENS);
	trDimLens.SetAttribute(STR_LABEL_ATTRIB, STR_DIM_SIZE);	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	trDimLens.nVals = vnDims;
	
	TreeNode trDimNum = tree_check_get_node(trVar, STR_VAR_DIM_NUM);
	trDimNum.SetAttribute(STR_LABEL_ATTRIB, STR_DIMENSIONALITY); ///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	trDimNum.nVal = nDims;
	
	TreeNode trUnits = tree_check_get_node(trVar, STR_VAR_UNITS);
	trUnits.strVal = strUnits;
	
	TreeNode trDescript = tree_check_get_node(trVar, STR_VAR_DESCRIPT);
	trDescript.strVal = strDescript;
	
	string	strComments;
	for ( int nAttID = 0; nAttID < nAttribs; nAttID++ )
		addOneAttribInfo(nVarID, nAttID, trVar, &strComments);
	
	TreeNode trComments = tree_check_get_node(trVar, STR_VAR_COMMENTS);
	trComments.strVal = strComments;
	
			
	TreeNode trMissValue = tree_check_get_node(trVar, STR_VAR_MISSVALUE);
	trMissValue.nVal = nHasMiss;
	trMissValue.SetAttribute(STR_MISSINGVALUE_ATTRIB, dMissVal);
	
	TreeNode trFillValue = tree_check_get_node(trVar, STR_VAR_FILLVALUE);	
	trFillValue.nVal = nHasFill;
	trFillValue.SetAttribute(STR_FILLINGVALUE_ATTRIB, dFillVal);
	
	return true;
}

bool	NetCDF::addOneAttribInfo(int nVarID, int nAttribID, TreeNode& trParent, string* pstrDescript)
{
	if ( m_ncID == NC_BAD || !trParent )
		return false;
	
	//NCATT members
	int		nVar;
	char	attName[NC_MAX_NAME];
	NCTYPE	ncType;
	size_t	nLen;
	
	if ( ncattname(m_ncID, nVarID, nAttribID, attName) == NC_FAIL )
		return false;
	if( ncattinq(m_ncID, nVarID, attName, &ncType, (int*)&nLen) == NC_FAIL )
		return false;
	
	//take 0-length attributes as empty strings
	if ( 0 == nLen )
	{
		ncType = NC_CHAR;
		nLen = 1;
	}
	
	int		nNCStatus;
	string strAttName(attName);
	strAttName.MakeValidCName();
	if ( strAttName.CompareNoCase(STR_VAR_UNITS) == 0 ) //already added outside, no need to add duplicated informatino
		return true;
	
	TreeNode trAttrib = tree_check_get_node(trParent, strAttName);
	trAttrib.SetAttribute(STR_LABEL_ATTRIB, attName);
	
	switch(ncType)
	{
	case NC_CHAR:
		string	strVal;
		LPSTR	lpBuffer = strVal.GetBuffer(nLen);;

		if ( (nNCStatus = nc_get_att_text(m_ncID, nVarID, attName, lpBuffer)) != NC_NOERR )
		{
			ASSERT(false); //error handling might need here.
		}
		strVal.ReleaseBuffer();
		
		if ( NC_NOERR == nNCStatus ) //no err
		{
			trAttrib.strVal = strVal;
			
			if ( pstrDescript )
			{
				string str;
				str.Format("%s = %s\n", attName, strVal);
				*pstrDescript += str;
			}
		}

		return (nNCStatus == NC_NOERR);
		
	default:
		vector<double>	vdVals(nLen);

		
		nNCStatus = nc_get_att_double(m_ncID, nVarID, attName, vdVals);
		if ( nNCStatus != NC_NOERR )
		{
			ASSERT(false);
			return false;
		}
		
		if ( nNCStatus == NC_NOERR )
		{
			string strVal;
			if ( getAttribValues(strVal, ncType, vdVals) > 0 )
			{
				trAttrib.strVal = strVal;
				
				if ( pstrDescript )
				{
					string str;
					str.Format("%s = %s\n", attName, strVal);
					*pstrDescript += str;
				}
			}
		}

		return (nNCStatus == NC_NOERR);
	}
	
	return true;
}

int		NetCDF::getAttribString(int nVarID, char* attName, string& strVal) //netcdf_get_att_str
{
	int		nNCStatus;
	double	dAttribVal;
	NCTYPE	xType;
	size_t	nLen;
	
	if ( (nNCStatus = nc_inq_att(m_ncID, nVarID, attName, &xType, &nLen)) == NC_ENOTATT )
		return 0;
	else if ( nNCStatus != NC_NOERR )
		return 0;
	else if ( NC_CHAR == xType )
	{
		LPSTR	lpStr = strVal.GetBuffer(nLen);
		if ( (nNCStatus = nc_get_att_text(m_ncID, nVarID, attName, lpStr)) != NC_NOERR )
		{
			strVal.ReleaseBuffer();
			return 0;
		}
		strVal.ReleaseBuffer();
	}
	else
	{
		if ( (nNCStatus = nc_get_att_double(m_ncID, nVarID, attName, &dAttribVal)) != NC_NOERR )
		{
			return 0;
		}
		strVal.Format("%g", dAttribVal);
	}
	return 1;
}

int		NetCDF::getAttribDouble(int nVarID, char* attName, double* attVal)
{
	int		nNCStatus;
	NCTYPE	xType;
	size_t	nLen;
	
	if ( (nNCStatus = nc_inq_att(m_ncID, nVarID, attName, &xType, &nLen)) == NC_ENOTATT )
		return 0;
	else if ( nNCStatus != NC_NOERR )
		return 0;
	else if ( NC_CHAR != xType )
	{
		if ( (nNCStatus = nc_get_att_double(m_ncID, nVarID, attName, attVal)) != NC_NOERR )
			return 0;
	}
	else
	{
		string	strVal;
		LPSTR	lpStr = strVal.GetBuffer(nLen);
		if ( (nNCStatus = nc_get_att_text(m_ncID, nVarID, attName, lpStr)) != NC_NOERR )
		{
			strVal.ReleaseBuffer();
			return 0;
		}

		strVal.ReleaseBuffer();
		*attVal = atof(strVal);
	}
	return 1;
}

int		NetCDF::getAttribValues(string& strVal, NCTYPE ncType, const vector& vVals)
{
	int		nLen = vVals.GetSize();
	if ( 0 == nLen )
		return 0;
	
	char	cVal;
	short	sVal;
	int		nVal;
	float	fVal;
	double	dVal;
	
	string	str;
	strVal.Empty();
	for ( int ii = 0; ii < nLen - 1; ii++ )
	{
		switch(ncType)
		{
		case NC_BYTE:
			cVal = (char) vVals[ii] & 0377;
			str.Format("%db, ", cVal);
			break;
			
		case NC_SHORT:
			sVal = (short) vVals[ii];
			str.Format("%ds, ", sVal);
			break;
			
		case NC_INT:
			nVal = (int) vVals[ii];
			str.Format("%d, ", nVal);
			break;
			
		case NC_FLOAT:
			fVal = (float) vVals[ii];
			str.Format("%#.7gf, ", fVal);
			break;
		case NC_DOUBLE:
			dVal = vVals[ii];
			str.Format("%#.15g, ", dVal);
			break;
		default:
	    	ASSERT(FALSE);
			return 0;
		}
		strVal += str;
	}
	
	switch(ncType)
	{
	case NC_BYTE:
		cVal = (char) vVals[ii] & 0377;
		str.Format("%db", cVal);
		break;
		
	case NC_SHORT:
		sVal = (short) vVals[ii];
		str.Format("%ds", sVal);
		break;
		
	case NC_INT:
		nVal = (int) vVals[ii];
		str.Format("%d", nVal);
		break;
		
	case NC_FLOAT:
		fVal = (float) vVals[ii];
		str.Format("%#.7gf", fVal);
		break;
	case NC_DOUBLE:
		dVal = vVals[ii];
		str.Format("%#.15g", dVal);
		break;
	default:
    	ASSERT(FALSE);
		return 0;
	}
	
	strVal += str;
	return strVal.GetLength();
}

int		NetCDF::getFillValue(int nVarID, double* dFillVal)
{
	int		nNCStatus;
	int		nHasFill = 1; //default, will turn off for bytes
	NCTYPE	attType;
	size_t	nLen;
	char	cFill;
	NCTYPE	varType;
	
	if ( (nNCStatus = nc_inq_vartype(m_ncID, nVarID, &varType)) != NC_NOERR )
		return 0;
	if ( (nNCStatus = nc_inq_att(m_ncID, nVarID, _FillValue, &attType, &nLen)) == NC_NOERR )
	{
		if ( (attType == varType) && nLen == 1 )
		{
			if ( varType == NC_CHAR )
			{
				if ( (nNCStatus = nc_get_att_text(m_ncID, nVarID, _FillValue, &cFill)) != NC_NOERR)
					cFill = NC_FILL_CHAR;
				
				*dFillVal = (double)cFill;
			}
		}
		else
		{
			nNCStatus = nc_get_att_double(m_ncID, nVarID, _FillValue, dFillVal);
		}
	}
	
	if ( nNCStatus != NC_NOERR )
	{
		switch( varType )
		{
		case NC_BYTE:
			nHasFill = 0;
			break;
		case NC_CHAR:
			*dFillVal = (double)NC_FILL_CHAR;
			break;
		case NC_SHORT:
			*dFillVal = (double)NC_FILL_SHORT;
			break;
		case NC_INT:
			*dFillVal = (double)NC_FILL_INT;
			break;
		case NC_FLOAT:
			*dFillVal = (double)NC_FILL_FLOAT;
			break;
		case NC_DOUBLE:
			*dFillVal = (double)NC_FILL_DOUBLE;
			break;
		default:
			break;
		}
	}
	return nHasFill;
}

//int		NetCDF::getVarFillValue(int nVarID, char* varName, double* dVal)
//{
	//int nNCStatus;
//}
int		NetCDF::getMissValue(int nVarID, double* dMissVal)
{
	if ( getAttribDouble(nVarID, STR_MISSINGVALUE_ATTRIB, dMissVal) == 0 )//First look for a field level missing_value attribute
		if ( getAttribDouble(NC_GLOBAL, STR_MISSINGVALUE_ATTRIB, dMissVal) == 0 )//Next look for a global missing_value attribute
			if ( getAttribDouble(nVarID, STR_MISSINGDATA_ATTRIB_A, dMissVal) == 0 )//Now check for a field level missing_data attribute
				if ( getAttribDouble(NC_GLOBAL, STR_MISSINGDATA_ATTRIB_A, dMissVal) == 0 )//Next check for a global missing_data attribute
					if ( getAttribDouble(nVarID, STR_MISSINGDATA_ATTRIB_B, dMissVal) == 0 )//Now check for the old arm style missing-data attribute
						if ( getAttribDouble(NC_GLOBAL, STR_MISSINGDATA_ATTRIB_B, dMissVal) == 0 )
							return 0;
						
					
	return 1;
				
}

LPCSTR	NetCDF::getTypeName(NCTYPE ncType)
{
	switch(ncType)
	{
	case NC_BYTE:
		return "byte";
		
	case NC_CHAR:
		return "char";
		
	case NC_SHORT:
		return "short";
		
	case NC_INT:
		return "int";
		
	case NC_FLOAT:
		return "float";
		
	case NC_DOUBLE:
		return "double";
	}	

	ASSERT(FALSE);
	return "bogus";
}

int		NetCDF::getOCType(NCTYPE ncType)
{
	switch( ncType )
	{
	case NC_NAT:		// NAT = 'Not A Type' (c.f. NaN)
		return FSI_MIXED;
	case NC_BYTE:		// signed 1 byte integer
//		return FSI_BYTE;
	case NC_CHAR:		// ISO/ASCII character
		return FSI_CHAR;
	case NC_SHORT:		// signed 2 byte integer
		return FSI_SHORT;
	case NC_INT:		// signed 4 byte integer
		return FSI_LONG;
	case NC_FLOAT:		// single precision floating point number
		return FSI_REAL;
	case NC_DOUBLE:		// double precision floating point number
		return FSI_DOUBLE;
	}

	ASSERT(FALSE); // Should never come here
	return -1;
}

void	NetCDF::removeIdleNodes(TreeNode& trVar)
{
	if ( !trVar )
		return;
	trVar.RemoveChild(STR_VAR_DIM_IDS);
	///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	//trVar.RemoveChild(STR_VAR_DIM_LENS);
	//trVar.RemoveChild(STR_VAR_DIM_NUM);
	TreeNode trDimLens = trVar.GetNode(STR_VAR_DIM_LENS);
	if ( trDimLens )
	{
		vector<int> vnDims;
		vnDims = trDimLens.nVals;
		vector<string> vsDims;
		convert_int_vector_to_string_vector(vnDims, vsDims);
		trDimLens.Reset();
		trDimLens.strVal = get_display_string(vsDims, vsDims.GetSize(), STR_DIMS_SEP);
		trDimLens.SetAttribute(STR_LABEL_ATTRIB, STR_DIM_SIZE);
	}
	trVar.RemoveChild(STR_VAR_FILLVALUE);
	trVar.RemoveChild(STR_VAR_MISSVALUE);
	///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER
	//trVar.RemoveChild(STR_VAR_UNITS);
	trVar.RemoveChild(STR_VAR_DESCRIPT); ///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER descript duplicated with long_name, removed.
	trVar.RemoveChild(STR_VAR_COMMENTS);
}

///Sophy 9/8/2009 QA80-14271 DISPLAY_VARIABLE_INFO_IN_ORGANIZER
bool	NetCDF::updateUserTree(OriginObject& obj, LPCSTR lpcszVarName, LPCSTR lpcszLabel)
{
	TreeNode trVar = getVarNode(lpcszVarName);
	if ( !trVar || !obj )
		return false;
	
	TreeNode trVarCopy = trVar.Clone(true);
	removeIdleNodes(trVarCopy);
	
	string strInfoName(lpcszLabel);
	trVarCopy.SetAttribute(STR_LABEL_ATTRIB, strInfoName);
	///---Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
	//trVarCopy.Show = 0; ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
	//set_user_info(obj, strInfoName, trVarCopy);
	fu_set_import_file_info(obj, trVarCopy, strInfoName, IMPORT_INFO_TO_USER_TREE);
	///---END QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
	///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
	///---Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
	// roll back move column info out of user tree, as CP said
	//trVarCopy.Show = 1;
	//strInfoName = lpcszLabel;
	//strInfoName.Insert(0, "NetCDF ");
	//trVarCopy.SetAttribute(STR_LABEL_ATTRIB, strInfoName);
	//set_import_file_info(obj, trVarCopy, strInfoName);
	///---END QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
	///---END QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
	
	return true;
}
///end DISPLAY_VARIABLE_INFO_IN_ORGANIZER

///Sophy 11/9/2009 QA80-13130-P6 UPDATE_COLUMN_FORMAT_ACCORDING_TO_SOURCE_FILE_DATATYPE
bool	NetCDF::updateColumnFormat(Column& colObj, int nOCType)
{
	if ( !colObj )
		return false;
	switch(nOCType)
	{
	case FSI_CHAR:
	case FSI_BYTE:	
	case FSI_SHORT:	
	case FSI_LONG:
	case FSI_REAL:
	case FSI_DOUBLE:
		colObj.SetFormat(OKCOLTYPE_NUMERIC);
		colObj.SetInternalDataType(nOCType);
		break;
		
	default:
		ASSERT(false);
		return false;
	}
	return true;
}
///end UPDATE_COLUMN_FORMAT_ACCORDING_TO_SOURCE_FILE_DATATYPE

#endif //_NETCDFMANAGER_H_
