/*------------------------------------------------------------------------------*
 * File Name:				 													*
 * Creation: 																	*
 * Purpose: OriginC Source C file												*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 * Hong 12/25/06 ADD_MORE_DATA_TYPE_SUPPORT										*
 *	Hong 04/13/07 v8.0600 CHANGE_LABELS_REQUIRED_BY_JUSTIN						*
 *	Hong 04/13/07 v8.0600 FIX_X_COLUMN_NOT_IMPORTED								*
 *	Hong 06/11/08 v8.0882b FIX_NETCDF_LINKING_ERROR_CAUSE_BY_REMOVE_GETN_NEXT_PREV_DLG
 *	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			*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
// Including the system header file Origin.h should be sufficient for most Origin
// applications and is recommended. Origin.h includes many of the most common system
// header files and is automatically pre-compiled when Origin runs the first time.
// Programs including Origin.h subsequently compile much more quickly as long as
// the size and number of other included header files is minimized. All NAG header
// files are now included in Origin.h and no longer need be separately included.
//
// Right-click on the line below and select 'Open "Origin.h"' to open the Origin.h
// system header file.
#include <Origin.h>
////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////
// Include your own header files here.
#include <GetNBox.h>
#include "netcdf_Utils.h"
#include "fu_utils.h"

////////////////////////////////////////////////////////////////////////////////////
// Start your functions here.
BOOL netcdf_1d_col(LPCSTR lpcszVar, Worksheet &wks, int iCol)
{
	if( !lpcszVar || *lpcszVar == '\0' || !wks )
		return FALSE;

	string strUnit, strLongName, strComments;
	int nType;	
	vector<int> vnDim(NC_MAX_VAR_DIMS);
	vnDim = 0;
	int nVarDims = netcdf_get_var_info(lpcszVar, vnDim, &nType, &strUnit, &strLongName, &strComments);
	
	BOOL bIs1DVar = (0 == nVarDims || 1 == nVarDims || (2 == nVarDims && 1 == vnDim[0]));
	// if not one dimension variable, just return 
	if( !bIs1DVar )
		return FALSE;
	
	//---- CPY check if wks has needed col
	if(iCol >= wks.GetNumCols())
		wks.SetSize(-1, iCol+1);
	//----
	
	// Prepare the range string
	Column col(wks, iCol);
	int nErr = -1;
	string strRange;
	if(make_column_range_string(strRange, col))
		nErr = netcdf_get_vector(lpcszVar, strRange);
	
	/// Hong 04/13/07 v8.0600 CHANGE_LABELS_REQUIRED_BY_JUSTIN
	Tree trFileVarInfo;
	netcdf_get_vars_info(&trFileVarInfo);
	TreeNode trVars = trFileVarInfo.GetNode(IMPTREE_NODE_1D, false);
	TreeNode trVar = trVars.GetNode(lpcszVar, false);
	
	if ( trVar )
	{
		strComments = strLongName; // put long_name to Comments
		strLongName = trVar.tagName; // put variable name to long name
		
		///---Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
		//trVar.Show = 0; ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
		//set_user_info(col, (string)"ColumnInfo", trVar);
		fu_set_import_file_info(col, trVar, "ColumnInfo", 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
		//trVar.Show = 1;
		//fu_set_import_file_name_info(col, trVar, "netcdf_var");
		///---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
		
		/// Hong 04/13/07 v8.0600 FIX_X_COLUMN_NOT_IMPORTED
		string strDimName;
		strDimName = trVar.Dims.strVal;
		strDimName.Delete(0);
		strDimName.Delete(strDimName.GetLength() - 1);
		
		if( trVar.tagName == strDimName ) // x column
			col.SetType( OKDATAOBJ_DESIGNATION_X );
		else
			col.SetType( OKDATAOBJ_DESIGNATION_Y );
		/// end FIX_X_COLUMN_NOT_IMPORTED
	}
	/// end CHANGE_LABELS_REQUIRED_BY_JUSTIN
	
	col.SetUnits(strUnit);
	col.SetLongName(strLongName);
	col.SetComments(strComments);
	
	return true;
}

// Refer dim must be zero terminated, for example:
//  vector<int> vnRef = {1, 0}; // will read the 1st 2 dim data of 3D netcdf var
//  vector<int> vnRef = {4, 0}; // will read the 4th 2 dim data of 3D netcdf var
//  vector<int> vnRef = {1, 2, 0}; // will read the 2 dim data of 4D netcdf var
int netcdf_2d_mat(LPCSTR lpcszVar, MatrixLayer &matl, int nIndex, vector<int> &vnRef, int nType)
{
	if( !lpcszVar || *lpcszVar == '\0' || !matl )
		return 1; // Error
	
	int nRet = 0;
	switch( nType )
	{
	case FSI_REAL:
		Matrix<float> mm(matl, nIndex);
		nRet = netcdf_get_f_mat(mm, lpcszVar, vnRef);
		break;
		
	case FSI_DOUBLE:
		Matrix<double> mm(matl, nIndex);
		nRet = netcdf_get_d_mat(mm, lpcszVar, vnRef);
		break;
		
	case FSI_LONG:
		Matrix<int> mm(matl, nIndex);
		nRet = netcdf_get_i_mat(mm, lpcszVar, vnRef);
		break;
		
	case FSI_SHORT:
		Matrix<short> mm(matl, nIndex);
		nRet = netcdf_get_s_mat(mm, lpcszVar, vnRef);
		break;
	/// Hong 12/25/06 ADD_MORE_DATA_TYPE_SUPPORT	
	case FSI_CHAR:
		Matrix<byte> mm(matl, nIndex);
		nRet = netcdf_get_b_mat(mm, lpcszVar, vnRef);
		break;
	/// end ADD_MORE_DATA_TYPE_SUPPORT	
	default:
		break;
	}
	
	return nRet;
}

#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];
	
/**
	Read netcdf variable into a MatrixLayer
	Remark:
		netcdf file must be already open when calling this function, see netcdf_open
	Parameters:
		lpcszVar = [in] netcdf file variable name, support 2 or 3 or 4 dim variables
		matl = [in/out] MatrixLayer to read the data
		nMatObj1 = [in] Starting MatrixObject index to read data
		nMatObj2 = [in] Ending MatrixObject index to read data, if -1, then we will read to the size of the variable
		nDim = [in] index in the 4rd dimension to read if lpcszVar is more than 3 dimension 
*/
BOOL netcdf_xd_mat(LPCSTR lpcszVar, MatrixLayer &matl, int nDim, int nMatObj1, int nMatObj2) // = 1, 0, -1
{
	if( !lpcszVar || *lpcszVar == '\0' || !matl )
		return FALSE;
	
	int nType;
	vector<int> vnDim(NC_MAX_VAR_DIMS);
	vnDim = 0;
	int nVarDims = netcdf_get_var_info(lpcszVar, vnDim, &nType);
	if( nVarDims <= 0 )
		return FALSE;
	
	vector<int> vnRef(NC_MAX_VAR_DIMS);
	vnRef = 0; // Refer dim must be zero terminated
	int ii;
	
	if( !matl.SetInternalDataType(nType) )
		return FALSE;
	
	switch(nVarDims)
	{
	case 2:
		if( !matl.SetSize(1, vnDim[nVarDims-2], vnDim[nVarDims-1], 0) )
			return FALSE;
		netcdf_2d_mat(lpcszVar, matl, nMatObj1, vnRef, nType);
		break;
	case 3:
		// nMatObj1 and nMatObj2 must be in range
		//	if( nMatObj1 < 0 )
		//		nMatObj1 = 0;
		//	if( nMatObj1 >= vnDim[0] )
		//		nMatObj1 = vnDim[0] - 1;
		//	if( nMatObj2 < 0 || nMatObj2 > vnDim[0] )
		//		nMatObj2 = vnDim[0];
		CHECK_MATOBJ_WITH_VARDIM(vnDim, 0, nMatObj1, nMatObj2)
		
		if( !matl.SetSize(nMatObj2 - nMatObj1, vnDim[nVarDims-2], vnDim[nVarDims-1], 0) )
			return FALSE;
		
		for( ii = nMatObj1; ii < nMatObj2; ii++ )
		{
			vnRef[0] = ii+1;
			netcdf_2d_mat(lpcszVar, matl, ii, vnRef, nType);
		}
		break;
	case 4:
		ASSERT( nDim >= 0 && nDim < vnDim[0] );
		// nMatObj1 and nMatObj2 must be in range
		//	if( nMatObj1 < 0 )
		//		nMatObj1 = 0;
		//	if( nMatObj1 >= vnDim[1] )
		//		nMatObj1 = vnDim[1] - 1;
		//	if( nMatObj2 < 0 || nMatObj2 > vnDim[1] )
		//		nMatObj2 = vnDim[1];
		CHECK_MATOBJ_WITH_VARDIM(vnDim, 1, nMatObj1, nMatObj2)
		
		if( !matl.SetSize(nMatObj2 - nMatObj1, vnDim[nVarDims-2], vnDim[nVarDims-1], 0) )
			return FALSE;
		
		vnRef[0] = nDim + 1;
		for( ii = nMatObj1; ii < nMatObj2; ii++ )
		{
			vnRef[1] = ii+1;
			netcdf_2d_mat(lpcszVar, matl, ii, vnRef, nType);
		}

		break;
	default:
		printf("Error: %d dimensions not supported.\n", nVarDims);
		return FALSE;
	}
	
	return TRUE;
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// The following code is for import wizard, drag and drop

typedef int (*PFNLOADNETCDFINMAT)(Page &pgTarget, LPCSTR lpcszFile, TreeNode &trFilter, int nFile);

BOOL fuGetVariable(TreeNode& tnFilter, TreeNode& tnVariable)
{
	if( tnFilter.Variable )
	{
		tnVariable = tnFilter.Variable;
		return TRUE;
	}
	return FALSE;
}

BOOL fuGetVarName(TreeNode& tnFilter, string& strName)
{
	TreeNode tnVar;
	if( fuGetVariable(tnFilter, tnVar) )
	{
		if( tnVar.Name )
		{
			strName = tnVar.Name.strVal;
			return TRUE;
		}
	}
	return FALSE;
}

BOOL fuSetVarName(TreeNode& tnFilter, LPCSTR lpcszName)
{
	TreeNode tnVar;
	if( !fuGetVariable(tnFilter, tnVar) )
		tnVar = tnFilter.AddNode("Variable");
	if( tnVar )
	{
		tnVar.Name.strVal = lpcszName;
		return TRUE;
	}
	return FALSE;
}


int OpenNetcdf(LPCSTR lpcszFile, TreeNode *ptrNCFile) // = NULL
{
	string strFile(lpcszFile);
	if( !strFile.IsFile() )
		return -1; // Error
	
	int ncid = netcdf_open(strFile, ptrNCFile);
	if( ncid < 0 )
		out_int("failed to open, err = ", ncid);

	return ncid;
}

// This function will put the selected netcdf variable into tnFilter,
// This function assume the file is already opened.
/// Hong 06/11/08 v8.0882b FIX_NETCDF_LINKING_ERROR_CAUSE_BY_REMOVE_GETN_NEXT_PREV_DLG
//BOOL netcdf_import_GetVarDialog(TreeNode& tnFilter, BOOL bGet1d, BOOL bFromIW) // = FALSE, FALSE
BOOL netcdf_import_GetVarDialog(TreeNode& tnFilter, BOOL bGet1d) // = FALSE
/// end FIX_NETCDF_LINKING_ERROR_CAUSE_BY_REMOVE_GETN_NEXT_PREV_DLG
{
	Tree trVars;
	netcdf_get_vars_info(&trVars);
	
	// Get all vaiables from tree
	string strVars;
	get_vars_list(trVars, strVars, bGet1d);

	GETN_TREE(tr)
	GETN_BEGIN_BRANCH(Variable, "Variable")
		GETN_LIST(Vars, "Vars", 0, strVars)
	GETN_END_BRANCH(Variable)

	// Get the current selected variable name
	BOOL bRet;
	/// Hong 06/11/08 v8.0882b FIX_NETCDF_LINKING_ERROR_CAUSE_BY_REMOVE_GETN_NEXT_PREV_DLG
	//if( bFromIW )
	//	bRet = GetNPrevNextBox(tr, NULL, GetWindow());	
	//else
	/// end FIX_NETCDF_LINKING_ERROR_CAUSE_BY_REMOVE_GETN_NEXT_PREV_DLG
		bRet = GetNBox(tr, "NETCDF Import", "Select Variable to Import");
	
	if( bRet )
	{
		string strVar;
		strVar = strVars.GetToken(tr.Variable.Vars.nVal, '|');
		ASSERT( !strVar.IsEmpty() ); // Should not empty
		// Get the current selected variable, need remove " (xd)"
		fuSetVarName(tnFilter, strVar.GetToken(0));			
	}
	
	return bRet;
}

// Get the var list from sub dimension node.
BOOL get_vars_xd_list(TreeNode &trVarsXd, string &strVarsXd, LPCSTR lpcszXd)
{
	if( !trVarsXd )
		return FALSE;
	
	strVarsXd.Empty();
	TreeNode trNode = trVarsXd.FirstNode;
	string str;
	
	while( trNode )
	{
		str.Format("%s (%s)|", trNode.tagName, lpcszXd);
		strVarsXd += str;
		trNode = trNode.NextNode;
	}
	
	return TRUE;
}

// This function will get all netcdf variables into a string list formatted like following rules:
// "varname1(1d)|...|varname2(2d)|...|varnamei(3d)|...|varnamen(4d)|"
// Right now, it only support 1d to 4d.
BOOL get_vars_list(TreeNode &trVars, string &strVarsList, BOOL bGet1d = FALSE)
{
	if( !trVars )
		return FALSE;

	strVarsList.Empty();
	string str;
	
	if( bGet1d )
	{
		if( trVars.Var1D )
		{
			get_vars_xd_list(trVars.Var1D, str, "1d");
			strVarsList += str;
		}
	}
	else
	{
		if( trVars.Var2D )
		{
			get_vars_xd_list(trVars.Var2D, str, "2d");
			strVarsList += str;
		}
	
		if( trVars.Var3D )
		{
			get_vars_xd_list(trVars.Var3D, str, "3d");
			strVarsList += str;
		}
	
		if( trVars.Var4D )
		{
			get_vars_xd_list(trVars.Var4D, str, "4d");
			strVarsList += str;
		}
	}	
	return TRUE;
}

int import_netcdf_into_matrix_sheet(MatrixPage &mo, string &strVar)
{
	vector<int> vnDim(NC_MAX_VAR_DIMS);
	vnDim = 0;
	int nVarDims = netcdf_get_var_info(strVar, vnDim, NULL);
	// 1D should read into worksheet
	if( nVarDims < 2 )
	{
		printf("The variable's dimension is %d.\n", nVarDims);
		return -1;
	}
	
	switch(nVarDims)
	{
	case 2:
	case 3:
		if( mo )
		{
			MatrixLayer matLayer = mo.Layers();
			netcdf_xd_mat(strVar, matLayer);
		}
		break;
		
	case 4:
		int nSheets = mo.Layers.Count();
		int ii;
		
		for( ii = nSheets; ii < vnDim[0]; ii++ )
			mo.AddLayer();
		
		for( ii = 0; ii < vnDim[0]; ii++ )
		{
			MatrixLayer matLayer = mo.Layers(ii);
			netcdf_xd_mat(strVar, matLayer, ii);
		}
		break;
		
	default:
		break;
	}
	
	return 0;
}

int import_netcdf_1d_into_wkssheet(Worksheet &wks, TreeNode &trVars)
{
	if( !wks || !trVars )
		return -1; // Error
	
	// Get 1d vaiables
	string strVars;
	get_vars_list(trVars, strVars, TRUE);
	
	int nNumTokens = strVars.GetNumTokens('|');
	string str;
	for( int ii = 0; ii < nNumTokens; ii++ )
	{
		str = strVars.GetToken(ii, '|');
		//str.GetToken(0); // remove " (xd)"
		netcdf_1d_col(str.GetToken(0), wks, ii);
	}
	
	return 0;	
}

int import_netcdf(Page &pgTarget, LPCSTR lpcszFile, TreeNode &trFilter, int nFile)
{
	int nRet = 0;
	Tree trNCInfo;
	if( OpenNetcdf(lpcszFile, &trNCInfo) < 0 )
		return -1; // Error
	
	Tree trVars;
	netcdf_get_vars_info(&trVars);

	string str; // For radio choice index
	int nNumIndex = 0;
	if( trVars.Var1D )
	{
		nNumIndex++;
		str = "Import all 1d variables into Worksheet";
	}
	
	if( trVars.Var2D || trVars.Var3D || trVars.Var4D )
	{
		if( !str.IsEmpty() )
			str += "|";
		
		nNumIndex++;
		str += "Import one 2 to 4d variables into Matrix";
	}

	int nImport = 0;
	if( nNumIndex > 1 )
	{
		GETN_TREE(tr)
		GETN_BEGIN_BRANCH(Import, "The radio of 1d/xd selection from...") 
			GETN_RADIO_INDEX(radio, 0, str)		
		GETN_END_BRANCH(Import)
		
		if( GetNBox(tr, "NETCDF Import", "Select to Import") )
			nImport = tr.Import.radio.nVal;
	}

	str = str.GetToken(nImport, '|');
	if( str.Find("1d") > 0 )
	{
		// Import all 1d variables into Worksheet
		WorksheetPage wp = pgTarget;
		if(!wp)
		{
			wp.Create("Origin");
			wp.Rename(lpcszFile);
			pgTarget = wp;
		}
		Worksheet wks = wp.Layers(0);
		
		nRet = import_netcdf_1d_into_wkssheet(wks, trVars);
	}
	else
	{
		// Import one 2~4d variables into Matrix
		MatrixPage mp(pgTarget);
		if( !mp )
		{
			mp.Create("origin");
			mp.Rename(lpcszFile);
			pgTarget = mp;
		}
		
		if( netcdf_import_GetVarDialog(trFilter) )
		{
			fuGetVarName(trFilter, str);
			nRet = import_netcdf_into_matrix_sheet(mp, str);
		}
	}
	
	// Close file
	netcdf_close();
	
	return nRet;
}

// This function is call by import wizrd, drag and drop
int ImportNETCDF(Page &pgTarget, TreeNode &trFilter, LPCSTR lpcszFile, int nFile)
{
//	if( !pgTarget || EXIST_MATRIX != pgTarget.GetType() )
//		return 1; // target page is required
	
	if( !trFilter || trFilter.Type.nVal != FILTER_TYPE_USERDEFINED )
		return 1; // user defined filter is required
	
	if( !lpcszFile )
		return 1; // source file name is required

	PFNLOADNETCDFINMAT pfn = Project.FindFunction("import_netcdf", "OriginLab\\netcdf_Utils.c", TRUE);
	if( !pfn || pfn(pgTarget, lpcszFile, trFilter, nFile) < 0 )
		return 1;
	
	return 0;
}
