/*------------------------------------------------------------------------------*
 * File Name:				 													*
 * Creation: 																	*
 * Purpose: OriginC Header File For EDF file									*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	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			*
 *	Sophy 6/1/2010 BETTER_METHOD_FOR_MAPPING_DIGIT_TO_PHYSIC_VALUE				*
 *	Sophy 6/4/2010 ORG-152-S8 DISPLAY_FILE_INFO_AS_MULTILINE_TEXT_CONTROL		*
 *	Sophy 6/4/2010 ORG-152-P6 SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY				*
 *------------------------------------------------------------------------------*/
 
#include <Origin.h>
#include <GetNBox.h>

//Constants
#define	FIXED_HEADER_SIZE		256
#define	EXTRA_DATASET_SIZE		256
#define	EDF_MAX_SIGNALS			256
#define	PRINTABLE_LOWER			32
#define	PRINTABLE_UPPER			126
#define	NUMBER_LOWER			48
#define	NUMBER_UPPER			57
#define	EDF_INFO_BUFFER_SIZE	128
#define	EDF_LABEL_SIZE			16
#define	EDF_TRANSTYPE_SIZE		80
#define	EDF_PHYDIM_SIZE			8
#define	EDF_PHYMIN_SIZE			8
#define	EDF_PHYMAX_SIZE			8
#define	EDF_DIGMIN_SIZE			8
#define	EDF_DIGMAX_SIZE			8
#define	EDF_PREFILTER_SIZE		80
#define	EDF_NUM_SAMPLE_SIZE		8
#define	EDF_RESERVED_SIZE		32

#define	MIN_DIGMIN_EDF_PLUS		(-32768)
#define	MAX_DIGMAX_EDF_PLUS		(32767)
#define	MIN_DIGMIN_BDF_PLUS		(-8388608)
#define	MAX_DIGMAX_BDF_PLUS		(8388607)

#define	RESERVED_INFO_LENGTH	5
#define	STR_EDF_VERSION_CHECK	"0       "
#define	STR_BDF_VERSION_TAG		"BIOSEMI"
#define	STR_EDF_CONTINUOUS		"EDF+C"
#define	STR_EDF_DISCONTINUOUS	"EDF+D"
#define	STR_BDF_CONTINUOUS		"BDF+C"
#define	STR_BDF_DISCONTINUOUS	"BDF+D"
#define	STR_EDF_ANNOTATIONS		"EDF Annotations "
#define	STR_BDF_ANNOTATIONS		"BDF Annotations "
#define	STR_UNKNOWN_FIELD		"X"	
#define	CHAR_INFO_FIELD_SEP		' '
#define	CHAR_TAL_TIMESTAMP_SEP	21
#define	CHAR_TAL_ANNOTATION_SEP	20
#define	STR_TAL_END				0x0014	//end of TAL, with 20 followed by 0

#define	STR_TIMESTAMP			"TimeStamp:"
#define	STR_DURATION			"Duration:"
#define	STR_ANNOTATION_LIST		"Annotation List:"
//file types
enum {
	EDF_FILETYPE_EDF = 0,
	EDF_FILETYPE_EDF_PLUS = 1,
	EDF_FILETYPE_BDF = 2,		//will support the following to type later.
	EDF_FILETYPE_BDF_PLUS = 3,
};

#define	EDF_BDF_MASK		0x0002
#define	EDF_BDF_PLUS_MASK	0x0001
#define	EDF_SAMPLE_SIZE		2
#define	BDF_SAMPLE_SIZE		3
//keep aligned by 1 byte
#pragma pack(push, 1)
//structure acoording to EDF specification
typedef	struct tagEDF_HEADER {
	char	cVersionInfo[8];		//offset 0
	char	cPatientInfo[80];		//offset 8
	char	cLocalRecordInfo[80];	//offset 88
	char	cStartDate[8];			//offset 168
	char	cStartTime[8];			//offset 176
	char	cHeaderSize[8];			//offset 184, number of bytes in header record
	char	cReserved[44];			//offset 192, reserved
	char	cNumData[8];			//offset 236, number of data records(-1 if unknown, obey item 10 of EDF+ specs)
	char	cDuration[8];			//offset 244, duration of data record, in seconds
	char	cNumSignals[4];			//offset 252, number of signals(ns) in data record
	char*	pcLabels;				//offset 256
	char*	pcTransducerTypes;
	char*	pcPhysicalDims;
	char*	pcPhysicalMinimums;
	char*	pcPhysicalMaximums;
	char*	pcDigitalMinimums;
	char*	pcDigitalMaximums;
	char*	pcPrefilterings;
	char*	pcSamplesEachData;
	char*	pcReserved;
	
}EDFHEADER, *pEDFHEADER;

typedef struct tagOTime {
	int nYear;
	int nMonth;
	int nDay;
	int nHour;
	int nMinute;
	int nSecond;
	int nMilSecond;
}OTime, *pOTime;

//useful information for OC directly access
typedef	struct tagOHeader {
	string	strVersion;
	DWORD	dwFileType;
	DWORD	dwContinuous;
	vector<string>	vsPatientInfo; //
	int		nStartDay;
	int		nStartMonth;
	int		nStartYear;
	int		nStartSecond;
	int		nStartMinute;
	int		nStartHour;
	string	strRecordInfo;
	string	strPatient;
	double	dDuration;
	int		nNumData;
	int		nNumSignals;
	int		nHeaderSize;
	vector<string>	vsLabels;
	vector<int>		vnAnnotations; //flag to indicate whether it is annotation
	vector<string>	vsTransducerTypes;
	vector<string>	vsPhyDims;
	vector<double>	vdPhyMin;
	vector<double>	vdPhyMax;
	vector<int>		vnDigMin;
	vector<int>		vnDigMax;
	vector<string>	vsPrefilters;
	vector<int>		vnNumSamplesEachData;
	int				iFirstAnnotSignal; //first "EDF Annotations" signal
	int				iMaxSamplesSignal; //index of the signal that has most samples in data record
	int				nRecordSize;
	vector<string>	vsReserved;
	
}OHeader, *pOHeader;

#pragma pack(pop)

static void _init_edf_header_info(pEDFHEADER pHeader)
{
	if ( !pHeader )
		return;
	
	pHeader->pcLabels = NULL;
	pHeader->pcTransducerTypes = NULL;
	pHeader->pcPhysicalDims = NULL;
	pHeader->pcPhysicalMinimums = NULL;
	pHeader->pcPhysicalMaximums = NULL;
	pHeader->pcDigitalMinimums = NULL;
	pHeader->pcDigitalMaximums = NULL;
	pHeader->pcSamplesEachData = NULL;
	pHeader->pcReserved = NULL;
}

static void _allocate_header_memory(pEDFHEADER pHeader, int nNumSignals)
{
	if ( !pHeader )
		return;
	
	pHeader->pcLabels = (char*)malloc(EDF_LABEL_SIZE * nNumSignals);
	pHeader->pcTransducerTypes = (char*)malloc(EDF_TRANSTYPE_SIZE * nNumSignals);
	pHeader->pcPhysicalDims = (char*)malloc(EDF_PHYDIM_SIZE * nNumSignals);
	pHeader->pcPhysicalMinimums = (char*)malloc(EDF_PHYMIN_SIZE * nNumSignals);
	pHeader->pcPhysicalMaximums = (char*)malloc(EDF_PHYMAX_SIZE * nNumSignals);
	pHeader->pcDigitalMinimums = (char*)malloc(EDF_DIGMIN_SIZE * nNumSignals);
	pHeader->pcDigitalMaximums = (char*)malloc(EDF_DIGMAX_SIZE * nNumSignals);
	pHeader->pcPrefilterings = (char*)malloc(EDF_PREFILTER_SIZE * nNumSignals);
	pHeader->pcSamplesEachData = (char*)malloc(EDF_NUM_SAMPLE_SIZE * nNumSignals);
	pHeader->pcReserved = (char*)malloc(EDF_RESERVED_SIZE * nNumSignals);
	
}

static void _free_edf_header_info(pEDFHEADER pHeader)
{
	if ( !pHeader )
		return;
	
	free(pHeader->pcLabels);
	free(pHeader->pcTransducerTypes);
	free(pHeader->pcPhysicalDims);
	free(pHeader->pcPhysicalMinimums);
	free(pHeader->pcPhysicalMaximums);
	free(pHeader->pcDigitalMinimums);
	free(pHeader->pcDigitalMaximums);
	free(pHeader->pcSamplesEachData);
	free(pHeader->pcReserved);
}

//EDF file operation error code
#define	MINIMUN_ERROR_CODE	(-30)
enum {
	EDF_SUCCESS = 0,
	EDF_UNKNOWN_ERROR = MINIMUN_ERROR_CODE,
	EDF_USERABORT,
	EDF_FORMAT_ERROR,
	EDF_INVALID_CHARACTER_FOUND,
	EDF_FILE_NOT_OPEN,
	EDF_FILE_NOT_EXIST,
	EDF_INVALID_FILE,
	EDF_INVALID_DATE_TIME,
	EDF_SIGNALS_NUMBER_EXCEED_LIMIT,
	EDF_NO_TIMESTAMP,
	EDF_INVALID_WORKSHEET,
	EDF_NO_SIGNAL_SELECTED,
	EDF_NO_SIGNAL_INFO,
	EDF_NO_FILE_INFO,
};

//utils BEGIN
#define	STR_FILENAME_L			_L("File Name:")
#define	STR_VERSION_L			_L("Version:")
#define	STR_FILETYPE_L			_L("File Type:")
#define	STR_RECORD_STARTTIME_L	_L("Record Start Time:")
#define	STR_RECORD_ENDTIME_L	_L("Record End Time:")
#define	STR_PATIENT_INFO_L		_L("Patient Information:")
#define	STR_NUMBER_OF_RECORD_L	_L("Number of Data Records:")
#define	STR_NUMBER_OF_SIGNAL_L	_L("Number of Signals:")
#define	STR_RECORD_DETAILS_L	_L("Record Information:")
#define	STR_RECORD_CONTINUOUS_L	_L("Record Continuous:")
#define	STR_SIGNALS_L			_L("Signals")
#define	STR_SIGNAL_I_FMT		_L("Signal %d")
#define	STR_SIGNAL_DETAILS_L	_L("Signal Details")
#define	STR_EDF_FILE_INFO		_L("EDF File Information")

static string _get_file_type_name(DWORD dwType)
{
	if ( dwType < EDF_FILETYPE_EDF || dwType > EDF_FILETYPE_BDF_PLUS )
		return "Unknown";
	vector<string> vs = {
		"EDF", "EDF+", "BDF", "BDF+"
	};
	return vs[dwType];
}
static bool _construct_update_edf_info(LPCSTR lpcszFileName, TreeNode& trInfo, pOHeader pHeader, BOOL bSelectable)
{
	if ( !trInfo || !pHeader )
		return false;
	
	OTime oStart, oEnd;
	oStart.nYear = pHeader->nStartYear;
	oStart.nMonth = pHeader->nStartMonth;
	oStart.nDay = pHeader->nStartDay;
	oStart.nHour = pHeader->nStartHour;
	oStart.nMinute = pHeader->nStartMinute;
	oStart.nSecond = pHeader->nStartSecond;
	oStart.nMilSecond = 0;	
	double dSec = pHeader->dDuration * pHeader->nNumData;
	_update_date_time(&oStart, &oEnd, dSec * 1000, true);
	GETN_USE(trInfo)
		///Sophy 6/4/2010 ORG-152-S8 DISPLAY_FILE_INFO_AS_MULTILINE_TEXT_CONTROL
		//GETN_STR(FileName, STR_FILENAME_L, lpcszFileName) GETN_READ_ONLY_EX(2)
		//GETN_STR(Version, STR_VERSION_L, pHeader->strVersion) GETN_READ_ONLY_EX(2)
		//GETN_STR(FileType, STR_FILETYPE_L, _get_file_type_name(pHeader->dwFileType)) GETN_READ_ONLY_EX(2)
		//GETN_STR(RecordDetails, STR_RECORD_DETAILS_L, pHeader->strRecordInfo) GETN_READ_ONLY_EX(2)
		//GETN_STR(PatientInfo, STR_PATIENT_INFO_L, pHeader->strPatient) GETN_READ_ONLY_EX(2)
		//GETN_STR(RecordStart, STR_RECORD_STARTTIME_L, _get_date_time_str(oStart.nYear, oStart.nMonth, oStart.nDay, oStart.nHour, oStart.nMinute, oStart.nSecond, 0)) GETN_READ_ONLY_EX(2)
		//GETN_STR(RecordEnd, STR_RECORD_ENDTIME_L, _get_date_time_str(oEnd.nYear, oEnd.nMonth, oEnd.nDay, oEnd.nHour, oEnd.nMinute, oEnd.nSecond, 0)) GETN_READ_ONLY_EX(2)
		//GETN_NUM(NumRecords, STR_NUMBER_OF_RECORD_L, pHeader->nNumData) GETN_READ_ONLY_EX(2)
		//GETN_NUM(NumSignals, STR_NUMBER_OF_SIGNAL_L, pHeader->nNumSignals) GETN_READ_ONLY_EX(2)
		//GETN_STR(IsContinuous, STR_RECORD_CONTINUOUS_L, pHeader->dwContinuous ? _L("Continuous") : _L("Discontinuous")) GETN_READ_ONLY_EX(2)
		string strHdr = "";
		strHdr += STR_FILENAME_L + "\t" + lpcszFileName + "\r\n";
		strHdr += STR_VERSION_L + "\t" + pHeader->strVersion + "\r\n";
		strHdr += STR_FILETYPE_L + "\t" + _get_file_type_name(pHeader->dwFileType) + "\r\n";
		strHdr += STR_RECORD_DETAILS_L + "\t" + pHeader->strRecordInfo + "\r\n";
		strHdr += STR_PATIENT_INFO_L + "\t" + pHeader->strPatient + "\r\n";
		strHdr += STR_RECORD_STARTTIME_L + "\t" + _get_date_time_str(oStart.nYear, oStart.nMonth, oStart.nDay, oStart.nHour, oStart.nMinute, oStart.nSecond, oStart.nMilSecond) + "\r\n";
		strHdr += STR_RECORD_ENDTIME_L + "\t" + _get_date_time_str(oEnd.nYear, oEnd.nMonth, oEnd.nDay, oEnd.nHour, oEnd.nMinute, oEnd.nSecond, oEnd.nMilSecond) + "\r\n";
		strHdr += STR_NUMBER_OF_RECORD_L + "\t" + pHeader->nNumData + "\r\n";
		strHdr += STR_NUMBER_OF_SIGNAL_L + "\t" + pHeader->nNumSignals + "\r\n";
		strHdr += STR_RECORD_CONTINUOUS_L + "\t" + (pHeader->dwContinuous ? _L("Continuous") : _L("Discontinuous"));
		GETN_MULTILINE_TEXT(FileInfo, _L("File Information"), strHdr) GETN_OPTION_MUTILINE_TEXT_BOX(true, "2-15", true) SET_EDITBOX_RESIZABLE(0)
		///end DISPLAY_FILE_INFO_AS_MULTILINE_TEXT_CONTROL
		GETN_BEGIN_BRANCH(Signals, STR_SIGNALS_L) GETN_OPTION_BRANCH(GETNBRANCH_CHECK_CONTROL_SUB_BRANCH | GETNBRANCH_CHECK_CONTROL_NONRECURSIVE | GETNBRANCH_CHECK_CONTROL)
					
				int iSig;
				for ( iSig = 0; iSig < pHeader->nNumSignals; iSig++ )
				{
					GETN_BEGIN_BRANCH(Signal, pHeader->vsLabels[iSig])
						if ( bSelectable )
							GETN_CHECKBOX_BRANCH(1)
						_construct_signal_info(GETN_CURRENT_SUBNODE, pHeader, iSig);
					GETN_END_BRANCH(Signal)
				}
		GETN_END_BRANCH(Signals)
	
	return true;
}

#define	STR_IS_ANNOTATION_L		_L("Is Annotation:")
#define	STR_TRANSDUCER_TYPE_L	_L("Transducer Type:")
#define	STR_PHY_DIM_L			_L("Physical Dimension:")
#define	STR_PHY_MIN_L			_L("Physical Minimum:")	
#define	STR_PHY_MAX_L			_L("Physical Maximum:")
#define	STR_DIG_MIN_L			_L("Digital Minimum:")
#define	STR_DIG_MAX_L			_L("Digital Maximum:")
#define	STR_PREFILTER_L			_L("Prefilter:")
#define	STR_NUM_SAMPLES_EACH_L	_L("Number of Samples Each Data:")
#define	STR_FREQUENCE_L			_L("Frequency(Hz):")
static bool _construct_signal_info(TreeNode& trInfo, pOHeader pHeader, int iSig)
{
	if ( !trInfo || !pHeader )
		return false;
	GETN_USE(trInfo)
		GETN_STR(IsAnnotation, STR_IS_ANNOTATION_L, pHeader->vnAnnotations[iSig] ? "Yes" : "No") GETN_READ_ONLY_EX(2)
		GETN_STR(Transducer, STR_TRANSDUCER_TYPE_L, pHeader->vsTransducerTypes[iSig]) GETN_READ_ONLY_EX(2)
		GETN_STR(PhsicalDim, STR_PHY_DIM_L, pHeader->vsPhyDims[iSig]) GETN_READ_ONLY_EX(2)
		GETN_NUM(PhysicalMin, STR_PHY_MIN_L, pHeader->vdPhyMin[iSig]) GETN_READ_ONLY_EX(2)
		GETN_NUM(PhysicalMax, STR_PHY_MAX_L, pHeader->vdPhyMax[iSig]) GETN_READ_ONLY_EX(2)
		GETN_NUM(DigitalMin, STR_DIG_MIN_L, pHeader->vnDigMin[iSig]) GETN_READ_ONLY_EX(2)
		GETN_NUM(DigitalMax, STR_DIG_MAX_L, pHeader->vnDigMax[iSig]) GETN_READ_ONLY_EX(2)
		GETN_STR(Prefilter, STR_PREFILTER_L, pHeader->vsPrefilters[iSig]) GETN_READ_ONLY_EX(2)
		GETN_NUM(SamplesEachData, STR_NUM_SAMPLES_EACH_L, pHeader->vnNumSamplesEachData[iSig]) GETN_READ_ONLY_EX(2)
		GETN_NUM(Frequencey, STR_FREQUENCE_L, pHeader->vnNumSamplesEachData[iSig] / pHeader->dDuration) GETN_READ_ONLY_EX(2)
	return true;
}

#define	STR_CUSTOM_DATE_FMT	"yyyy'-'MM'-'dd HH':'mm':'ss'.'###"
#define	EDF_DATETIME_FMT	"%04d-%02d-%02d %02d:%02d:%02d.%03d"
static string _get_date_time_str(int nY, int nM, int nD, int nHH, int nMM, int nSS, int nMilSec)
{
	string str;
	str.Format(EDF_DATETIME_FMT, nY, nM, nD, nHH, nMM, nSS, nMilSec);
	return str;
}

#define	BYTE_MASK	0x00FFFFFF
#define	BIT_T_MASK	0x80000000
static bool _cvt_extract_bytes_into_ints(const char* buffer, const int nBytesPerPoint, const int nPoints, vector<int>& vn)
{
	if ( !buffer )
		return false;
	int iPt;
	int nTemp;
	for ( iPt = 0; iPt < nPoints; iPt++ )
	{
		memcpy(&nTemp, (LPVOID)((char*)buffer + iPt * nBytesPerPoint), nBytesPerPoint);
		
		vn[iPt] = BYTE_MASK & nTemp;
		nTemp = nTemp << 8;
		if ( nTemp & BIT_T_MASK )
			vn[iPt] |= ~BYTE_MASK; //negative
		else
			vn[iPt] &= BYTE_MASK; //positive
	}
	return true;
}

static bool _cvt_digital_to_physical(vector<int>& vn, vector<double>& vd, const int nDigitMin, const int nDigitMax, const double dPhysicalMin, const double dPhysicalMax)
{
	double dDigitRange = (nDigitMax - nDigitMin);
	double dPhysicalRange = (dPhysicalMax - dPhysicalMin);
	///Sophy 6/1/2010 BETTER_METHOD_FOR_MAPPING_DIGIT_TO_PHYSIC_VALUE
	//vd = ( (vn - nDigitMin)/(dDigitRange ) ) * (dPhysicalRange) + dPhysicalMin;
	double dBitValue = dPhysicalRange / dDigitRange;
	int nOffset = (int)(dPhysicalMax/dBitValue - nDigitMax);
	vn += nOffset;
	vd = dBitValue * vn;
	///end BETTER_METHOD_FOR_MAPPING_DIGIT_TO_PHYSIC_VALUE
	return true;
}

static bool _cvt_digital_to_physical(vector<short>& vn, vector<double>& vd, const int nDigitMin, const int nDigitMax, const double dPhysicalMin, const double dPhysicalMax)
{
	double dDigitRange = (nDigitMax - nDigitMin);
	double dPhysicalRange = (dPhysicalMax - dPhysicalMin);
	///Sophy 6/1/2010 BETTER_METHOD_FOR_MAPPING_DIGIT_TO_PHYSIC_VALUE
	//vd = ( (vn - nDigitMin)/(dDigitRange ) ) * (dPhysicalRange) + dPhysicalMin;
	double dBitValue = dPhysicalRange / dDigitRange;
	int nOffset = (int)(dPhysicalMax/dBitValue - nDigitMax);
	vn += nOffset;
	vd = dBitValue * vn;
	///end BETTER_METHOD_FOR_MAPPING_DIGIT_TO_PHYSIC_VALUE
	return true;
}

static void _inc_field(int& nNew, const int nOld, const int nAddOn, int nScale, int& nCarry)
{
	nNew = (nOld + nAddOn) % nScale;
	nCarry = (nOld + nAddOn) / nScale;
}

static void _dec_field(int& nNew, const int nOld, const int nSubBy, int nScale, int& nCarry)
{
	nCarry = nSubBy / nScale;
	int nCarry2 = nSubBy % nScale;
	nNew = nOld - nCarry2;
	if ( nNew < 0 )
	{
		nNew = nNew + nScale;
		nCarry++;
	}
}
static BOOL _update_date_time(pOTime pOld, pOTime pNew, int nMilSecs, bool bAdd = true)
{
	if ( !pOld || !pNew )
		return FALSE;
	int nDaysOfMonth[] = {
		31, 28, 31, 30, 31, 30,
		31, 31, 30, 31, 30, 31
	};
	if ( pOld->nYear % 4 == 0 && pOld->nYear % 100 != 0 || pOld->nYear % 400 == 0 )
		nDaysOfMonth[1] = 29;
	
	int nInc = 0;
	if ( bAdd )
	{
		_inc_field(pNew->nMilSecond, pOld->nMilSecond, nMilSecs, 1000, nInc);
		_inc_field(pNew->nSecond, pOld->nSecond, nInc, 60, nInc);
		_inc_field(pNew->nMinute, pOld->nMinute, nInc, 60, nInc);
		_inc_field(pNew->nHour, pOld->nHour, nInc, 24, nInc);
		_inc_field(pNew->nDay, pOld->nDay, nInc, nDaysOfMonth[pOld->nMonth - 1], nInc);
		_inc_field(pNew->nMonth, pOld->nMonth, nInc, 12, nInc);
		if ( pNew->nMonth == 0 )
		{
			pNew->nMonth = 12;
			nInc--; ///Sophy 6/8/2010 ORG-152-P7 PROPER_UPDATE_DATETIME_WHEN_INCREASE
		}
		_inc_field(pNew->nYear, pOld->nYear, nInc, 8000, nInc); //this 8000 & inc makes not sense.
	}
	else
	{
		_dec_field(pNew->nMilSecond, pOld->nMilSecond, nMilSecs, 1000, nInc);
		_dec_field(pNew->nSecond, pOld->nSecond, nInc, 60, nInc);
		_dec_field(pNew->nMinute, pOld->nMinute, nInc, 60, nInc);
		_dec_field(pNew->nHour, pOld->nHour, nInc, 24, nInc);
		_dec_field(pNew->nMonth, pOld->nMonth, nInc, 24, nInc);
		if ( pNew->nMonth == 0 )
			pNew->nMonth = 12;
		_dec_field(pNew->nYear, pOld->nYear, nInc, 8000, nInc); //8000 & inc makes not sense here
	}
	return TRUE;
}

static BOOL	_is_str_numeric_int(LPCSTR lpcszSrc)
{
	string str(lpcszSrc);
	str.TrimLeft();
	str.TrimRight();
	return is_str_numeric_integer(str);
}
static bool _check_all_valid_ascii(char* buffer, int nSize, int nLower = PRINTABLE_LOWER, int nUpper = PRINTABLE_UPPER)
{
	int ii = 0;
	for ( ii = 0; ii < nSize; ii++ )
	{
		if ( buffer[ii] < nLower || buffer[ii] > nUpper )
			return false;
	}
	return true;
}

static bool _check_valid_datetime(const char* buffer, int nSize)
{
	if ( nSize != 8 || !buffer )
		return false;
	if ( buffer[2] != '.' || buffer[5] != '.' )
		return false;
	for ( int ii = 0; ii < nSize; ii++ )
	{
		if ( ii != 2 && ii != 5 && (buffer[ii] < NUMBER_LOWER || buffer[ii] > NUMBER_UPPER) )
			return false;
	}
	return true;
}

static bool _strncpy(char* buffer, const char* Src, int nSize)
{
	if ( nSize >= EDF_INFO_BUFFER_SIZE )
		return false;
	strncpy(buffer, Src, nSize);
	buffer[nSize] = '\0';
	return true;
}

static int	_convert_eheader_to_oheader(pEDFHEADER peHeader, pOHeader poHeader)
{
	if ( !peHeader || !poHeader )
		return EDF_UNKNOWN_ERROR;
	
	char buffer[EDF_INFO_BUFFER_SIZE];
	
	//version
	_strncpy(buffer, peHeader->cVersionInfo, 8);
	if ( buffer[0] == -1 ) //BDF file unsigned 255
	{
		if ( !_check_all_valid_ascii((char*)buffer + 1, 7) )
			return EDF_INVALID_CHARACTER_FOUND;
		if ( strcmp((char*)buffer + 1, STR_BDF_VERSION_TAG) )
			return EDF_FORMAT_ERROR;
		poHeader->dwFileType = EDF_FILETYPE_BDF;
	}
	else //EDF file
	{
		if ( !_check_all_valid_ascii(buffer, 8) )
			return EDF_INVALID_CHARACTER_FOUND;
		if ( strcmp(buffer, STR_EDF_VERSION_CHECK) )
			return EDF_FORMAT_ERROR;
		poHeader->dwFileType = EDF_FILETYPE_EDF;
	}
	poHeader->strVersion = buffer; //copy version information
	
	//patient
	_strncpy(buffer, peHeader->cPatientInfo, 80);
	if ( !_check_all_valid_ascii(buffer, 80) )
		return EDF_INVALID_CHARACTER_FOUND;
	poHeader->strPatient = buffer;
	
	//recording information
	_strncpy(buffer, peHeader->cLocalRecordInfo, 80);
	if ( !_check_all_valid_ascii(buffer, 80) )
		return EDF_INVALID_CHARACTER_FOUND;
	poHeader->strRecordInfo = buffer;
	
	//start date
	_strncpy(buffer, peHeader->cStartDate, 8);
	if ( !_check_valid_datetime(buffer, 8) )
		return EDF_INVALID_DATE_TIME;
	
	string strDateTime = buffer;
	vector<string> vsTokens;
	strDateTime.GetTokens(vsTokens, '.');
	poHeader->nStartDay = atol(vsTokens[0]);
	poHeader->nStartMonth = atol(vsTokens[1]);
	poHeader->nStartYear = atol(vsTokens[2]);
	if ( poHeader->nStartYear >= 85 )
		poHeader->nStartYear += 1900;
	else
		poHeader->nStartYear += 2000;
	
	//start time
	_strncpy(buffer, peHeader->cStartTime, 8);
	if ( !_check_valid_datetime(buffer, 8) )
		return EDF_INVALID_DATE_TIME;
	
	strDateTime = buffer;
	strDateTime.GetTokens(vsTokens, '.');
	poHeader->nStartSecond = atol(vsTokens[2]);
	poHeader->nStartMinute = atol(vsTokens[1]);
	poHeader->nStartHour = atol(vsTokens[0]);
	
	//number of signals
	_strncpy(buffer, peHeader->cNumSignals, 4);
	if ( !_check_all_valid_ascii(buffer, 4) )
		return EDF_INVALID_CHARACTER_FOUND;
	
	if ( !_is_str_numeric_int(buffer) )
		return EDF_FORMAT_ERROR;
	poHeader->nNumSignals = atol(buffer);
	if ( poHeader->nNumSignals > EDF_MAX_SIGNALS )
		return EDF_SIGNALS_NUMBER_EXCEED_LIMIT;
	
	//number of bytes in header
	_strncpy(buffer, peHeader->cHeaderSize, 8);
	if ( !_check_all_valid_ascii(buffer, 8) )
		return EDF_INVALID_CHARACTER_FOUND;
	if ( !_is_str_numeric_int(buffer) )
		return EDF_FORMAT_ERROR;
	poHeader->nHeaderSize = atol(buffer);
	if ( (poHeader->nNumSignals * EXTRA_DATASET_SIZE + FIXED_HEADER_SIZE) != poHeader->nHeaderSize )
		return EDF_FORMAT_ERROR;
	
	//reserved field
	_strncpy(buffer, peHeader->cReserved, 44);
	if ( !_check_all_valid_ascii(buffer, 44) )
		return EDF_INVALID_CHARACTER_FOUND;
	
	if ( poHeader->dwFileType == EDF_FILETYPE_EDF )
	{
		poHeader->dwContinuous = 1;
		if ( strncmp(buffer, STR_EDF_CONTINUOUS, RESERVED_INFO_LENGTH) == 0 )
		{
			poHeader->dwFileType = EDF_FILETYPE_EDF_PLUS;
			poHeader->dwContinuous = 1;
		}
		else if ( strncmp(buffer, STR_EDF_DISCONTINUOUS, RESERVED_INFO_LENGTH) == 0 )
		{
			poHeader->dwFileType = EDF_FILETYPE_EDF_PLUS;
			poHeader->dwContinuous = 0;
		}
	}
	else if ( poHeader->dwFileType == EDF_FILETYPE_BDF )
	{
		poHeader->dwContinuous = 1;
		if ( strncmp(buffer, STR_BDF_CONTINUOUS, RESERVED_INFO_LENGTH) == 0 )
		{
			poHeader->dwFileType = EDF_FILETYPE_BDF_PLUS;
			poHeader->dwContinuous = 1;
		}
		else if ( strncmp(buffer, STR_BDF_DISCONTINUOUS, RESERVED_INFO_LENGTH) == 0 )
		{
			poHeader->dwFileType = EDF_FILETYPE_BDF_PLUS;
			poHeader->dwContinuous = 0;
		}
	}
	
	//number of data records
	_strncpy(buffer, peHeader->cNumData, 8);
	if ( !_check_all_valid_ascii(buffer, 8) )
		return EDF_INVALID_CHARACTER_FOUND;
	if ( !_is_str_numeric_int(buffer) )
		return EDF_FORMAT_ERROR;
	poHeader->nNumData = atol(buffer);
	
	//record duration
	_strncpy(buffer, peHeader->cDuration, 8);
	if ( !_check_all_valid_ascii(buffer, 8) )
		return EDF_INVALID_CHARACTER_FOUND;
	if ( !is_str_numeric(buffer) )
		return EDF_FORMAT_ERROR;
	
	poHeader->dDuration = atof(buffer);
	
	//signals in the header
	int iSig, nSignals = poHeader->nNumSignals;
	int nAnnots = 0;
	//prepare buffers
	poHeader->vsLabels.SetSize(nSignals);
	poHeader->vnAnnotations.SetSize(nSignals);
	poHeader->vsTransducerTypes.SetSize(nSignals);
	poHeader->vsPhyDims.SetSize(nSignals);
	poHeader->vdPhyMin.SetSize(nSignals);
	poHeader->vdPhyMax.SetSize(nSignals);
	poHeader->vnDigMin.SetSize(nSignals);
	poHeader->vnDigMax.SetSize(nSignals);
	poHeader->vsPrefilters.SetSize(nSignals);
	poHeader->vnNumSamplesEachData.SetSize(nSignals);
	poHeader->vsReserved.SetSize(nSignals);
	
	int iFirstAnnotSignal = -1;
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcLabels + iSig * EDF_LABEL_SIZE, EDF_LABEL_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_LABEL_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		
		poHeader->vsLabels[iSig] = buffer;
		
		if ( poHeader->dwFileType == EDF_FILETYPE_EDF_PLUS )
		{
			if ( strncmp(buffer, STR_EDF_ANNOTATIONS, EDF_LABEL_SIZE) == 0 )
			{
				nAnnots++;
				poHeader->vnAnnotations[iSig] = 1;
				if ( iFirstAnnotSignal < 0 )
					iFirstAnnotSignal = iSig;
			}
		}
		else if ( poHeader->dwFileType == EDF_FILETYPE_BDF_PLUS )
		{
			if ( strncmp(buffer, STR_BDF_ANNOTATIONS, EDF_LABEL_SIZE) == 0 )
			{
				nAnnots++;
				poHeader->vnAnnotations[iSig] = 1;
				if ( iFirstAnnotSignal < 0 )
					iFirstAnnotSignal = iSig;
			}
		}
		else
			poHeader->vnAnnotations[iSig] = 0;
	}
	if ( (poHeader->dwFileType == EDF_FILETYPE_EDF_PLUS || poHeader->dwFileType == EDF_FILETYPE_BDF_PLUS) && 0 == nAnnots )
		return EDF_FORMAT_ERROR;
	
	poHeader->iFirstAnnotSignal = iFirstAnnotSignal;
	
	if ( (poHeader->dwFileType != EDF_FILETYPE_EDF_PLUS && poHeader->dwFileType != EDF_FILETYPE_BDF_PLUS) || nSignals != nAnnots )
	{
		if ( poHeader->dDuration < 0.000001 )
			return EDF_FORMAT_ERROR;
	}
	
	//transducer types
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcTransducerTypes + iSig * EDF_TRANSTYPE_SIZE, EDF_TRANSTYPE_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_TRANSTYPE_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		
		poHeader->vsTransducerTypes[iSig] = buffer;
		
		if ( (poHeader->dwFileType == EDF_FILETYPE_EDF_PLUS || poHeader->dwFileType == EDF_FILETYPE_BDF_PLUS) && poHeader->vnAnnotations[iSig] == 1 )
		{
			if ( poHeader->vsTransducerTypes[iSig].Count(' ') != EDF_TRANSTYPE_SIZE )
				return EDF_FORMAT_ERROR;
		}
	}
	
	//physical dimensions
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcPhysicalDims + iSig * EDF_PHYDIM_SIZE, EDF_PHYDIM_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_PHYDIM_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		
		poHeader->vsPhyDims[iSig] = buffer;
	}
	
	//physical minimums
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcPhysicalMinimums + iSig * EDF_PHYMIN_SIZE, EDF_PHYMIN_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_PHYMIN_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		if ( !is_str_numeric(buffer) )
			return EDF_FORMAT_ERROR;
		
		poHeader->vdPhyMin[iSig] = atof(buffer);
	}
	
	//physical maximums
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcPhysicalMaximums + iSig * EDF_PHYMAX_SIZE, EDF_PHYMAX_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_PHYMAX_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		if ( !is_str_numeric(buffer) )
			return EDF_FORMAT_ERROR;
		
		poHeader->vdPhyMax[iSig] = atof(buffer);
	}
	
	//digital minimums
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcDigitalMinimums + iSig * EDF_DIGMIN_SIZE, EDF_DIGMIN_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_DIGMIN_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		if ( !_is_str_numeric_int(buffer) )
			return EDF_FORMAT_ERROR;
		
		poHeader->vnDigMin[iSig] = atol(buffer);
		if ( poHeader->dwFileType == EDF_FILETYPE_EDF_PLUS && poHeader->vnAnnotations[iSig] == 1 )
		{
			if ( poHeader->vnDigMin[iSig] != MIN_DIGMIN_EDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
		if ( poHeader->dwFileType == EDF_FILETYPE_BDF_PLUS && poHeader->vnAnnotations[iSig] == 1 )
		{
			if ( poHeader->vnDigMin[iSig] != MIN_DIGMIN_BDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
		if ( (poHeader->dwFileType & EDF_BDF_MASK) == 0 ) //edf, edf+
		{
			if ( poHeader->vnDigMin[iSig] > MAX_DIGMAX_EDF_PLUS || poHeader->vnDigMin[iSig] < MIN_DIGMIN_EDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
		if ( (poHeader->dwFileType & EDF_BDF_MASK) == 1 ) //bdf, bdf+
		{
			if ( poHeader->vnDigMin[iSig] > MAX_DIGMAX_BDF_PLUS || poHeader->vnDigMin[iSig] < MIN_DIGMIN_BDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
	}
	//digital maximums
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcDigitalMaximums + iSig * EDF_DIGMAX_SIZE, EDF_DIGMAX_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_DIGMAX_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		if ( !_is_str_numeric_int(buffer) )
			return EDF_FORMAT_ERROR;
		
		poHeader->vnDigMax[iSig] = atol(buffer);
		if ( poHeader->dwFileType == EDF_FILETYPE_EDF_PLUS && poHeader->vnAnnotations[iSig] == 1 )
		{
			if ( poHeader->vnDigMax[iSig] != MAX_DIGMAX_EDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
		if ( poHeader->dwFileType == EDF_FILETYPE_BDF_PLUS && poHeader->vnAnnotations[iSig] == 1 )
		{
			if ( poHeader->vnDigMax[iSig] != MAX_DIGMAX_BDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
		if ( (poHeader->dwFileType & EDF_BDF_MASK) == 0 ) //edf, edf+
		{
			if ( poHeader->vnDigMax[iSig] > MAX_DIGMAX_EDF_PLUS || poHeader->vnDigMin[iSig] < MIN_DIGMIN_EDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
		if ( (poHeader->dwFileType & EDF_BDF_MASK) == 1 ) //bdf, bdf+
		{
			if ( poHeader->vnDigMax[iSig] > MAX_DIGMAX_BDF_PLUS || poHeader->vnDigMin[iSig] < MIN_DIGMIN_BDF_PLUS )
				return EDF_FORMAT_ERROR;
		}
		
		//min max limit
		if ( poHeader->vnDigMax[iSig] < poHeader->vnDigMin[iSig] + 1 )
			return EDF_FORMAT_ERROR;
	}

	//prefilter
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcPrefilterings + iSig * EDF_PREFILTER_SIZE, EDF_PREFILTER_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_PREFILTER_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		
		poHeader->vsPrefilters[iSig] = buffer;
		if ( (poHeader->dwFileType == EDF_FILETYPE_EDF_PLUS || poHeader->dwFileType == EDF_FILETYPE_BDF_PLUS) && poHeader->vnAnnotations[iSig] == 1 )
		{
			if ( poHeader->vsTransducerTypes[iSig].Count(' ') != EDF_TRANSTYPE_SIZE )
				return EDF_FORMAT_ERROR;
		}
	}
	
	//number of samples in each data record
	poHeader->nRecordSize = 0;
	int iMaxSamplesSignal = 0; //default, the first signal
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcSamplesEachData + iSig * EDF_NUM_SAMPLE_SIZE, EDF_NUM_SAMPLE_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_NUM_SAMPLE_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		if ( !_is_str_numeric_int(buffer) )
			return EDF_FORMAT_ERROR;
		poHeader->vnNumSamplesEachData[iSig] = atol(buffer);
		if ( poHeader->vnNumSamplesEachData[iSig] < 1 )
			return EDF_FORMAT_ERROR;
		
		if ( poHeader->vnNumSamplesEachData[iSig] > poHeader->vnNumSamplesEachData[iMaxSamplesSignal] )
			iMaxSamplesSignal = iSig;
		
		poHeader->nRecordSize += poHeader->vnNumSamplesEachData[iSig];
	}
	poHeader->iMaxSamplesSignal = iMaxSamplesSignal;
	
	if ( (poHeader->dwFileType & EDF_BDF_MASK) == 0 ) //edf, edf+
	{
		poHeader->nRecordSize *= EDF_SAMPLE_SIZE; //2 bytes each sample
	}
	else //bdf, bdf+
	{
		poHeader->nRecordSize *= BDF_SAMPLE_SIZE; //3 bytes each sample
	}
	
	//reserved fields
	for ( iSig = 0; iSig < nSignals; iSig++ )
	{
		_strncpy(buffer, peHeader->pcReserved + iSig * EDF_RESERVED_SIZE, EDF_RESERVED_SIZE);
		if ( !_check_all_valid_ascii(buffer, EDF_RESERVED_SIZE) )
			return EDF_INVALID_CHARACTER_FOUND;
		poHeader->vsReserved[iSig] = buffer;
	}
	
	//patient information
	_strncpy(buffer, peHeader->cPatientInfo, 80);
	string strTemp = buffer;
	strTemp.TrimRight(); //trim right spaces
	strTemp.GetTokens(poHeader->vsPatientInfo, CHAR_INFO_FIELD_SEP); //need to check later. stored as: Code, Sex, Birthdate, Name

	//maybe need to continue, if need more information for importing data...
	return EDF_SUCCESS;
}

static	int _parse_annotation_list(const char* buffer, int nSize, vector<string>& vsTimeStamps, vector<string>& vsDurations, vector<string>& vsAnnotLists)
{
	if ( nSize < min(EDF_SAMPLE_SIZE, BDF_SAMPLE_SIZE) )
		return EDF_FORMAT_ERROR;
	
	int iPos = 0; //current position being processed...
	LPSTR lp = (LPSTR)buffer;
	
	while ( iPos < nSize ) //loop all ATLs of annotation signal in data record
	{
		lp = (LPSTR)(buffer + iPos);
		string strTemp(lp); //buffer may contain '\0' inside the string buffer, and may be trimed, but here is allowed since I only want to find some specified character before the first '\0'
		if ( strTemp.GetLength() == 0 )
			break; //all ATLs processed.
		
		string strTimeStamp, strDuration;
		if ( lp[0] != '+' && lp[0] != '-' )
			return EDF_NO_TIMESTAMP;
		
		int nTimeStampEnd = strTemp.Find(CHAR_TAL_TIMESTAMP_SEP); //find the end position of the timestamp
		int nAnnotEnd = strTemp.Find(CHAR_TAL_ANNOTATION_SEP); //find the end position of the first annotation
		if ( nTimeStampEnd > 0 ) 
		{
			LPSTR lpDuration = strDuration.GetBuffer(nAnnotEnd - nTimeStampEnd);
			_strncpy(lpDuration, lp + nTimeStampEnd + 1, nAnnotEnd - nTimeStampEnd - 1); //ignore last CHAR_TAL_ANNOTATION_SEP
			strDuration.ReleaseBuffer();
		}
		else	//maybe not found, and treated as duration skipped
		{
			if ( nAnnotEnd < 0 )
				return EDF_FORMAT_ERROR;
			nTimeStampEnd = nAnnotEnd;
			strDuration = "0"; //ignored		
		}
			
		LPSTR lpTimeStamp = strTimeStamp.GetBuffer(nTimeStampEnd + 1);
		_strncpy(lpTimeStamp, lp, nTimeStampEnd);
		strTimeStamp.ReleaseBuffer();
		
		vsTimeStamps.Add(strTimeStamp);
		vsDurations.Add(strDuration);
		
		string strAnnoList(lp + nAnnotEnd);
		strAnnoList.TrimLeft(CHAR_TAL_ANNOTATION_SEP);
		strAnnoList.TrimRight(CHAR_TAL_ANNOTATION_SEP);
		vsAnnotLists.Add(strAnnoList);
		iPos += strTemp.GetLength() + 1; //first ATL and a byte with value '\0'
	}
	return EDF_SUCCESS;
}
//utils END

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////// Declaration of EDF class ///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class EDF
{
public:
	EDF();
	~EDF();
	
	int		Open(LPCSTR lpcszFile);
	int		Import(Worksheet& wks, const TreeNode& trSettings);
	int		GetFileInfo(TreeNode& trInfo, BOOL bSelectable = TRUE);
	void	Close();
	
protected:
	int		Init();
	int		UpdateSignalTree(OriginObject& obj, int iSignal, LPCSTR lpcszLabel);
	int		UpdateFileTree(OriginObject& obj, LPCSTR lpcszLabel);
	
private:
	string		m_strFileName;
	FILE*		m_fpEDF;
	pEDFHEADER	m_pEDFHeader;
	OHeader		m_oHeader;
	TreeNode	m_trFileInfo;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////// Implementation of EDF class ///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define	FIELD(_Name)	m_pEDFHeader->_Name

EDF::EDF()
{
	m_fpEDF = NULL;
	m_pEDFHeader = NULL;
}

EDF::~EDF()
{
	Close();
}

int		EDF::Open(LPCSTR lpcszFile)
{
	//in case already opened
	Close();
	
	m_fpEDF = fopen(lpcszFile, "rb");
	if ( m_fpEDF == NULL )
		return EDF_FILE_NOT_EXIST;
	
	m_strFileName = lpcszFile;
	
	m_pEDFHeader = (pEDFHEADER)calloc(1, sizeof(EDFHEADER));
	
	_init_edf_header_info(m_pEDFHeader);
	
	return Init();
}

int		EDF::Init()
{
	if ( m_fpEDF == NULL )
		return EDF_FILE_NOT_OPEN;
	
	fseek(m_fpEDF, 0, SEEK_SET);
	
	//basic information
	fread(FIELD(cVersionInfo), 8, 1, m_fpEDF);
	fread(FIELD(cPatientInfo), 80, 1, m_fpEDF);
	fread(FIELD(cLocalRecordInfo), 80, 1, m_fpEDF);
	fread(FIELD(cStartDate), 8, 1, m_fpEDF);
	fread(FIELD(cStartTime), 8, 1, m_fpEDF);
	fread(FIELD(cHeaderSize), 8, 1, m_fpEDF);
	fread(FIELD(cReserved), 44, 1, m_fpEDF);
	fread(FIELD(cNumData), 8, 1, m_fpEDF);
	fread(FIELD(cDuration), 8, 1, m_fpEDF);
	fread(FIELD(cNumSignals), 4, 1, m_fpEDF);
	
	//extend information
	int nNumSignals = atol(FIELD(cNumSignals));
	_allocate_header_memory(m_pEDFHeader, nNumSignals);
	
#define	READ_FIELD(_Field, _Size)	fread(FIELD(_Field), _Size, nNumSignals, m_fpEDF)

	READ_FIELD(pcLabels, EDF_LABEL_SIZE);
	READ_FIELD(pcTransducerTypes, EDF_TRANSTYPE_SIZE);
	READ_FIELD(pcPhysicalDims, EDF_PHYDIM_SIZE);
	READ_FIELD(pcPhysicalMinimums, EDF_PHYMIN_SIZE);
	READ_FIELD(pcPhysicalMaximums, EDF_PHYMAX_SIZE);
	READ_FIELD(pcDigitalMinimums, EDF_DIGMIN_SIZE);
	READ_FIELD(pcDigitalMaximums, EDF_DIGMAX_SIZE);
	READ_FIELD(pcPrefilterings, EDF_PREFILTER_SIZE);
	READ_FIELD(pcSamplesEachData, EDF_NUM_SAMPLE_SIZE);
	READ_FIELD(pcReserved, EDF_RESERVED_SIZE);
	
	int nRet = _convert_eheader_to_oheader(m_pEDFHeader, &m_oHeader);
	return nRet;
}

int		EDF::UpdateSignalTree(OriginObject& obj, int iSignal, LPCSTR lpcszLabel)
{
	if ( m_trFileInfo )
	{
		TreeNode trSignal = m_trFileInfo.Signals.FindNodeByAttribute(STR_LABEL_ATTRIB, m_oHeader.vsLabels[iSignal], FALSE, TRUE);
		if ( trSignal )
		{
			trSignal.RemoveAttribute(STR_USE_ATTRIB);
			string strLabel = lpcszLabel;
			trSignal.SetAttribute(STR_LABEL_ATTRIB, strLabel);
			///---Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
			//trSignal.Show = 0; ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
			//set_user_info(obj, strLabel, trSignal);
			fu_set_import_file_info(obj, trSignal, strLabel, 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
			//trSignal.Show = 1;
			//strLabel = lpcszLabel;
			//strLabel.Insert(0, "EDF ");
			//trSignal.SetAttribute(STR_LABEL_ATTRIB, strLabel);
			//set_import_file_info(obj, trSignal, strLabel);
			///---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 EDF_SUCCESS;
		}
	}
	return EDF_NO_SIGNAL_INFO;
}

int		EDF::UpdateFileTree(OriginObject& obj, LPCSTR lpcszLabel)
{
	if ( m_trFileInfo )
	{
		string strLabel = lpcszLabel;
		TreeNode trFileInfo = m_trFileInfo.Clone(TRUE);
		trFileInfo.Signals.Remove();
		set_user_info(obj, strLabel, trFileInfo);
		return EDF_SUCCESS;
	}
	return EDF_NO_FILE_INFO;
}

///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
class	LTVarHelper
{
public:
	LTVarHelper(LPCSTR lpVarName, double dVarValue)
	{
		m_strVarName = lpVarName;
		LT_get_var(lpVarName, &m_dVarValue);
		LT_set_var(lpVarName, dVarValue);
	}
	~LTVarHelper()
	{
		LT_set_var(m_strVarName, m_dVarValue);
	}
private:
	string	m_strVarName;
	double	m_dVarValue;
};
///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
int		EDF::Import(Worksheet& wks, const TreeNode& trSettings)
{
	if ( !wks )
		return EDF_INVALID_WORKSHEET;
	if ( NULL == m_fpEDF )
		return EDF_FILE_NOT_OPEN;
	if ( !trSettings )
		return EDF_NO_SIGNAL_SELECTED;
	
	m_trFileInfo = trSettings; //trSettings is the same as FileInfo tree.m_trFileInfo
	
	int nDataRecordOffset = m_oHeader.nHeaderSize;
	fseek(m_fpEDF, nDataRecordOffset, SEEK_SET); //beginning of data record
	
	int nSignals = m_oHeader.nNumSignals;
	int nDataRecords = m_oHeader.nNumData;
	int nMaxSamples = m_oHeader.vnNumSamplesEachData[m_oHeader.iMaxSamplesSignal]; //max number of samples  of a single signal in data record
	int iSig, iRecord;
	bool bBDF = (m_oHeader.dwFileType & EDF_BDF_MASK) != 0;
	
	iSig = 0;
	int nCols = 0; ///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	vector<int> vnSelSignals(0); //selected signals' indices
	foreach(TreeNode trSignal in trSettings.Signals.Children)
	{
		if ( trSignal.Use )
		{
			vnSelSignals.Add(iSig);
			///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
			if ( m_oHeader.vnAnnotations[iSig] == 0 ) //not annotationlist
				nCols++;
			///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
		}
		iSig++;
	}
	if ( vnSelSignals.GetSize() == 0 ) //no selection
		return EDF_NO_SIGNAL_SELECTED;
		
	///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	//wks.SetSize(-1, vnSelSignals.GetSize() + 1); //resize the worksheet for putting data, one extra column for timestamps.
	wks.SetSize(-1, nCols);	//each ordinary signal occupy one column.
	///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	
	int iSelIndex = 0;
	for ( iSig = 0; iSig < nSignals && iSelIndex < vnSelSignals.GetSize(); iSig++ )
	{
		if ( iSig == vnSelSignals[iSelIndex] )
		{
			Column col(wks, iSelIndex);
			double dRateHz = m_oHeader.vnNumSamplesEachData[vnSelSignals[iSelIndex]] / m_oHeader.dDuration;
			string strComments;
			strComments.Format("%s Hz", ftoa(dRateHz, "*"));
			col.SetLongName(m_oHeader.vsLabels[vnSelSignals[iSelIndex]]);
			col.SetComments(strComments);
			col.SetUnits(m_oHeader.vsPhyDims[vnSelSignals[iSelIndex]]);
			col.SetType(OKDATAOBJ_DESIGNATION_Y);
			if ( m_oHeader.vnAnnotations[vnSelSignals[iSelIndex]] == 0 ) //if not annotations, set as numeric
			{
				//if ( bBDF )
					//col.SetInternalDataType(FSI_LONG);
				//else
					//col.SetInternalDataType(FSI_SHORT);
				col.SetInternalDataType(FSI_DOUBLE);
			}
			col.SetUpperBound(-1);
			UpdateSignalTree(col, iSig, STR_SIGNAL_DETAILS_L);
			iSelIndex++; //check next. Note: Items in vnSelSignals are sorted ascending.
		}
		
	}
	///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	////create timestamp column
	//Column colTime(wks, 0);
	//colTime.SetLongName("Time");
	//colTime.SetComments("TimeStamp of Sample");
	//colTime.SetUpperBound(-1);
	///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	
	double dSampleInterval;
	OTime oStart, oEnd;
	oStart.nYear = m_oHeader.nStartYear;
	oStart.nMonth = m_oHeader.nStartMonth;
	oStart.nDay = m_oHeader.nStartDay;
	oStart.nHour = m_oHeader.nStartHour;
	oStart.nMinute = m_oHeader.nStartMinute;
	oStart.nSecond = m_oHeader.nStartSecond;
	oStart.nMilSecond = 0;	
	
	double dStartDate, dEndDate;
	bool bDateTimeFormat = true; //not controlable from GUI currently, if false, only show values by offset.
	if ( m_oHeader.dwContinuous == 1 || m_oHeader.nNumData == 1 )
	{
		double dSeconds = m_oHeader.dDuration * nDataRecords;  //diff in seconds
		_update_date_time(&oStart, &oEnd, dSeconds * 1000, true);
		string strDateTimeStart = _get_date_time_str(oStart.nYear, oStart.nMonth, oStart.nDay, oStart.nHour, oStart.nMinute, oStart.nSecond, oStart.nMilSecond);
		string strDateTimeEnd = _get_date_time_str(oEnd.nYear, oEnd.nMonth, oEnd.nDay, oEnd.nHour, oEnd.nMinute, oEnd.nSecond, oEnd.nMilSecond);
		

		if ( !str_to_date_custom(strDateTimeStart, STR_CUSTOM_DATE_FMT, &dStartDate) )
			return EDF_INVALID_DATE_TIME;

		if ( !str_to_date_custom(strDateTimeEnd, STR_CUSTOM_DATE_FMT, &dEndDate) )
			return EDF_INVALID_DATE_TIME;
		
		if ( !bDateTimeFormat ) //not full datetime, just only offset from the start.
		{
			dStartDate = 0;
			dEndDate = dSeconds * 1000;
		}
		dSampleInterval = (dEndDate - dStartDate) / (nMaxSamples * nDataRecords);
	
		///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
		//vectorbase& vbInternal = colTime.GetDataObject();
		//vbInternal.Data(dStartDate, dEndDate - dSampleInterval, dSampleInterval);
		//vbInternal.SetSize(nMaxSamples * nDataRecords);
		///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	}
	
	///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	//if ( bDateTimeFormat )
	//{
		//colTime.SetFormat(OKCOLTYPE_DATE);
		//colTime.SetSubFormat(LDF_OBJ_CUSTOM);
		//colTime.SetCustomDisplay(STR_CUSTOM_DATE_FMT);
		//colTime.SetWidth(16);
	//}
	//else
		//colTime.SetUnits("ms");
	///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	
	//progressbar...
	progressBox pb("", PBOX_DEFAULT_STYLE, FALSE); //show progress bar
	pb.SetRange(0, nDataRecords * nSignals);
	string strMsg;
	strMsg.Format("Importing File %s...", m_strFileName);
	pb.SetText(strMsg, PBOXT_TITLE);
	int nRet = EDF_SUCCESS;
	
	///Sophy 6/28/2010 ORG-152-S10 REPORT_ANNOTATIONLIST_IN_SEPARATE_SHEET
	//prepare annotation list sheet
	string strAnnotSheetName = GetFileName(m_strFileName, TRUE) + "_AnnotationLists";
	Worksheet wksAnnot = wks.GetPage().Layers(strAnnotSheetName);
	if ( !wksAnnot )
	{
		int iIndex = wks.GetPage().AddLayer(strAnnotSheetName);
		wksAnnot = wks.GetPage().Layers(iIndex);
		wksAnnot.SetSize(-1, 0);
	}
	///end REPORT_ANNOTATIONLIST_IN_SEPARATE_SHEET
	
	///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
	//temporary reset@ND for set even sammpling
	LTVarHelper	ltHelper("@ND", 1e-30);
	///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL

	//prepare buffer for read annotations
	const int nSampleSize = bBDF ? BDF_SAMPLE_SIZE : EDF_SAMPLE_SIZE;
	char* buffer = (char*)malloc(nMaxSamples * nSampleSize);
	for ( iRecord = 0; iRecord < nDataRecords && (nRet == EDF_SUCCESS); iRecord++ )
	{
		iSelIndex = 0;
		for ( iSig = 0; iSig < nSignals && iSelIndex < vnSelSignals.GetSize(); iSig++ )
		{
			int nSamples = m_oHeader.vnNumSamplesEachData[iSig];
			if ( iSig == vnSelSignals[iSelIndex] )
			{
				//update progressbar status
				if ( !pb.Set(iSig + iRecord * nSignals) ) //if user abort, return.
				{
					nRet = EDF_USERABORT;
					break;
				}
				strMsg.Format("Importing record %d/%d, signal %d/%d...", iRecord + 1, nDataRecords, iSig + 1, nSignals);
				pb.SetText(strMsg, PBOXT_MIDCENTER);
				
				Column col(wks, iSelIndex);
				if ( m_oHeader.vnAnnotations[iSig] == 0 ) //ordinary signal
				{
					vectorbase& vbInternal = col.GetDataObject();
					if ( bBDF )
					{
						fread(buffer, nSampleSize, nSamples, m_fpEDF);
						vector<int> vn(nSamples);
						_cvt_extract_bytes_into_ints(buffer, nSampleSize, nSamples, vn);
						vector<double> vd(nSamples);
						_cvt_digital_to_physical(vn, vd, m_oHeader.vnDigMin[iSig], m_oHeader.vnDigMax[iSig], m_oHeader.vdPhyMin[iSig], m_oHeader.vdPhyMax[iSig]);
						///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
						/*
						///Sophy 6/4/2010 ORG-152-P6 SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY
						if ( nMaxSamples % nSamples == 0 )
						{
							int iMiss, nMiss = nMaxSamples/nSamples - 1;
							for ( iMiss = 0; iMiss < nMiss; iMiss++ )
							{
								vector<int> vnIndices;
								vnIndices.Data(1, nMaxSamples, iMiss + 2);
								vnIndices.SetSize(nSamples);
								vd.InsertValue(vnIndices, NANUM);
							}
						}
						*/
						///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
						///Sophy 6/8/2010 ORG-152 SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY_EX
						if ( iRecord == 0 ) //only need to set once the sample interval
						{
							double dSampleInterval = (dEndDate - dStartDate) / (nSamples * nDataRecords - 1);
							col.SetEvenSampling(dStartDate, dSampleInterval, col.GetUnits(), col.GetLongName());
						}
						///end SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY_EX
						///end SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY
						vbInternal.Append(vd);
					}
					else
					{
						vector<short> vs(nSamples); //buffer to receive data
						fread(vs, nSampleSize, nSamples, m_fpEDF);
						vector<double> vd(nSamples);
						_cvt_digital_to_physical(vs, vd, m_oHeader.vnDigMin[iSig], m_oHeader.vnDigMax[iSig], m_oHeader.vdPhyMin[iSig], m_oHeader.vdPhyMax[iSig]);
						///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
						/*
						///Sophy 6/4/2010 ORG-152-P6 SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY
						//vd.SetSize(nMaxSamples);
						if ( nMaxSamples % nSamples == 0 )
						{
							int iMiss, nMiss = nMaxSamples/nSamples - 1;
							for ( iMiss = 0; iMiss < nMiss; iMiss++ )
							{
								//insert missing values as those with blank in 3rd part software.
								vector<int> vnIndices;
								vnIndices.Data(1, nMaxSamples, iMiss + 2);
								vnIndices.SetSize(nSamples);
								vd.InsertValue(vnIndices, NANUM);
							}
						}
						*/
						///ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
						///Sophy 6/8/2010 ORG-152 SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY_EX
						if ( iRecord == 0 ) //only need to set once the sample interval
						{
							double dSampleInterval = (dEndDate - dStartDate) / (nSamples * nDataRecords - 1);
							col.SetEvenSampling(dStartDate, dSampleInterval, col.GetUnits(), col.GetLongName());
						}
						///end SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY_EX
						///end SYNC_SIGNALS_WITH_DIFFERENT_FREQUENCY
						vbInternal.Append(vd);
					}
				}
				else		//annotation, get a text
				{
					fread(buffer, nSamples * nSampleSize, 1, m_fpEDF);
					vector<string> vsTimeStamps, vsDurations, vsAnnotLists;
					_parse_annotation_list(buffer, nSamples * nSampleSize, vsTimeStamps, vsDurations, vsAnnotLists);
					///Sophy 6/28/2010 ORG-152-S10 REPORT_ANNOTATIONLIST_IN_SEPARATE_SHEET
					for ( int iTAL = 0; iTAL < vsTimeStamps.GetSize(); iTAL++ )
					{
						GETN_TREE(trReport)
						GETN_NUM(RecordNumber, "Record", iRecord)
						GETN_CURRENT_SUBNODE.SetAttribute(STR_COL_DESIGNATION_ATTRIB, OKDATAOBJ_DESIGNATION_X);
						GETN_STR(OnSet, "OnSet", vsTimeStamps[iTAL])
						GETN_CURRENT_SUBNODE.SetAttribute("Units", "ms");
						GETN_CURRENT_SUBNODE.TypeID = TNVAL_TYPE_CSTRING;
						GETN_CURRENT_SUBNODE.SetAttribute(STR_COL_DESIGNATION_ATTRIB, OKDATAOBJ_DESIGNATION_Y);
						GETN_NUM(Duration, "Duration", atof(vsDurations[iTAL]))
						GETN_CURRENT_SUBNODE.SetAttribute("Units", "ms");
						GETN_CURRENT_SUBNODE.SetAttribute(STR_COL_DESIGNATION_ATTRIB, OKDATAOBJ_DESIGNATION_Y);
						GETN_STR(Annotation, "Annotation", vsAnnotLists[iTAL]);
						GETN_CURRENT_SUBNODE.TypeID = TNVAL_TYPE_CSTRING;
						GETN_CURRENT_SUBNODE.SetAttribute(STR_COL_DESIGNATION_ATTRIB, OKDATAOBJ_DESIGNATION_Y);
						out_tree_to_wks(trReport, wksAnnot);
					}
					if ( iRecord == 0 )
					{
						UpdateSignalTree(wksAnnot, iSig, STR_SIGNAL_DETAILS_L);
					}
					/*
					vector<string> vsTALInfos;
					for ( int iTAL = 0; iTAL < vsTimeStamps.GetSize(); iTAL++ )
					{
						//timestamps info
						vsTALInfos.Add(STR_TIMESTAMP);
						vsTALInfos.Add(vsTimeStamps[iTAL]);
						
						//duration info
						vsTALInfos.Add(STR_DURATION);
						vsTALInfos.Add(vsDurations[iTAL]);
						
						//vsAnnotLists;
						vsTALInfos.Add(STR_ANNOTATION_LIST);
						vector<string> vsAnnots;
						vsAnnotLists[iTAL].GetTokens(vsAnnots, CHAR_TAL_ANNOTATION_SEP);
						vsTALInfos.Append(vsAnnots);
					}
					col.PutStringArray(vsTALInfos, nMaxSamples * iRecord);
					///Sophy 6/8/2010 ORG-152-P8 UPDATE_DATATIME_COLUMN_DATA_WHEN_CONTAIN_ANNOTATION
					//if ( m_oHeader.dwContinuous == 0 ) //discontinuous, need to update "Time" column
					if ( m_oHeader.dwContinuous == 0 || m_oHeader.dDuration == 0 )	///Sophy 6/12/2010 ORG-152-P10 PROPER_INIT_DATATIME_COLUMN_WHEN_CONTAIN_ONLY_ANNOTATION
					///end UPDATE_DATATIME_COLUMN_DATA_WHEN_CONTAIN_ANNOTATION
					{
						///Sophy 6/24/2010 ORG-152-P12 PROPER_INIT_DATETIME_COLUMN_BY_ANNOTATIONLIST
						//if ( vsTimeStamps.GetSize() > 1 )
						if ( vsTimeStamps.GetSize() > 0 )
						///end PROPER_INIT_DATETIME_COLUMN_BY_ANNOTATIONLIST
						{
							OTime oRecStart;
							double dSecs = atof(vsTimeStamps[0]);
							_update_date_time(&oStart, &oRecStart, dSecs * 1000, true);
							///Sophy 6/8/2010 ORG-152-P7 UPDATE_DATATIME_COLUMN_DATA_WHEN_CONTAIN_ANNOTATION
							//_update_date_time(&oStart, &oEnd, m_oHeader.dDuration * 1000, true);
							if ( m_oHeader.dDuration != 0 )
							{
								_update_date_time(&oRecStart, &oEnd, m_oHeader.dDuration * 1000, true);
							}
							else
							{
								double dMilSecs = (atof(vsTimeStamps[vsTimeStamps.GetSize() - 1]) + atof(vsDurations[vsDurations.GetSize() - 1])) * 1000;
								_update_date_time(&oRecStart, &oEnd, dMilSecs, true);
							}
							///end UPDATE_DATATIME_COLUMN_DATA_WHEN_CONTAIN_ANNOTATION
							
							string strDateTimeStart = _get_date_time_str(oRecStart.nYear, oRecStart.nMonth, oRecStart.nDay, oRecStart.nHour, oRecStart.nMinute, oRecStart.nSecond, oRecStart.nMilSecond);
							string strDateTimeEnd = _get_date_time_str(oEnd.nYear, oEnd.nMonth, oEnd.nDay, oEnd.nHour, oEnd.nMinute, oEnd.nSecond, oEnd.nMilSecond);
							
							double dStartDate;
							if ( !str_to_date_custom(strDateTimeStart, STR_CUSTOM_DATE_FMT, &dStartDate) )
								return EDF_INVALID_DATE_TIME;
							double dEndDate;
							if ( !str_to_date_custom(strDateTimeEnd, STR_CUSTOM_DATE_FMT, &dEndDate) )
								return EDF_INVALID_DATE_TIME;
							
							double dSamlpleInterval = (dEndDate - dStartDate) / (nMaxSamples);
					
							vector vAdd;
							vAdd.Data(dStartDate, dEndDate - dSamlpleInterval, dSamlpleInterval);
							vAdd.SetSize(nMaxSamples);
							
							///Sophy 6/28/2010 ORG-152-S11 ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
							//vectorbase& vbInternal = colTime.GetDataObject();
							//vbInternal.Append(vAdd);
							//
							//colTime.SetFormat(OKCOLTYPE_DATE);
							//colTime.SetSubFormat(LDF_OBJ_CUSTOM);
							//colTime.SetCustomDisplay(STR_CUSTOM_DATE_FMT);
							///end ADD_DATATIME_SAMPLE_INTERVALS_FOR_EACH_SIGNAL
						}
						else
						{
							ASSERT(FALSE);
							return EDF_FORMAT_ERROR;
						}
					}
					*/
					///end REPORT_ANNOTATIONLIST_IN_SEPARATE_SHEET
				}
				iSelIndex++;
			}
			else //if not stored in worksheet, still need to read and discard it.
			{
				fread(buffer, nSampleSize, nSamples, m_fpEDF);
			}
		}
	}
	free(buffer);
	
	//update worksheet name
	///Sophy 6/28/2010 ORG-152-S10 REPORT_ANNOTATIONLIST_IN_SEPARATE_SHEET
	if ( wks.GetNumCols() == 0 )
	{
		wks.Destroy();
		UpdateFileTree(wksAnnot, STR_EDF_FILE_INFO);
		wks = wksAnnot; //refer to annot list layer.
	}
	else
	{
		wks.SetName(GetFileName(m_strFileName, TRUE));
		UpdateFileTree(wks, STR_EDF_FILE_INFO);
	}
	if ( is_sheet_empty(wksAnnot) )
	{
		wksAnnot.Destroy();
	}
	///end REPORT_ANNOTATIONLIST_IN_SEPARATE_SHEET
	return nRet;
}

int		EDF::GetFileInfo(TreeNode& trInfo, BOOL bSelectable) //TRUE
{
	if ( m_fpEDF == NULL )
		return EDF_FILE_NOT_OPEN;
	_construct_update_edf_info(m_strFileName, trInfo, &m_oHeader, bSelectable);
	m_trFileInfo = trInfo;
	return EDF_SUCCESS;
}


void	EDF::Close()
{
	if ( m_fpEDF == NULL )
		return;
	
	fclose(m_fpEDF);
	m_fpEDF = NULL;
	m_strFileName = "";
	
	_free_edf_header_info(m_pEDFHeader);
	m_pEDFHeader = NULL;
}
