/*------------------------------------------------------------------------------*
 * 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:															*
 *	Cheney 2006-12-15 READ_JCAMP_DATA											*
 *	Cheney 2007-3-7 DATASET_NOW_ONLY_SUPPORT_DOUBLE_TYPE_COLUMN					*
 *	Hong 08/30/07 QA80-9910 FIX_RUNTIME_ERROR_BY_ARRAY_OVERFLOW_ACCESS			*
 *	Hong 07/16/08 v8.0902c FIX_JCAMP_IMPORT_FAIL_AVOID_DUPLICATE_SHEEET_NAME	*
 *	Hong 07/01/09 QA80-13858 FAIL_IMPORT_JCAMP_FILE_WHICH_USE_COMMA_AS_DECIMAL	*
 *	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 <oExtFile.h>
#include "JCAMPFile.h"
#include <..\Originlab\fu_utils.h> ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE

////////////////////////////////////////////////////////////////////////////////////
// Start your functions here.

#define STR_EXCLUDE_CHARACTERS 			"-/\_ "
#define STR_HEADER_TAG_NAME				"Header"
#define STR_DATA_TAG_NAME				"Data"
#define STR_POSITION_TAG_NAME			"Position"
///Cheney 2006-12-15 READ_JCAMP_DATA
#define STR_PAGE_LABEL					"PAGE"

#define STR_DATA_CLASS_ASSIGNMENTS		"ASSIGNMENTS"
#define STR_DATA_CLASS_PEAKASSIGNMENTS	"PEAKASSIGNMENTS"

#define CHAR_SPACE						' '
#define CHAR_TAB						'	'
#define CHAR_PLUS						'+'	
#define CHAR_MINUS						'-'
#define	CHAR_SEMICOLON					';'
#define	CHAR_COMMA						','

#define	STR_EMPTY						""

#define	XDATA_INDEX						0
#define	YDATA_OR_RDATA_INDEX			1
#define IDATA_INDEX						2

#define	MAX_LABEL_SIZE					3

#define TRIM_STRING_LEFT_RIGHT_SPACE(_str) _str.TrimLeft(); _str.TrimRight();

#define ADD_TWO_STR_CONTENTS_INTO_ARRAY(_trInfo1, _trInfo2, _saInfo) \
			_saInfo.Add(_trInfo1? _trInfo1.strVal : STR_EMPTY); \
			_saInfo.Add(_trInfo2? _trInfo2.strVal : STR_EMPTY);

#define ADD_MULTI_STR_CONTENTS_INTO_ARRAY(_nStrings, _strInfos, _saInfo) \
			for(int _ii = 0; _ii < _nStrings; _ii++) \
			{	string _strInfo = _strInfos.GetToken(_ii, CHAR_COMMA); \
				TRIM_STRING_LEFT_RIGHT_SPACE(_strInfo) \
				_saInfo.Add(_strInfo);}

#define ADD_TWO_NEUMERICAL_CONTENTS_INTO_ARRAY(_trInfo1, _trInfo2, _saInfo) \
			_saInfo.Add(_trInfo1? _get_tree_node_numerical_value(_trInfo1) : NANUM); \
			_saInfo.Add(_trInfo2? _get_tree_node_numerical_value(_trInfo2) : NANUM);

#define ADD_MULTI_NEUMERICAL_CONTENTS_INTO_ARRAY(_nStrings, _strInfos, _saInfo) \
			for(int _ii = 0; _ii < _nStrings; _ii++) \
			{	string _strInfo = _strInfos.GetToken(_ii, CHAR_COMMA); \
				TRIM_STRING_LEFT_RIGHT_SPACE(_strInfo) \
				_saInfo.Add(atof(_strInfo));}

				
////////////////////////////////////////////////////////////////////////////////////				

				
enum {
	JCAMP_FILE_END 				= -3,
	JCAMP_INVALID_RECORD 		= -2,
	JCAMP_UNIMPORTANT_RECORD 	= -1,
	JCAMP_IMPORTANT_RECORD 		= 0,
};

enum{
	JCAMP_VAR_XYYY,  /// X+(Y..Y)
	JCAMP_VAR_XYXY,  /// XY..XY
	JCAMP_VAR_XYW,	 /// XYW..XYW 
	JCAMP_VAR_XA,	 /// XA 
	JCAMP_VAR_XYA,	 /// XYA 
	JCAMP_VAR_XYWA,	 /// XYWA 
	JCAMP_VAR_XRRR,	 /// (X+(R..R)) 
	JCAMP_VAR_XIII,	 /// (X+(I..I)) 
};

enum{
	JCAMP_TYPE_COMPRESS,  		/// SQZ, DIF or DUP
	JCAMP_TYPE_NOT_COMPRESS,  	 
	JCAMP_TYPE_PAC  			/// use + and - as seprator
};

enum{
	JCAMP_TYPE_COMPRESS_SQZ,  	
	JCAMP_TYPE_COMPRESS_DIF,  	 
	JCAMP_TYPE_COMPRESS_DUP,  			
};
///end READ_JCAMP_DATA


////////////////////////////////////////////////////////////////////////////////////


static vector<string> gvsLabels = {"TITLE","JCAMPDX", "DATATYPE", "DATACLASS", "APPLICATION",
	"DICTIONARY","BLOCKS", "BLOCKID", "XYDATA", "XYPAIRS", "XYPOINTS", "PEAKTABLE", "PEAKASSIGNMENTS", 
	"DATATABLE", "RADATA", "XUNITS", "YUNITS", "XLABEL", "YLABEL", "XFACTOR", "YFACTOR", "FIRSTX", "LASTX",
    "NPOINTS", "FIRSTY", "MAXX", "MINX", "MAXY", "MINY", "RUNITS", "AUNITS", "FIRSTR", 
    "LASTR", "MAXA", "MINA", "RFACTOR", "AFACTOR", "FIRSTR", "ALIAS", "ZPD", "NTUPLES", 
	"VARNAME", "SYMBOL", "VARTYPE", "VARFORM", "VARDIM", "UNITS", "FIRST", "LAST", "MIN",
    "MAX", "FACTOR", "PAGE", "DELTAX", "CLASS", "ORIGIN", "OWNER", "DATE", "TIME", 
    "SOURCEREFERENCE", "CROSSREFERENCE", "SPECTROMETERDATASYSTEM", "INSTRUMENTPARAMETERS",
	"DESCRIPTION", "CASNAME", "NAMES", "MOLEFORM", "CASREGISTRYNO", "WISWESSER",
	"BEILSTEINLAWSONNO", "MP", "BP", "REFRACTIVEINDEX", "DENSITY", "MW", "CONCENTRATIONS",
	"SAMPLINGPROCEDURE", "STATE", "PATHLENGTH", "PRESSURE", "TEMPERATURE", "DATAPROCESSING" }; 
/// Hong 07/01/09 QA80-13858 FAIL_IMPORT_JCAMP_FILE_WHICH_USE_COMMA_AS_DECIMAL
// Hong, gvbConvert should be consistent with gvsLabels
static vector<BOOL> gvbConvert = {0/*"TITLE"*/,0/*"JCAMPDX"*/, 0/*"DATATYPE"*/, 0/*"DATACLASS"*/, 0/*"APPLICATION"*/,
	0/*"DICTIONARY"*/,0/*"BLOCKS"*/, 0/*"BLOCKID"*/, 0/*"XYDATA"*/, 0/*"XYPAIRS"*/, 0/*"XYPOINTS"*/, 0/*"PEAKTABLE"*/, 0/*"PEAKASSIGNMENTS"*/, 
	0/*"DATATABLE"*/, 0/*"RADATA"*/, 0/*"XUNITS"*/, 0/*"YUNITS"*/, 0/*"XLABEL"*/, 0/*"YLABEL"*/, 0/*"XFACTOR"*/, 0/*"YFACTOR"*/, 1/*"FIRSTX"*/, 1/*"LASTX"*/,
    0/*"NPOINTS"*/, 1/*"FIRSTY"*/, 1/*"MAXX"*/, 1/*"MINX"*/, 1/*"MAXY"*/, 1/*"MINY"*/, 0/*"RUNITS"*/, 0/*"AUNITS"*/, 1/*"FIRSTR"*/, 
    1/*"LASTR"*/, 1/*"MAXA"*/, 1/*"MINA"*/, 0/*"RFACTOR"*/, 0/*"AFACTOR"*/, 1/*"FIRSTR"*/, 0/*"ALIAS"*/, 0/*"ZPD"*/, 0/*"NTUPLES"*/, 
	0/*"VARNAME"*/, 0/*"SYMBOL"*/, 0/*"VARTYPE"*/, 0/*"VARFORM"*/, 0/*"VARDIM"*/, 0/*"UNITS"*/, 1/*"FIRST"*/, 1/*"LAST"*/, 1/*"MIN"*/,
    1/*"MAX"*/, 0/*"FACTOR"*/, 0/*"PAGE"*/, 0/*"DELTAX"*/, 0/*"CLASS"*/, 0/*"ORIGIN"*/, 0/*"OWNER"*/, 0/*"DATE"*/, 0/*"TIME"*/, 
    0/*"SOURCEREFERENCE"*/, 0/*"CROSSREFERENCE"*/, 0/*"SPECTROMETERDATASYSTEM"*/, 0/*"INSTRUMENTPARAMETERS"*/,
	0/*"DESCRIPTION"*/, 0/*"CASNAME"*/, 0/*"NAMES"*/, 0/*"MOLEFORM"*/, 0/*"CASREGISTRYNO"*/, 0/*"WISWESSER"*/,
	0/*"BEILSTEINLAWSONNO"*/, 0/*"MP"*/, 0/*"BP"*/, 0/*"REFRACTIVEINDEX"*/, 0/*"DENSITY"*/, 0/*"MW"*/, 0/*"CONCENTRATIONS"*/,
	0/*"SAMPLINGPROCEDURE"*/, 0/*"STATE"*/, 0/*"PATHLENGTH"*/, 0/*"PRESSURE"*/, 0/*"TEMPERATURE"*/, 0/*"DATAPROCESSING"*/ }; 
/// end FAIL_IMPORT_JCAMP_FILE_WHICH_USE_COMMA_AS_DECIMAL

///Cheney 2006-12-15 READ_JCAMP_DATA	
static vector<string> gvsVarType = {"(X++(Y..Y))","(XY..XY)", "(XYW..XYW)", "(XA)", "(XYA)", 
	"(XYWA)", "(X++(R..R))", "(X++(I..I))"}; 
///end READ_JCAMP_DATA


////////////////////////////////////////////////////////////////////////////////////

//when addtextnode, if node vlaue is "0.00000", its dVal will be NANUM, should get its nVal
static double _get_tree_node_numerical_value(TreeNode& trNode)
{
	double dVal = trNode.dVal;
	return !is_missing_value(dVal)? dVal : trNode.nVal;
}

/////////////////////////////////////////////////////////////////
/////
/////
JCAMPFile::JCAMPFile(LPCTSTR lpszFileName, bool bImport) 
:stdioFile(lpszFileName, file::modeRead | file::typeBinary | file::shareDenyWrite)	
{
	m_nDataIndexStart 	= gvsLabels.Find("XYDATA");
	///Cheney 2006-12-15 READ_JCAMP_DATA
	m_nDataIndexEnd 	= gvsLabels.Find("RADATA");
	m_nMaxRows			= 0;
	m_lpszFileName		= lpszFileName;
	///end READ_JCAMP_DATA
	ASSERT(bImport);  //ot support writing now
	m_bCheckComma = TRUE; /// Hong 07/01/09 QA80-13858 FAIL_IMPORT_JCAMP_FILE_WHICH_USE_COMMA_AS_DECIMAL
}

JCAMPFile::~JCAMPFile()
{
}

BOOL JCAMPFile::Open(LPCTSTR lpszFileName)
{
	if ( Open(lpszFileName, file::modeRead | file::typeBinary | file::shareDenyWrite) )
	{
		return true;
	}
	return false;
}

int JCAMPFile::Import(Worksheet& wks, TreeNode& trInfo, int nC1, DWORD dwOption)
{
	int nRet = ReadHeader(trInfo);
	if(nRet != JCAMP_NO_ERROR)
		return nRet;
	
	Close(); //VC level will open it again
	nRet = ReadData(wks, nC1);
	if(nRet != JCAMP_NO_ERROR && nRet != JCAMP_WARN_Y_VAL_CHECK_NOT_EQUAL)
		return nRet;
	/// Hong 07/16/08 v8.0902c FIX_JCAMP_IMPORT_FAIL_AVOID_DUPLICATE_SHEEET_NAME
	//wks.SetName(GetFileName(m_lpszFileName, TRUE));
	if ( JCAMP_RENAME_SHEET_BY_FILENAME & dwOption )
	{
		wks.SetName(GetFileName(m_lpszFileName, TRUE), OCD_ENUM_NEXT | OCD_ENUM_ADD_SEPARATOR);
	}
	/// end FIX_JCAMP_IMPORT_FAIL_AVOID_DUPLICATE_SHEEET_NAME
	return nRet;
}

int JCAMPFile::ReadHeader(TreeNode& trInfo)
{
	if ( IsOpen() )
	{
		m_trHeader = trInfo.AddNode(STR_HEADER_TAG_NAME);
		m_trData = trInfo.AddNode(STR_DATA_TAG_NAME);
		if ( JCAMP_IMPORTANT_RECORD == getRecord() && m_trHeader.TITLE)   // first line must be ##TITLE=
		{
			while ( getRecord() > JCAMP_FILE_END )
			{
			}
		}
		return JCAMP_NO_ERROR;
	}
	return JCAMP_ERR_INVALID_FILE;
}

int JCAMPFile::getRecord()
{
	string strLine, strName, strValue;
	int nRet;
	///Cheney 2006-12-15 READ_JCAMP_DATA
	bool bFirstRec = true;
	///end READ_JCAMP_DATA
	if ( ReadString(strLine) )
	{
		///Cheney 2006-12-15 READ_JCAMP_DATA
		TRIM_STRING_LEFT_RIGHT_SPACE(strLine) //trim no need space , else can not tell lable
		///end READ_JCAMP_DATA
		char	*pstr, *pstrName;
		int nRight,nPos;
		int nLineLength = strLine.GetLength();
		pstr = strLine.GetBuffer(nLineLength);
		if ( '#' ==*pstr++ && '#'== *pstr++ )
		{
			string strExclude = STR_EXCLUDE_CHARACTERS;
			if ( '.' != *pstr && '$' != *pstr )  // no need check this case now
			{
				pstrName = strName.GetBuffer(nLineLength);
				for ( ; *pstr; pstr++ )
				{
					if ( '=' == *pstr )
					{
						*pstrName = '\0';
						pstr++;
						break;
					}
					if ( strExclude.Find(*pstr) < 0 )
					{
						*pstrName++ = *pstr;							
					}
				}
				strName.ReleaseBuffer();
				
				
//				nRight = strLine.Find('=');
//				strName = strLine.Mid(2, nRight-2);
				strName = strName.SpanExcluding(STR_EXCLUDE_CHARACTERS);
				strName.MakeUpper();
				if ( 0 <= (nPos = gvsLabels.Find(strName)) )
				{
					if ( *pstr )
					{
						/// Hong 07/01/09 QA80-13858 FAIL_IMPORT_JCAMP_FILE_WHICH_USE_COMMA_AS_DECIMAL
						if ( gvbConvert[nPos] )
							jcamp_check_convert_comma_decimal_to_dot(pstr, &m_bCheckComma); 
						/// end FAIL_IMPORT_JCAMP_FILE_WHICH_USE_COMMA_AS_DECIMAL
						strValue = pstr;//strLine.Right(nLineLength - nRight);
					}
					TreeNode trNode;
					///Cheney 2006-12-15 READ_JCAMP_DATA
					TRIM_STRING_LEFT_RIGHT_SPACE(strValue)
					///end READ_JCAMP_DATA
					trNode = m_trHeader.AddTextNode(strValue,strName);
					///Cheney 2006-12-15 READ_JCAMP_DATA
					if(STR_PAGE_LABEL == strName)  //if label is "PAGE", should add its value into m_vPageValue
					{
						string strData = strValue.GetToken(1, '=');
						TRIM_STRING_LEFT_RIGHT_SPACE(strData)
						m_vPageValue.Add(atof(strData));
					}
					//if ( nPos >= m_nDataIndexStart )
					if ( nPos >= m_nDataIndexStart && nPos <= m_nDataIndexEnd)
					///end READ_JCAMP_DATA
					{
						trNode.SetAttribute(STR_POSITION_TAG_NAME, GetPosition());
						m_trData.AddNode(trNode);
						///Cheney 2006-12-15 READ_JCAMP_DATA
						if(nPos == m_nDataIndexEnd - 1) //DATA TABLE, just get var type
						{
							TreeNode trtemp = m_trData.LastNode;
							trtemp.strVal = trtemp.strVal.GetToken( 0, ',' )
						}
						///end READ_JCAMP_DATA
					}
					nRet =  JCAMP_IMPORTANT_RECORD;
				}
			}
			else
				nRet = JCAMP_UNIMPORTANT_RECORD;
		}
		else
			nRet = JCAMP_INVALID_RECORD;
		strLine.ReleaseBuffer();
	}
	else
		nRet = JCAMP_FILE_END;
	return nRet;	
}

///Cheney 2006-12-15 READ_JCAMP_DATA
//int JCAMPFile::ReadData()
int JCAMPFile::ReadData(Worksheet& wks, int nC1, DWORD dwOption)
///end READ_JCAMP_DATA
{
	//set wks name by data title
	if(m_trData.TITLE)
		wks.SetName(m_trData.TITLE.strVal);
	
	TreeNode trDataClass = m_trHeader.DATACLASS;
	if(trDataClass)
	{
		string strDataClass = trDataClass.strVal;
		strDataClass.Replace(" ", "");
		if(strDataClass == STR_DATA_CLASS_ASSIGNMENTS || strDataClass == STR_DATA_CLASS_PEAKASSIGNMENTS) //should support if we have enough testing file
			return JCAMP_ERR_UNSUPPORT_DATA;
	}
		
	int nDataPos = -1;
	string strLine;
	int nColIndex = nC1;
	m_b3dData = checkIf3dData();
	m_bMultiFraction = checkIfMultiFraction();
	
	///Cheney 2006-12-15 READ_JCAMP_DATA
	prepareTreeInfo();
	///end READ_JCAMP_DATA
	int nPageIndex = 0;	
	bool bYValueCHKEqual = true;
	foreach (TreeNode trNode in m_trData.Children )
	{
		trNode.GetAttribute(STR_POSITION_TAG_NAME, nDataPos); 
		///Cheney 2006-12-15 READ_JCAMP_DATA	
		m_nCurrentVarType = getDataVarType(trNode);
		if(m_nCurrentVarType == JCAMP_ERR_INVALID_VAR_TYPR || m_nCurrentVarType == JCAMP_ERR_UNSUPPORT_DATA)
			return m_nCurrentVarType;
		
		double dDeltaX = 0;
		if(m_nCurrentVarType == JCAMP_VAR_XYYY || m_nCurrentVarType == JCAMP_VAR_XRRR || m_nCurrentVarType == JCAMP_VAR_XIII)
		{
			m_nCurrentDataType = jcamp_get_data_type(m_lpszFileName, nDataPos);
			if(m_nCurrentDataType == JCAMP_ERR_INVALID_DATA_TYPR)
				return JCAMP_ERR_INVALID_DATA_TYPR;
				
			dDeltaX = getDeltaX(m_trHeader, nDataPos);
		}
		
		//if wks has not enough col, should add it
		int nAddColumns = getAddColumnsNum(m_nCurrentVarType);
		for(int nLoop = wks.GetNumCols(); nLoop < nColIndex + nAddColumns; nLoop = wks.GetNumCols() ) 
			wks.AddCol();
		
		//if NPOINTS not exists, no way to set dataset's size, so set its size as 0, then add each number
		int nColx = m_nCurrentVarType==JCAMP_VAR_XIII? nColIndex-2: nColIndex;
		int nColy = m_nCurrentVarType==JCAMP_VAR_XIII? nColIndex: nColIndex+1;
		///Cheney 2007-3-7 DATASET_NOW_ONLY_SUPPORT_DOUBLE_TYPE_COLUMN
		wks.Columns(nColx).SetFormat(OKCOLTYPE_NUMERIC);
		wks.Columns(nColy).SetFormat(OKCOLTYPE_NUMERIC);
		wks.Columns(nColx).SetInternalDataType(FSI_DOUBLE);
		wks.Columns(nColy).SetInternalDataType(FSI_DOUBLE);
		///end DATASET_NOW_ONLY_SUPPORT_DOUBLE_TYPE_COLUMN
		Dataset dsx(wks.Columns(nColx));
		Dataset dsy(wks.Columns(nColy));
	
		string strXColRange, strYColRange;
		make_column_range_string(strXColRange, wks.Columns(nColx));
		make_column_range_string(strYColRange, wks.Columns(nColy));

		//read data
		int nArraySize = 0;
		int nRet = jcamp_read_data(m_lpszFileName, nDataPos, m_nCurrentVarType, m_nCurrentDataType, strXColRange, strYColRange, dDeltaX, &nArraySize);
		if(nRet != JCAMP_NO_ERROR && nRet != JCAMP_WARN_Y_VAL_CHECK_NOT_EQUAL)
			return nRet;
		
		if(nRet == JCAMP_WARN_Y_VAL_CHECK_NOT_EQUAL) //should show warning message in XF
			bYValueCHKEqual = false;
		
		dsx.SetSize(nArraySize); //if not set size, will wrong when replace data
		dsy.SetSize(nArraySize);
		if(m_bHasFactors)
			handelFactor(dsx, dsy);
		
		handleUserParameters(m_trHeader, wks.Columns(nColx), wks.Columns(nColy), nPageIndex);
		handleUserInfo(m_trHeader, wks.Columns(nColx), wks.Columns(nColy), dDeltaX, m_nCurrentVarType==JCAMP_VAR_XIII);
		nColIndex += nAddColumns;
		m_nMaxRows = nArraySize > m_nMaxRows? nArraySize : m_nMaxRows; 
		nPageIndex++;
		//ReadString(strLine);
		///end READ_JCAMP_DATA
	}
	
	//wks.SetSize(-1, nColIndex);
	return !bYValueCHKEqual? JCAMP_WARN_Y_VAL_CHECK_NOT_EQUAL : JCAMP_NO_ERROR;
}

///Cheney 2006-12-15 READ_JCAMP_DATA
//check data var type with gvsVarType
int JCAMPFile::getDataVarType(TreeNode& trData)
{
	int nVarType = JCAMP_ERR_INVALID_VAR_TYPR;
	
	string strVarType = trData.strVal;
	strVarType.Replace(" ", "");
	
	if(trData.tagName == STR_DATA_CLASS_ASSIGNMENTS || trData.tagName == STR_DATA_CLASS_PEAKASSIGNMENTS) //should support if we have enough testing file
		return JCAMP_ERR_UNSUPPORT_DATA;

	int nPos = gvsVarType.Find(strVarType);
	if(nPos >= 0 && nPos < gvsVarType.GetSize())
		nVarType = nPos;
	
	return nVarType;
}

//for JCAMP_VAR_XYYY, JCAMP_VAR_XRRR, JCAMP_VAR_XIII
double JCAMPFile::getDeltaX(TreeNode& trHeader, int nPosition)
{
	///Cheney 2007-1-4 DELTAX_MAYBE_NOT_EQUAL_TO_X_STEP, according doc, should use (lastx - firstx)/(n-1) if could
	//if(trHeader.DELTAX)
		//return trHeader.DELTAX.dVal = trHeader.XFACTOR? trHeader.DELTAX.dVal/trHeader.XFACTOR.dVal : trHeader.XFACTOR.dVal;
	double dDeltaX;
	if(trHeader.NPOINTS && trHeader.LASTX && trHeader.FIRSTX)
	{
		dDeltaX = ( _get_tree_node_numerical_value(trHeader.LASTX) - _get_tree_node_numerical_value(trHeader.FIRSTX) )
						/ ( _get_tree_node_numerical_value(trHeader.NPOINTS) - 1.);
		
		if(trHeader.XFACTOR)
			dDeltaX /= _get_tree_node_numerical_value(trHeader.XFACTOR);
	}
	///end DELTAX_MAYBE_NOT_EQUAL_TO_X_STEP
	else
		dDeltaX = jcamp_get_deltaX(m_lpszFileName, nPosition, m_nCurrentDataType);
	
	return dDeltaX;
}

void JCAMPFile::handleUserParameters(TreeNode& trHeader, Column& colx, Column& coly, int nPageIndex)
{
	if(m_bHasLabels)
	{
		colx.SetLongName(m_saLabels[XDATA_INDEX]);
		coly.SetLongName(m_nCurrentVarType==JCAMP_VAR_XIII? m_saLabels[IDATA_INDEX] : m_saLabels[YDATA_OR_RDATA_INDEX]);
	}	
	if(m_bHasUnits)
	{
		colx.SetUnits(m_saUnits[XDATA_INDEX]);
		coly.SetUnits(m_nCurrentVarType==JCAMP_VAR_XIII? m_saUnits[IDATA_INDEX] : m_saUnits[YDATA_OR_RDATA_INDEX]);	
	}	
	if(m_b3dData)
	{
		if(m_bHasFactors)
			m_vPageValue[nPageIndex] *= m_vFactors[MAX_LABEL_SIZE - 1];
		colx.SetParameter(ftoa(m_vPageValue[nPageIndex])); 
		coly.SetParameter(ftoa(m_vPageValue[nPageIndex]));
	}	
	colx.SetType(OKDATAOBJ_DESIGNATION_X);
	coly.SetType(OKDATAOBJ_DESIGNATION_Y);
	
	//set user parameter
	vector<string> vsNames, vsValues;
	//if 3D data, should add its label and units in m_trHeader
	if(m_b3dData)
	{
		vsNames.Add("Page Label");
		vsNames.Add("Page Unit");
		vsValues.Add(m_bHasLabels? m_saLabels[MAX_LABEL_SIZE-1] : STR_EMPTY);
		vsValues.Add(m_bHasUnits? m_saUnits[MAX_LABEL_SIZE-1] : STR_EMPTY);
	}
	set_user_parameters(colx, vsNames, vsValues);
	if(m_bMultiFraction)
	{
		vsNames.Add("Fraction Index");
		vsValues.Add(ftoa(m_vPageValue[nPageIndex]));
	}
	set_user_parameters(coly, vsNames, vsValues);
}

void JCAMPFile::handleUserInfo(TreeNode& trHeader, Column& colx, Column& coly, double dDeltaX, bool bYColVarTypeXIII)
{
	string strColumnInfo = "XColumnInfo";
	//set X column info
	if(!bYColVarTypeXIII)
	{
		Tree trXColumnInfo;
		trXColumnInfo.AddTextNode(m_lpszFileName, "ImportFile");
		double dxFactor = 1.;
		if(m_bHasFactors)
		{
			dxFactor = is_missing_value(m_vFactors[XDATA_INDEX])? 1. : m_vFactors[XDATA_INDEX];
			trXColumnInfo.AddTextNode(ftoa(dxFactor), "XFactor");
			if(m_b3dData)
				/// Hong 08/30/07 QA80-9910 FIX_RUNTIME_ERROR_BY_ARRAY_OVERFLOW_ACCESS
				//trXColumnInfo.AddTextNode(ftoa(is_missing_value(m_vFactors[m_vFactors.GetSize()])? 1. : m_vFactors[m_vFactors.GetSize()]), "ZFactor");
				{
					int nIndex = m_vFactors.GetSize() - 1;
					float fVal = is_missing_value(m_vFactors[nIndex]) ? 1. : m_vFactors[nIndex];
					trXColumnInfo.AddTextNode(ftoa(fVal), "ZFactor");
				}
				/// end FIX_RUNTIME_ERROR_BY_ARRAY_OVERFLOW_ACCESS

		}
		if( m_nCurrentVarType == JCAMP_VAR_XYYY || m_nCurrentVarType == JCAMP_VAR_XRRR || m_nCurrentVarType == JCAMP_VAR_XIII)
			trXColumnInfo.AddTextNode(ftoa(dDeltaX*dxFactor), "XDelta");
		trXColumnInfo.SetAttribute(STR_ATTRIB_BRANCH, GETNBRANCH_OPEN);
		trXColumnInfo.Enable = ENABLE_READ_ONLY;
		///---Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
		//trXColumnInfo.Show = 0; ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
		//bool bRet = set_user_info(colx, strColumnInfo, trXColumnInfo);
		bool bRet = fu_set_import_file_info(colx, trXColumnInfo, strColumnInfo, 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
		//fu_set_import_file_name_info(colx, m_lpszFileName);
	//
		//trXColumnInfo.RemoveChild("ImportFile");
		//trXColumnInfo.Show = 1;
		//set_import_file_info(colx, trXColumnInfo, "JCAMPXColumnInfo");
		///---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
	}
	//set Y column info
	if(m_bHasFactors)
	{
		strColumnInfo = "YColumnInfo";
		Tree trYColumnInfo;
		trYColumnInfo.AddTextNode(m_lpszFileName, "ImportFile");
		int nyFactorIndex = m_nCurrentVarType == JCAMP_VAR_XIII? IDATA_INDEX : YDATA_OR_RDATA_INDEX;
		trYColumnInfo.AddTextNode(ftoa(is_missing_value(m_vFactors[nyFactorIndex])? 1. : m_vFactors[nyFactorIndex]), "YFactor");
		if(m_b3dData)
			/// Hong 08/30/07 QA80-9910 FIX_RUNTIME_ERROR_BY_ARRAY_OVERFLOW_ACCESS
			//trYColumnInfo.AddTextNode(ftoa(is_missing_value(m_vFactors[m_vFactors.GetSize()])? 1. : m_vFactors[m_vFactors.GetSize()]), "ZFactor");
			{
					int nIndex = m_vFactors.GetSize() - 1;
					float fVal = is_missing_value(m_vFactors[nIndex]) ? 1. : m_vFactors[nIndex];
					trYColumnInfo.AddTextNode(ftoa(fVal), "ZFactor");
			}
			/// end /// Hong 08/30/07 QA80-9910 FIX_RUNTIME_ERROR_BY_ARRAY_OVERFLOW_ACCESS
		trYColumnInfo.SetAttribute(STR_ATTRIB_BRANCH, GETNBRANCH_OPEN);
		trYColumnInfo.Enable = ENABLE_READ_ONLY;
		///---Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
		//trYColumnInfo.Show = 0; ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
		//bool bRet = set_user_info(coly, strColumnInfo, trYColumnInfo);
		bool bRet = fu_set_import_file_info(coly, trYColumnInfo, strColumnInfo, 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
		//fu_set_import_file_name_info(coly, m_lpszFileName);
	//
		//trYColumnInfo.RemoveChild("ImportFile");
		//trYColumnInfo.Show = 1;
		//set_import_file_info(coly, trYColumnInfo, "JCAMPYColumnInfo");
		///---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
	}
}

//if JCAMP_VAR_XIII, add only 1 colums, because it will use same X column with real part data
int JCAMPFile::getAddColumnsNum(int nVarType)
{
	int nAddColumns = 0;
	switch(nVarType)
	{
	case JCAMP_VAR_XIII:
		nAddColumns = 1;
		break;
	case JCAMP_VAR_XYYY:
	case JCAMP_VAR_XYXY:
	case JCAMP_VAR_XRRR:
	case JCAMP_VAR_XA:  //maybe JCAMP_VAR_XA is wrong, need more data to check it
		nAddColumns = 2;
		break;
	case JCAMP_VAR_XYW: //maybe those 3 types below are wrong, need more data to check it
	case JCAMP_VAR_XYA:
		nAddColumns = 3;
		break;
	case JCAMP_VAR_XYWA:
		nAddColumns = 4;
		break;
	default:
	}
	return nAddColumns;
}

//if multi bloks, like vartype: maybe will be "string1, string2, string3, string4", e.g
//string1 is X's label, string2 is R's label, string3 is I's label
//should put them in the vector
void JCAMPFile::prepareTreeInfo()
{
	m_bHasLabels = prepareLabels();
	m_bHasUnits = prepareUnits();
	m_bHasFactors = prepareFactors();
}

bool JCAMPFile::prepareLabels()
{
	if(m_trHeader.XLABEL || m_trHeader.YLABEL)
	{
		ADD_TWO_STR_CONTENTS_INTO_ARRAY(m_trHeader.XLABEL, m_trHeader.YLABEL, m_saLabels)
		return true;
	}
	else if(m_trHeader.VARNAME)
	{
		string strVarName = m_trHeader.VARNAME.strVal;
		int nStrings = strVarName.GetNumTokens(CHAR_COMMA);
		nStrings = nStrings > MAX_LABEL_SIZE ? MAX_LABEL_SIZE: nStrings;//the 4th value is page's, no need
		ADD_MULTI_STR_CONTENTS_INTO_ARRAY(nStrings, strVarName, m_saLabels)
		
		//set to orgnizer
		m_trHeader.AddTextNode(m_saLabels[XDATA_INDEX], "XLABEL");
		
		string strName = m_b3dData? "YLABEL" : "RLABEL";
		m_trHeader.AddTextNode(m_saLabels[YDATA_OR_RDATA_INDEX], strName);
		strName = m_b3dData? "ZLABEL" : "ILABEL";
		m_trHeader.AddTextNode(m_saLabels[IDATA_INDEX], strName);
		return true;
	}
	return false;
}

bool JCAMPFile::prepareUnits()
{
	if(m_trHeader.XUNITS || m_trHeader.YUNITS)
	{
		ADD_TWO_STR_CONTENTS_INTO_ARRAY(m_trHeader.XUNITS, m_trHeader.YUNITS, m_saUnits)
		return true;
	}
	else if(m_trHeader.RUNITS || m_trHeader.AUNITS)
	{
		ADD_TWO_STR_CONTENTS_INTO_ARRAY(m_trHeader.RUNITS, m_trHeader.AUNITS, m_saUnits)
		return true;
	}
	else if(m_trHeader.UNITS)
	{
		string strUnits = m_trHeader.UNITS.strVal;
		int nStrings = strUnits.GetNumTokens(CHAR_COMMA);
		nStrings = nStrings > MAX_LABEL_SIZE ? MAX_LABEL_SIZE: nStrings;//the 4th value is page's, no need
		ADD_MULTI_STR_CONTENTS_INTO_ARRAY(nStrings, strUnits, m_saUnits)
		
		//set to orgnizer
		m_trHeader.AddTextNode(m_saUnits[XDATA_INDEX], "XUNIT");
		
		string strName = m_b3dData? "YUNIT" : "RUNIT";
		m_trHeader.AddTextNode(m_saUnits[YDATA_OR_RDATA_INDEX], strName);
		strName = m_b3dData? "ZUNIT" : "IUNIT";
		m_trHeader.AddTextNode(m_saUnits[IDATA_INDEX], strName);
		return true;
	}
	return false;
}

bool JCAMPFile::prepareFactors()
{
	if(m_trHeader.XFACTOR || m_trHeader.YFACTOR)
	{
		ADD_TWO_NEUMERICAL_CONTENTS_INTO_ARRAY(m_trHeader.XFACTOR, m_trHeader.YFACTOR, m_vFactors)
		return true;
	}
	else if(m_trHeader.RFACTOR || m_trHeader.AFACTOR)
	{
		ADD_TWO_NEUMERICAL_CONTENTS_INTO_ARRAY(m_trHeader.RFACTOR, m_trHeader.AFACTOR, m_vFactors)
		return true;
	}
	else if(m_trHeader.FACTOR)
	{
		string strFactors = m_trHeader.FACTOR.strVal;
		int nStrings = strFactors.GetNumTokens(CHAR_COMMA);
		ADD_MULTI_NEUMERICAL_CONTENTS_INTO_ARRAY(nStrings, strFactors, m_vFactors)
		
		//set to orgnizer
		m_trHeader.AddTextNode(ftoa(m_vFactors[XDATA_INDEX]), "XFACTOR");
		
		string strName = m_b3dData? "YFACTOR" : "RFACTOR";
		m_trHeader.AddTextNode(ftoa(m_vFactors[YDATA_OR_RDATA_INDEX]), strName);
		strName = m_b3dData? "ZFACTOR" : "IFACTOR";
		m_trHeader.AddTextNode(ftoa(m_vFactors[IDATA_INDEX]), strName);
		return true;
	}
	return false;
}

void JCAMPFile::handelFactor(vector& vx, vector& vy)
{
	if(m_nCurrentVarType == JCAMP_VAR_XIII)
		vy *= is_missing_value(m_vFactors[IDATA_INDEX])? 1. : m_vFactors[IDATA_INDEX];
	else
		vy *= is_missing_value(m_vFactors[YDATA_OR_RDATA_INDEX])? 1. : m_vFactors[YDATA_OR_RDATA_INDEX];
	
	vx *= is_missing_value(m_vFactors[XDATA_INDEX])? 1. : m_vFactors[XDATA_INDEX];
}

bool JCAMPFile::checkIf3dData()
{
	string strVarType = m_trData.FirstNode.strVal;
	strVarType.Replace(" ", "");
	int nPos = gvsVarType.Find(strVarType);
	
	if(m_vPageValue.GetSize() > 0 && nPos < 2) //(X+Y..Y), (XY..XY)
		return true;
	else
		return false;
}

bool JCAMPFile::checkIfMultiFraction()
{
	string strVarType = m_trData.FirstNode.strVal;
	strVarType.Replace(" ", "");
	int nPos = gvsVarType.Find(strVarType);
	
	if(m_vPageValue.GetSize() > 0 && nPos == gvsVarType.GetSize() - 2) //(X++(R..R))
		return true;
	else
		return false;
}
///end READ_JCAMP_DATA