/*------------------------------------------------------------------------------*
 * 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 11/17/06 ADD_INFO_TO_COLUMNS											*
 * Hong 11/20/06 ADD_VALID_FILE_CHECK											*
 * Cheney 2007-1-17 HANDLE_COLUMN_INFO											*
 * Cheney 2007-3-7 DATASET_NOW_ONLY_SUPPORT_DOUBLE_TYPE_COLUMN					*
 *	Hong 07/17/08 v8.0903 FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM*
 *	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 "readDCF.h"
#include <..\Originlab\fu_utils.h> ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
////////////////////////////////////////////////////////////////////////////////////
//#include "readDCF.h"

////////////////////////////////////////////////////////////////////////////////////
// Include your own header files here.


////////////////////////////////////////////////////////////////////////////////////
// Start your functions here.
/// Hong 11/20/06 ADD_VALID_FILE_CHECK
#define FILE_TYPE_ID	0x484D5447
#define FILE_CHUNK_ID	0x444D5447
/// end ADD_VALID_FILE_CHECK

typedef struct{
	DWORD Magic;
	DWORD Version;
}DCF_HEADER;

typedef struct{
	DWORD Magic;
	DWORD Length;
	WORD Datatype;
	WORD TimeAxis;
	int AssignedTimeAxis;
	double starttime;
	double TimeIncrementl;
	double scale;
	double offset;
	double EngRangeMin;
	double EngRangeMax;
	BYTE engunitlength;
	char engunit[15];
	BYTE descriptionlength;
	char Description[31];	
	//BYTE data;	
}DCF_DATA;
/// Hong 11/20/06 MOVE_FROM_FILE_SOURCE
/*
//Error Message
enum{
	FAIL_TO_OPEN_FILE				=	-10,			
	FILE_TOO_SMALL,			
	NUMBER_OF_COLUMNS_INCONSISTENT,			
	UNSUPPORTED_DATA_TYPE,			
	UNSUPPORTED_VERSION,
	FAIL_TO_READ_FILE_INFO,
	FAIL_TO_READ_DATA
};
*/
/// end MOVE_FROM_FILE_SOURCE

///Cheney 2007-1-17 HANDLE_COLUMN_INFO
enum{
	DATA_TYPE_SHORT		= 2,
	DATA_TYPE_INT		= 3,
	DATA_TYPE_FLOAT		= 4,	
	DATA_TYPE_DOUBLE	= 5,
	DATA_TYPE_BYTE		= 17,
};
///end HANDLE_COLUMN_INFO

static void add_header_info_in_tree(DCF_HEADER& dcfHdr, TreeNode& trInfo,  string& strLabel) 
{
	TreeNode trChild= trInfo.AddNode(strLabel); 
	trChild = dcfHdr;
}

static void add_chunk_info_in_tree(DCF_DATA& dcfData, TreeNode& trInfo,  string& strLabel) 
{
	TreeNode trChild= trInfo.AddNode(strLabel); 
	trChild = dcfData;
}
	
static int _read_file_info(file& fid, DCF_HEADER& dcfHdr, TreeNode& trInfo)
{
	fid.Seek(0, file::begin);
	int nReadByte = fid.Read(&dcfHdr, sizeof(DCF_HEADER));
	string strLabel = "fileHeader";
	add_header_info_in_tree(dcfHdr, trInfo, strLabel);
	return nReadByte;
}

static int _read_one_chunk_info(file& fid, DCF_DATA& dcfData, int nChunkPlace, int nChunkIndex, TreeNode& trInfo)
{
	fid.Seek(nChunkPlace, file::begin);
	int nReadByte = fid.Read(&dcfData, sizeof(dcfData));
	string strName = "Chunk" + ftoa(nChunkIndex);
	add_chunk_info_in_tree(dcfData, trInfo, strName);
	return nReadByte;
}

static int _read_short_data(file& fid, vector<short>& vData, int nChunkDataPlace, int nChunkDataSize)
{
	fid.Seek(nChunkDataPlace, file::begin);
	
	if( fid.Read(vData, nChunkDataSize) != nChunkDataSize)
	{
		fid.Close();
		return -1;
	}
	
	return 0; 
}

static int _read_byte_data(file& fid, vector<byte>& vData, int nChunkDataPlace, int nChunkDataSize)
{
	fid.Seek(nChunkDataPlace, file::begin);
	if( fid.Read(vData, nChunkDataSize) != nChunkDataSize)
	{
		fid.Close();
		return -1;
	}
	return 0;
}

static int _read_int_data(file& fid, vector<int>& vData, int nChunkDataPlace, int nChunkDataSize)
{
	fid.Seek(nChunkDataPlace, file::begin);
	if( fid.Read(vData, nChunkDataSize) != nChunkDataSize)
	{
		fid.Close();
		return -1;
	}
	return 0;
}

static int _read_float_data(file& fid, vector<float>& vData, int nChunkDataPlace, int nChunkDataSize)
{
	fid.Seek(nChunkDataPlace, file::begin);
	if( fid.Read(vData, nChunkDataSize) != nChunkDataSize)
	{
		fid.Close();
		return -1;
	}
	return 0;
}

static int _read_double_data(file& fid, vector<double>& vData, int nChunkDataPlace, int nChunkDataSize)
{
	fid.Seek(nChunkDataPlace, file::begin);
	
	if( fid.Read(vData, nChunkDataSize) != nChunkDataSize)
	{
		fid.Close();
		return -1;
	}
	return 0;
}
/*
static int _read_one_chunk_data(file& fid, DCF_DATA& dcfData, vectorbase& vData, int nInternalType, int nChunkDataPlace, UINT nChunkDataSize)
{
	fid.Seek(nChunkDataPlace, file::begin);
	int nRet = file_read_data(fid, vData, nInternalType, nChunkDataSize);
	return nRet;
}
*/

///Cheney 2007-1-17 HANDLE_COLUMN_INFO
static string _get_data_type(WORD wDatatype)
{
	string strDataType; //empty string
	switch(wDatatype)
	{
	case DATA_TYPE_SHORT:
		strDataType = "Short";
		break;
	case DATA_TYPE_INT:
		strDataType = "Integer";
		break;
	case DATA_TYPE_FLOAT:
		strDataType = "Float";
		break;
	case DATA_TYPE_DOUBLE:
		strDataType = "Double";
		break;
	case DATA_TYPE_BYTE:
		strDataType = "Byte";
		break;
	default:
	}
	return strDataType;
}

static void _handle_column_info(Column& col, DCF_DATA& dcfData, LPCSTR lpcszFilename)
{
	//set user info
	string strChunkInfo = "ChunkInfo";
	Tree trChunkInfo;
	trChunkInfo.AddTextNode(lpcszFilename, "ImportFile");
	trChunkInfo.AddTextNode(ftoa(dcfData.Length), "BytesOfChunk");
	string strDataType = _get_data_type(dcfData.Datatype);
	trChunkInfo.AddTextNode(strDataType, "DataType");
	trChunkInfo.AddTextNode(ftoa(dcfData.scale), "Scale");
	trChunkInfo.AddTextNode(ftoa(dcfData.offset), "Offset");
	trChunkInfo.AddTextNode(ftoa(dcfData.EngRangeMin), "EngRangeMin");
	trChunkInfo.AddTextNode(ftoa(dcfData.EngRangeMax), "EngRangeMax");
	trChunkInfo.Enable = ENABLE_READ_ONLY;
	trChunkInfo.SetAttribute(STR_ATTRIB_BRANCH, GETNBRANCH_OPEN);
	///---Sim 02-05-2010 QA81-15063 ROLL_BACK_MOVE_COL_INFO_OUT_OF_USER_TREE
	//trChunkInfo.Show = 0; ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
	//bool bRet = set_user_info(col, strChunkInfo, trChunkInfo);
	bool bRet = fu_set_import_file_info(col, trChunkInfo, strChunkInfo, 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(col, lpcszFilename);
//
	//trChunkInfo.RemoveChild("ImportFile");
	//trChunkInfo.Show = 1;
	//set_import_file_info(col, trChunkInfo, "DCFChunkInfo");
	///---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
}
///end HANDLE_COLUMN_INFO

/// Hong 11/17/06 ADD_INFO_TO_COLUMNS
//static int _read_all_chunk_info_and_data(file& fid, Worksheet& wks, int nColBegin, int& nColEnd, TreeNode& trInfo, int nHeaderSize)
static int _read_all_chunk_info_and_data(file& fid, LPCSTR lpcszFilename, Worksheet& wks, int nColBegin, int& nColEnd, TreeNode& trInfo, int nHeaderSize, bool& bAddSampling)
/// end ADD_INFO_TO_COLUMNS
{
	int nFileSize = fid.SeekToEnd();
	int nCurrent = nHeaderSize;
	int nChunkIndex = 0;
	int nCol = nColBegin;
	int nMaxRows = 0;
	while(nCurrent < nFileSize)
	{
		DCF_DATA dcfData;
		int nRet;
		nChunkIndex++;
		int nReadByte = _read_one_chunk_info(fid, dcfData, nCurrent, nChunkIndex, trInfo);
		/// Hong 11/20/06 ADD_VALID_FILE_CHECK
		if( dcfData.Magic != FILE_CHUNK_ID)
			return DCF_FAIL_TO_READ_DATA;
		/// end ADD_VALID_FILE_CHECK
		int dataWidth, numData;
		int nChunkDataSize = dcfData.Length - sizeof(DCF_DATA);
		int nChunkDataPlace = nCurrent + sizeof(DCF_DATA);
		Column col;
		if(nCol <= nColEnd)
			col = wks.Columns(nCol);
		else
		{
			wks.AddCol();
			col = wks.Columns(nCol);
		}
		/// Hong 11/17/06 ADD_INFO_TO_COLUMNS
		string strLabel = "Chunk" + ftoa(nChunkIndex);
		col.SetLabel(strLabel);		
		string strUnit(dcfData.engunit, dcfData.engunitlength);
		string strComments(dcfData.Description, dcfData.descriptionlength);
		col.SetUnits(strUnit);
		col.SetComments(strComments);
		if( -1 == dcfData.AssignedTimeAxis )
		{
			wks.SetEvenSampling(dcfData.starttime, dcfData.TimeIncrementl, col, _L("ms"), _L("Time") );
			bAddSampling = true;
		}
		/// end ADD_INFO_TO_COLUMNS
		if(dcfData.TimeAxis == 0)
			col.SetType(OKDATAOBJ_DESIGNATION_Y);
		else
			col.SetType(OKDATAOBJ_DESIGNATION_X);
		
		///Cheney 2007-3-7 DATASET_NOW_ONLY_SUPPORT_DOUBLE_TYPE_COLUMN
		col.SetFormat(OKCOLTYPE_NUMERIC);
		///end DATASET_NOW_ONLY_SUPPORT_DOUBLE_TYPE_COLUMN
		
		switch(dcfData.Datatype)
		{
		case DATA_TYPE_SHORT:
			dataWidth  = 2;
			col.SetInternalData(FSI_SHORT);
			numData = nChunkDataSize/dataWidth;
			col.SetNumRows(numData);
			vector<short> vData;
			vData.SetSize(numData);
			nRet = _read_short_data(fid, vData, nChunkDataPlace, nChunkDataSize);
			if(nRet < 0)
				return nRet;
			vData = vData*dcfData.scale + dcfData.offset;
			vectorbase &vCol=col.GetDataObject();
			vCol = vData;
			break;
		case DATA_TYPE_INT:
			dataWidth  = 4;
			numData = nChunkDataSize/dataWidth;
			col.SetInternalData(FSI_LONG);
			col.SetNumRows(numData); 
			vector<int> vData;
			vData.SetSize(numData);
			nRet = _read_int_data(fid, vData, nChunkDataPlace, nChunkDataSize);
			if(nRet < 0)
				return nRet;
			vData = vData*dcfData.scale + dcfData.offset;
			vectorbase &vCol=col.GetDataObject();
			vCol = vData;
			break;
		case DATA_TYPE_FLOAT:
			dataWidth  = 4;
			numData = nChunkDataSize/dataWidth;
			col.SetInternalData(FSI_REAL);
			col.SetNumRows(numData); 
			vector<float> vData;
			vData.SetSize(numData);
			nRet = _read_float_data(fid, vData, nChunkDataPlace, nChunkDataSize);
			if(nRet < 0)
				return nRet;
			vData = vData*dcfData.scale + dcfData.offset;
			vectorbase &vCol=col.GetDataObject();
			vCol = vData;
			break;
		case DATA_TYPE_DOUBLE:
			dataWidth  = 8;
			numData = nChunkDataSize/dataWidth;
			col.SetInternalData(FSI_DOUBLE);
			col.SetNumRows(numData); 
			vector<double> vData;
			vData.SetSize(numData);
			nRet = _read_double_data(fid, vData, nChunkDataPlace, nChunkDataSize);
			if(nRet < 0)
				return nRet;
			vData = vData*dcfData.scale + dcfData.offset;
			vectorbase &vCol=col.GetDataObject();
			vCol = vData;
			break;
		case DATA_TYPE_BYTE:
			dataWidth  = 1;
			numData = nChunkDataSize/dataWidth;
			col.SetInternalData(FSI_BYTE);
			col.SetNumRows(numData); 
			vector<byte> vData;
			vData.SetSize(numData);
			nRet = _read_byte_data(fid, vData, nChunkDataPlace, nChunkDataSize);
			if(nRet < 0)
				return nRet;
			vData = vData*dcfData.scale + dcfData.offset;
			vectorbase &vCol=col.GetDataObject();
			vCol = vData;
			break;
		default:
		}
		nCurrent = nCurrent + dcfData.Length;
		///Cheney 2007-1-17 HANDLE_COLUMN_INFO
		_handle_column_info(col, dcfData, lpcszFilename);
		///end HANDLE_COLUMN_INFO
		nCol++;
		nMaxRows = nMaxRows < numData? numData : nMaxRows;
	}
	nColEnd = nCol;
	//wks.SetSize(-1, nColEnd);
	return 0;
}
	

/// Hong 11/17/06 ADD_INFO_TO_COLUMNS
//int readDCF(LPCSTR lpcszFilename, Worksheet& wks, TreeNode& trInfo, int nC1)
/// Hong 07/17/08 v8.0903 FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM
//int readDCF(LPCSTR lpcszFilename, Worksheet& wks, TreeNode& trInfo, bool& bAddSampling, int nC1)
int readDCF(LPCSTR lpcszFilename, Worksheet& wks, TreeNode& trInfo, bool& bAddSampling, int nC1, DWORD dwOptions) // = 0, DCF_RENAME_SHEET_BY_FILENAME
/// end FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM
/// end ADD_INFO_TO_COLUMNS
{
	file fid;
	
	//if the file failed to open
	if( !fid.Open(lpcszFilename, file::modeRead) )
	{
		fid.Close();
		return DCF_FAIL_TO_OPEN_FILE;
	}

	int nFileSize = fid.SeekToEnd();
	// If the file is too small to evaluate, just exit
	if(nFileSize < sizeof(DCF_HEADER))
	{
		fid.Close();
		return DCF_FILE_TOO_SMALL;
	}
	
	DCF_HEADER dcfHdr;
	int nReadByte;
	nReadByte = _read_file_info(fid, dcfHdr, trInfo);
	/// Hong 11/20/06 ADD_VALID_FILE_CHECK
	if( dcfHdr.Magic != FILE_TYPE_ID)
		return DCF_UNSUPPORTED_DATA_TYPE;
	/// end ADD_VALID_FILE_CHECK
	int nColEnd = wks.GetNumCols() - 1;
	/// Hong 11/17/06 ADD_INFO_TO_COLUMNS
	//int nRet = _read_all_chunk_info_and_data(fid, wks, nC1, nColEnd, trInfo, sizeof(DCF_HEADER));	
	bAddSampling = false;
	int nRet = _read_all_chunk_info_and_data(fid, lpcszFilename, wks, nC1, nColEnd, trInfo, sizeof(DCF_HEADER), bAddSampling);	
	/// end ADD_INFO_TO_COLUMNS

	/// Hong 07/17/08 v8.0903 FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM
	//wks.SetName(GetFileName(lpcszFilename, true));
	if ( DCF_RENAME_SHEET_BY_FILENAME & dwOptions )
		wks.SetName(GetFileName(lpcszFilename, true), OCD_ENUM_NEXT | OCD_ENUM_ADD_SEPARATOR);
	/// end FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM
	fid.Close();
	int nNumCols = nColEnd - nC1;
	return nRet;
}
