/*------------------------------------------------------------------------------*
 * File Name: BinImp.c		 													*
 * Creation: LY 11/4/2002														*
 * Purpose: OriginC Source C file												*
 * Copyright (c) Originlab Corp. 2002											*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	JCG 05/29/03 v7.0594 FIX_BUG_29_ORIGIN_SUSPENDS								*
 * EJP 06-23-2003 IMPROVE_IMPORT_SPEED											*
 * EJP 07-23-2003 v7.0631 QA70-4575 ADD_SKIPROWS_TO_BINIMP						*
 * EJP 08-07-2003 v7.0649 FIX_BINIMP_DATA_TYPE_MACROS							*
 * EJP 09-12-2003 v7.5693 QA70-5073 ADD_READROWS_TO_BINIMP						*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
#include <origin.h>      // main Origin C header, has precompiled file
#include "Wks_Utils.h"		// wuRenameToFileName function
#include "BinImp.h"      	// Function prototypes and non-localized constants 

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

#define IMPROVE_IMPORT_SPEED

////////////////////////////////////////////////////////////////////////////////////
//utility function prototype used by binary import
////////////////////////////////////////////////////////////////////////////////////

#define GET_NUMOFCOLS_FROM_FILE(_pbi) ( pbi->iPartial ? (pbi->iPartialC2 - pbi->iPartialC1+1)\
										: pbi->iNumColumns )
/// EJP 08-07-2003 v7.0649 FIX_BINIMP_DATA_TYPE_MACROS
///#define IS_TYPE_REAL(_type)  	((_type & BIP_FLAG_REAL) && (_type & BIP_FLAG_SIGNED))
///#define IS_TYPE_INT(_type)	 	(_type & BIP_TYPE_INT)
///#define IS_TYPE_UINT(_type)	 	(_type & BIP_TYPE_UINT)
///#define IS_TYPE_STRING(_type)	(_type & BIP_TYPE_STRING)
#define IS_TYPE_REAL(_type)  	(_type == BIP_TYPE_REAL)
#define IS_TYPE_INT(_type)	 	(_type == BIP_TYPE_INT)
#define IS_TYPE_UINT(_type)	 	(_type == BIP_TYPE_UINT)
#define IS_TYPE_STRING(_type)	(_type == BIP_TYPE_STRING)
/// end FIX_BINIMP_DATA_TYPE_MACROS

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#ifndef IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

//data type used in file import
typedef struct tagPOSITION_IDX {
	int nRow;
	int nCol;
} POSITION_IDX;

//global variables
static 	POSITION_IDX s_Pos;
static	LONG   	s_nFullRowSize = 0;
static	LONG	s_nBeforeRange = 0;
static	LONG	s_nAfterRange = 0;

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#else // IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

static 	LONG	s_nStartCol = 0;
static	LONG	s_nStartRow = 0;

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
static LONG s_lRowSizeInBytes;
static BYTE *s_pbyRowBuffer;
static LONG s_lBytesBeforePartial;
static int s_iFirstParamVecElement;
static int s_iFirstParamCount;
static int s_iLastParamVecElement;
static int s_iLastParamCount;

#endif // IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

////////////////////////////////////////////////////////////////////////////////////
// functions


/**
		Import binary data file into a worksheet based on the description struct filled in by user.
	Example:
		
	Parameters:
		wks		= The worksheet object
		lpcszFile	= Binary data file name
		pbi		= Struct defined in OC_types.h file to descript binary data format
	Return:
		Returns 0 to indicate import successful. Non-zero value means error
*/
int wuImportBinary(Worksheet &wks, LPCSTR lpcszFile, BINIMP *pbi)
{
	if( pbi->vParamType.GetSize() == 0 )
		return BIN_INVALID_ARGUMENT;

	if( !wks.IsValid() || lpcszFile == NULL || pbi == NULL )
	{
		printf("Invalid object in arguments\n");
		return BIN_INVALID_ARGUMENT;
	}

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#ifndef IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

	file fDataFile;
	int iRet = 0;
	
	//open the binary file
	if( !fDataFile.Open(lpcszFile, file::modeRead | file::typeBinary) )
	{
		printf("Error to open file %s\n", lpcszFile);
		return BIN_OPEN_FILE_FAILURE;
	}

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#endif // IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

/// EJP the worksheet is setup before import is called
///	//intialize the worksheet
///	if( !initWorksheet(wks, pbi) )
///	{
///		fDataFile.Close();
///		return BIN_FAIL_INIT_WKS;
///	}
	
	//rename the worksheet
	/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
	/*
	if( pbi->iRenameWks && !wuRenameToFileName(wks, lpcszFile) )
	{
		fDataFile.Close();
		return BIN_FAIL_RENAME_WKS;
	}
	*/
	/// EJP 06-27-2003 CENTRALIZE_RENAME_WKS_ON_IMPORT, Moved to fileimport.c
	///if( pbi->iRenameWks )
	///	wuRenameToFileName(wks, lpcszFile);
	/// end CENTRALIZE_RENAME_WKS_ON_IMPORT
	/// end IMPROVE_IMPORT_SPEED
	
/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#ifndef IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

	return importData(wks, fDataFile, pbi);

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#else // IMPROVE_IMPORT_SPEED
	return import_binary_file(wks, lpcszFile, *pbi);
#endif // IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED
}

////////////////////////////////////////////////////////////////////////////////////
//Implement utility functions to support binary import
////////////////////////////////////////////////////////////////////////////////////

#ifdef JUNK
/**
		initialize the worksheet: append columns, set type or rename worksheet if required
*/
static BOOL initWorksheet(Worksheet& wks, BINIMP *pbi)
{
	int iColsFromFile = GET_NUMOFCOLS_FROM_FILE(pbi);
	int iAddNumColumns = 0;
	int iNumCols = wks.GetNumCols();
	
	switch( pbi->iMode )
	{
	case ASCIMP_MODE_APPEND_COLS:
		s_nStartRow = 0; // Start from first row
		
		int iUpper = 0, iLower = 0, iCol = 0;
		
		foreach(Column cc in wks.Columns)
		{
			cc.GetRange(iLower, iUpper);
			if( iLower == 0 && iUpper == -1 )
				break;
			iCol++;
		}
		
		s_nStartCol = iCol;
		
		if( s_nStartCol + iColsFromFile > iNumCols )
			iAddNumColumns = s_nStartCol + iColsFromFile - iNumCols;
		
		break;

	case ASCIMP_MODE_APPEND_ROWS:
		s_nStartCol = 0; // Start from first column
		
		Dataset dd(wks, 0);
		
		if( dd.IsValid() )
			s_nStartRow = dd.GetSize();
		else
			s_nStartRow = 0;
		
		if( iColsFromFile > iNumCols )
			iAddNumColumns = iColsFromFile - iNumCols;
		
		break;

	case ASCIMP_MODE_REPLACE_DATA:
	default:
		//delete all columns from worksheet
		while( wks.DeleteCol(0) )
			;
		iAddNumColumns = iColsFromFile;
		s_nStartCol = 0; // Start from first column
		s_nStartRow = 0; // Start from first row
		break;
	}
	
	for( int ii = 0; ii < iAddNumColumns; ii++)
	{
		wks.AddCol();
		
		//if pbi->iAutoColType evaluates true
		//set the new column to the colsest data
		//type of the element data type
		if( pbi->iAutoColTypes )
		{
			int nIndexBase = pbi->iPartial ? pbi->iPartialC1 : 0;
			
			byte byDataType = 0;
			UINT nElementSize = getRowElementInfo(pbi, iNumCols + ii + nIndexBase, byDataType);
			
			Column colObj(wks, iNumCols + ii);
			if( IS_TYPE_REAL(byDataType) )  //real number
			{
				colObj.SetFormat(OKCOLTYPE_NUMERIC);
				
				if( nElementSize <= sizeof(float) )
					colObj.SetInternalData(FSI_REAL);
				else
					colObj.SetInternalData(FSI_DOUBLE);
			}
			else if( IS_TYPE_INT(byDataType) || IS_TYPE_UINT(byDataType) ) //int or UINT
			{
				colObj.SetFormat(OKCOLTYPE_NUMERIC);
				colObj.SetInternalData(FSI_LONG );
			}
			else if( IS_TYPE_STRING(byDataType) ) //string
			{
				colObj.SetFormat(OKCOLTYPE_TEXT);
			}			
		} //end auto type setting		
	}
	
	return TRUE;	
}
#endif JUNK

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#ifndef IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

/**
		initialize the global varaiables
*/
static void	initGlobalVar()
{
	//reset global variable
	s_Pos.nRow = 0;
	s_Pos.nCol = 0;
	
	s_nFullRowSize = 0;
	s_nBeforeRange = 0;
	s_nAfterRange  = 0;
}

/**
		import binary data. It will do that by setting each cell in the worksheet
*/
static int importData(Worksheet& wks, file& fDataFile, BINIMP *pbi)
{
	if( !fDataFile.IsOpen() || !wks.IsValid() )
		return BIN_INVALID_ARGUMENT;
	
	// init global variables
	initGlobalVar();

	// get file size
	///LONG nSize = getFileLength(fDataFile);
	LONG nSize = fDataFile.GetLength();

	// skip the header section
	fDataFile.Seek(pbi->iHeaderBytes, file::begin);
	
	int nRetCode = BIN_SUCCEED;
	POSITION_IDX wksPos;
	int nRowIndex = s_nStartRow;
	int nUpperBound = wks.GetNumRows();
	int iColsFromFile = GET_NUMOFCOLS_FROM_FILE(pbi);

	while( fDataFile.GetPosition() < nSize )
	{
		// increase worksheet row number, the step is 50
		if( nRowIndex == nUpperBound )
		{
			wks.AppendRows(50);
			nUpperBound = wks.GetNumRows();
		}
		
		// read data from file, fill in one row in worksheet 
		for( int ii = s_nStartCol; ii < s_nStartCol + iColsFromFile; ii++ )
		{
			// position for the cell in the worksheet
			wksPos.nRow = nRowIndex;
			wksPos.nCol = ii;
			
			nRetCode = setWksCellData(wks, wksPos, fDataFile, pbi); 
			if( BIN_SUCCEED != nRetCode )
			{
				fDataFile.Close();
				return nRetCode;
			}
		}	
		
		// check if partial import range is done
		if( isPartialImportDone(nRowIndex, pbi) )
			break;
		
		nRowIndex++;
	}	

	fDataFile.Close();
	return BIN_SUCCEED;
}

/**
		get number of bytes in a row upto the column index
		the nColIndex is the column index in the binary data file
*/
static LONG GetByteOffsetOfDataColumn(BINIMP &binimp, int nColIndex)
{
	LONG nBytes = 0;
	//vParamType, vParamSize and vParamCount vectors have the same size
	int  nSize = binimp.vParamType.GetSize();
	int  nColIndexInFile = 0;
	
	for( int ii=0; ii<nSize; ii++ )
	{
		int nTemp = binimp.vParamCount[ii];
		if( nColIndex < nColIndexInFile + nTemp )
			nTemp = nColIndex - nColIndexInFile;
		
		nColIndexInFile += nTemp;		
		nBytes += nTemp * binimp.vParamSize[ii];  
		
		if( nColIndexInFile == nColIndex )
			break;
	}
	
	return nBytes;
}

/**
		get an element data type and data type size
		the nIndex is the column index in the binary data file
*/
static UINT getRowElementInfo(BINIMP *pbi, int nIndex, byte& byType)
{
	UINT nBytes = 0;
	//vParamType, vParamSize and vParamCount vectors have the same size
	int  nSize = pbi->vParamType.GetSize();
	int  nColIndexInFile = 0;
	
	for( int ii=0; ii<nSize; ii++ )
	{
		int nTemp = pbi->vParamCount[ii];
		if( nIndex < nColIndexInFile + nTemp )
			nTemp = nIndex - nColIndexInFile;
		else
		{
			nColIndexInFile += nTemp;
			continue;
		}
			
		
		nColIndexInFile += nTemp;				 
		
		if( nColIndexInFile == nIndex )
		{
			nBytes = pbi->vParamSize[ii];
			byType = pbi->vParamType[ii];
			break;
		}
	}
	
	return nBytes;
}

/**
		check if partial import has done import for all range
*/
static bool isPartialImportDone(int nRowIndex, BINIMP *pbi)
{
	if( pbi->iPartial && pbi->iPartialR2 >= 0 )
		if( nRowIndex == (pbi->iPartialR2 - pbi->iPartialR1) )
			return true;
	return false;
}

/**
		set each worksheet cell
*/
static int setWksCellData(Worksheet& wks, POSITION_IDX& wksPos, file& fDataFile, BINIMP *pbi)
{
	if( !fDataFile.IsOpen() || !wks.IsValid() )
		return BIN_INVALID_ARGUMENT;
	
	int nRowInFile = 0, nColInFile = 0;
	int nStartPartialImport = 0;
		
	int iRowInWks = wksPos.nRow - s_nStartRow;
	int iColInWks = wksPos.nCol - s_nStartCol;
	
	POSITION_IDX filePos;
	filePos.nRow = iRowInWks;
	filePos.nCol = iColInWks;
	
	if( pbi->iPartial ) //compute partial import parameters
	{
		//initial three globale variables to indicate the block size in partial import
		//s_nFullRowSize denotes bytes of a whole row
		//s_nBeforeRange denotes bytes before the import col index in a row
		//s_nAfterRange denotes bytes after the import col index in a row
		if( 0 == s_nFullRowSize )
			s_nFullRowSize = GetByteOffsetOfDataColumn(*pbi, pbi->iNumColumns);		
		if( 0 == s_nBeforeRange )
			s_nBeforeRange = GetByteOffsetOfDataColumn(*pbi, pbi->iPartialC1);		
		if( 0 == s_nAfterRange )
			s_nAfterRange = s_nFullRowSize - GetByteOffsetOfDataColumn(*pbi, pbi->iPartialC2 + 1);
		
		//calculate row and col indexes in the partial impoer range
		filePos.nRow = iRowInWks + pbi->iPartialR1;
		filePos.nCol = iColInWks + pbi->iPartialC1;
		
		//seek to the element before right the first element in the import range
		//the import range indexes are 0 based			
		if( iRowInWks == 0 && iColInWks == 0 && nStartPartialImport == 0)
		{
			fDataFile.Seek(s_nFullRowSize*pbi->iPartialR1 + s_nBeforeRange, file::current);
			
			nStartPartialImport = 1;			
		}		
		//seek to the beginning position of next row
		else if( filePos.nRow == s_Pos.nRow + 1 && filePos.nCol == pbi->iPartialC1 )
		{
			fDataFile.Seek(s_nAfterRange + s_nBeforeRange, file::current);
		}			
	}	
	
	return setCellFromFile(wks, wksPos, fDataFile, filePos, pbi);
}

/**
		get the file size
*/
static LONG getFileLength(file& fDataFile)
{
	if( !fDataFile.IsOpen() )
		return 0;
	
	LONG nCurPos = fDataFile.GetPosition();	
	LONG nSize = fDataFile.SeekToEnd();
	
	//restore previous position
	fDataFile.Seek(nCurPos, file::begin);	
	return nSize;
}

/**
		read data from file by its logical row and col position
		then, put into the corresponding cell in the worksheet
*/
static int setCellFromFile(Worksheet& wks, POSITION_IDX& wksPos, file& fDataFile, POSITION_IDX& filePos, BINIMP *pbi)
{
	if( !fDataFile.IsOpen() || !wks.IsValid() )
		return BIN_INVALID_ARGUMENT;
	
	int nRetCode = BIN_SUCCEED;
	UINT nElementSize = 0;
	byte byDataType = 0;
	BOOL bLittleEndian = !(pbi->iBigEndian);
	
	nElementSize = getRowElementInfo(pbi, filePos.nCol, byDataType);
	
	if( IS_TYPE_REAL(byDataType) )	//real number
	{
		double dData = 0;
		float  fData = 0;
		if( sizeof(float) == nElementSize )
		{
			fDataFile.ReadFloat(&fData, nElementSize, 1, bLittleEndian);
			wks.SetCell(wksPos.nRow, wksPos.nCol, fData);
		}
		else if( sizeof(double) == nElementSize )
		{
			fDataFile.ReadFloat(&dData, nElementSize, 1, bLittleEndian);
			wks.SetCell(wksPos.nRow, wksPos.nCol, dData);
		}
		else
			nRetCode = BIN_INVALID_SIZE;
	}
	else if( IS_TYPE_INT(byDataType) || IS_TYPE_UINT(byDataType) ) //int or UINT
	{
		int nData = 0;
		fDataFile.ReadInt(&nData, nElementSize, 1, bLittleEndian);
		
		wks.SetCell(wksPos.nRow, wksPos.nCol, nData);
	}
	else if( IS_TYPE_STRING(byDataType) ) //string
	{
		string strResult;
		char *pRet = strResult.GetBuffer(nElementSize + 1); 
		
		int nRead = fDataFile.Read(pRet, nElementSize);
		*(pRet+nRead) = '\0';
		strResult.ReleaseBuffer();
		
		/// JCG 05/29/03 v7.0594 FIX_BUG_29_ORIGIN_SUSPENDS
		// need check the read result...
		if(0 != nRead)
			wks.SetCell(wksPos.nRow, wksPos.nCol, strResult);
		else
			nRetCode = BIN_INVALID_SIZE;
		/// end FIX_BUG_29_ORIGIN_SUSPENDS

	}
	else
		nRetCode = BIN_INVALID_TYPE;	
	
	
	//save the current reading logical row and col number in data file
	s_Pos.nRow = filePos.nRow;
	s_Pos.nCol = filePos.nCol;
	
	return nRetCode;	
}
/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#endif // !IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED

bool ReadBinaryHeaderParam(string &strDest, file &fil, int nType, int nOffset, int nSize, bool bLittleEndian)
{
	if( !fil.IsOpen() )
		return false;
	if( fil.Seek(nOffset, file::begin) != nOffset )
		return false;

	string strFormat;
	
	switch( nType )
	{
	case BIP_TYPE_REAL:
		strFormat = "%g";
		switch( nSize )
		{
		case 4:
			float f;
			fil.ReadFloat(&f, 4, 1, bLittleEndian);
			strDest.Format(strFormat, f);
			break;
		case 8:
			double d;
			fil.ReadFloat(&d, 8, 1, bLittleEndian);
			strDest.Format(strFormat, d);
			break;
		default:
			return false;
		}
		break;

	case BIP_TYPE_INT:
	case BIP_TYPE_UINT:
		strFormat = (BIP_TYPE_INT == nType ? "%d" : "%u");
		switch( nSize )
		{
		case 1:
			char n8;
			fil.Read(&n8, 1);
			strDest.Format(strFormat, n8);
			break;
		case 2:
			short n16;
			fil.ReadInt(&n16, 2, 1, bLittleEndian);
			strDest.Format(strFormat, n16);
			break;
		case 4:
			int n32;
			fil.ReadInt(&n32, 4, 1, bLittleEndian);
			strDest.Format(strFormat, n32);
			break;
		default:
			return false;
		}
		break;

	case BIP_TYPE_STRING:
		LPSTR lpstr = strDest.GetBufferSetLength(nSize + 1); // +1 for null terminator
		if( lpstr )
		{
			fil.Read(lpstr, nSize);
			lpstr[nSize] = 0; // be sure string is terminated
			strDest.ReleaseBuffer();
		}
		break;
		
	default:
		return false;
	}
	
	return true;
}

/// EJP 06-23-2003 IMPROVE_IMPORT_SPEED
#ifdef IMPROVE_IMPORT_SPEED

static int import_binary_file(Worksheet& wks, LPCSTR lpcstrFile, BINIMP& binimp)
{
	init_globals(binimp);
	init_wks_starting_col_and_row(wks, binimp);
	
	if( binimp.iPartial )
	{
		if( (binimp.iPartialC1 > binimp.iPartialC2 && binimp.iPartialC2 != -1) ||
			(binimp.iPartialC1 >= get_column_count(binimp)) ||
			(binimp.iPartialR1 > binimp.iPartialR2 && binimp.iPartialR2 != -1) )
		{
			return 1; // invalid range specified
		}
	}

	file fil;
	if( fil.Open(lpcstrFile, file::modeRead | file::typeBinary) )
	{
		// Get the number of bytes in each row of data.
		s_lRowSizeInBytes = get_binimp_row_size(binimp);
		if( s_lRowSizeInBytes )
		{
			// Allocate a buffer to hold a row of data
			s_pbyRowBuffer = (BYTE*)malloc(s_lRowSizeInBytes);
			if( s_pbyRowBuffer )
			{
				// Skip header bytes.
				fil.Seek(binimp.iHeaderBytes, file::begin);

				int iDataRow = 0; // 0=first row of data

				// If partial import then skip unwanted rows.
				if( binimp.iPartial && binimp.iPartialR1 > 0 )
				{
					fil.Seek(binimp.iPartialR1 * s_lRowSizeInBytes, file::current);
					iDataRow = binimp.iPartialR1;
				}
				
				// Set starting wks row.
				int iWksRow = s_nStartRow;
				
				int iSkipRows = 0; /// EJP 07-23-2003 v7.0631 QA70-4575 ADD_SKIPROWS_TO_BINIMP
				int iReadRows = binimp.iReadRows; /// EJP 09-12-2003 v7.5693 QA70-5073 ADD_READROWS_TO_BINIMP

				// Read data rows.
				while( fil.Read(s_pbyRowBuffer, s_lRowSizeInBytes) == s_lRowSizeInBytes )
				{
					/// EJP 07-23-2003 v7.0631 QA70-4575 ADD_SKIPROWS_TO_BINIMP
					if( iSkipRows )
					{
						iSkipRows--;
						/// EJP 09-12-2003 v7.5693 ADD_READROWS_TO_BINIMP
						if( 0 == iSkipRows )
							iReadRows = binimp.iReadRows;
						/// end ADD_READROWS_TO_BINIMP
					}
					else // iSkipRows == 0
					{
					/// end ADD_SKIPROWS_TO_BINIMP
						// Put row into worksheet.
						row_buffer_to_wks(wks, iWksRow, s_pbyRowBuffer, binimp);
						iWksRow++; // Next worksheet row.
					/// EJP 07-23-2003 v7.0631 QA70-4575 ADD_SKIPROWS_TO_BINIMP

						/// EJP 09-12-2003 v7.5693 QA70-5073 ADD_READROWS_TO_BINIMP
						///iSkipRows = binimp.iSkipRows; // reset iSkipRows
						if( binimp.iSkipRows )
						{
							iReadRows--;
							if( 0 == iReadRows )
								iSkipRows = binimp.iSkipRows; // reset iSkipRows
						}
						/// end ADD_READROWS_TO_BINIMP
					}
					/// end ADD_SKIPROWS_TO_BINIMP
					
					// If partial import and we just did the last row...
					if( binimp.iPartial && iDataRow == binimp.iPartialR2 )
						break; // Partial import is done.
					
					iDataRow++; // Next row of data
				}
		
				free(s_pbyRowBuffer);
			}
		}
		fil.Close();
	}
	return 0;
}

static int get_column_count(BINIMP &binimp)
{
	int iCount = 0;
	for( int i = 0; i < binimp.vParamCount.GetSize(); i++ )
		iCount += binimp.vParamCount[i];
	return iCount;
}

static LONG get_binimp_row_size(BINIMP& binimp)
{
	LONG lSize = 0;
	for( int i = 0; i < binimp.vParamType.GetSize(); i++ )
		lSize += (binimp.vParamSize[i] * binimp.vParamCount[i]);
	return lSize;
}

static bool get_param_info_for_col(int& iParamVecElement, int& iParamCount, BINIMP& binimp, int iCol)
{
	int iCurrCol = 0;
	for( int iElement = 0; iElement < binimp.vParamCount.GetSize(); iElement++ )
	{
		for( int iCount = 0; iCount < binimp.vParamCount[iElement]; iCount++ )
		{
			if( iCurrCol == iCol )
			{
				iParamVecElement = iElement;
				iParamCount = iCount;
				return true;
			}
			iCurrCol++;
		}
	}
	return false;
}

static int get_num_bytes_before_col(BINIMP &binimp, int iCol)
{
	int iBytesBeforeCol = 0, iCurrCol = 0;

	for( int iElement = 0; iElement < binimp.vParamCount.GetSize(); iElement++ )
	{
		for( int iCount = 0; iCount < binimp.vParamCount[iElement]; iCount++ )
		{
			if( iCurrCol == iCol )
				return iBytesBeforeCol;
			iBytesBeforeCol += binimp.vParamSize[iElement];
			iCurrCol++;
		}
	}
	return 0;
}

static void init_globals(BINIMP &binimp)
{
	s_lRowSizeInBytes = 0;
	s_pbyRowBuffer = NULL;
	
	if( binimp.iPartial )
	{
		get_param_info_for_col(s_iFirstParamVecElement, s_iFirstParamCount, binimp, binimp.iPartialC1);
		if( binimp.iPartialC2 >= 0 )
			get_param_info_for_col(s_iLastParamVecElement, s_iLastParamCount, binimp, binimp.iPartialC2);
		else
		{
			s_iLastParamVecElement = -1;
			s_iLastParamCount = -1;
		}
		
		s_lBytesBeforePartial = get_num_bytes_before_col(binimp, binimp.iPartialC1);
	}
	else
	{
		s_iFirstParamVecElement = 0;
		s_iFirstParamCount = 0;
		
		s_iLastParamVecElement = -1;
		s_iLastParamCount = -1;
		
		s_lBytesBeforePartial = 0;
	}
}

static void init_wks_starting_col_and_row(Worksheet& wks, BINIMP& binimp)
{
	switch( binimp.iMode )
	{
	case ASCIMP_MODE_REPLACE_DATA:
		s_nStartCol = 0;
		s_nStartRow = 0;
		break;
	case ASCIMP_MODE_APPEND_COLS:
		s_nStartCol = FindEmptyColumn(wks);
		s_nStartRow = 0;
		break;
	case ASCIMP_MODE_APPEND_ROWS:
		s_nStartCol = 0;
		Dataset dd(wks, 0);
		if( dd.IsValid() )
			s_nStartRow = dd.GetSize();
		else
			s_nStartRow = 0;
		break;
	}
}

static int row_buffer_to_wks(Worksheet& wks, int iWksRow, BYTE *pbyRowBuffer, BINIMP& binimp)
{
	int iParamCount = 0;

	if( binimp.iPartial )
	{
		// Skip bytes for unwanted columns
		pbyRowBuffer += s_lBytesBeforePartial;
		iParamCount = s_iFirstParamCount;
	}

	int iWksCol = s_nStartCol;
	
	for( int iParamVecElement = s_iFirstParamVecElement; iParamVecElement < binimp.vParamType.GetSize(); iParamVecElement++ )
	{
		while( iParamCount < binimp.vParamCount[iParamVecElement] )
		{
			// Set worksheet cell
			set_worksheet_cell(wks, iWksRow, iWksCol, pbyRowBuffer, binimp.vParamType[iParamVecElement], binimp.vParamSize[iParamVecElement], binimp.iBigEndian);

			// Advance pointer to data for next paramter.
			pbyRowBuffer += binimp.vParamSize[iParamVecElement];
			
			// If we have set the cell of the last column.
			if( iParamVecElement == s_iLastParamVecElement && iParamCount == s_iLastParamCount )
				return 0; // we are done
			
			// Next parameter in vector element.
			iParamCount++;

			// Next wks column.
			iWksCol++;
		}
		iParamCount = 0;
	}
	
	return 0;
}

static int set_worksheet_cell(Worksheet &wks, int iRow, int iCol, BYTE *pbyData, int iDataType, int iDataSize, bool bBigEndian)
{
	if( IS_TYPE_REAL(iDataType) )
	{
		if( bBigEndian )
			reverse_bytes(pbyData, iDataSize);
		
		if( sizeof(float) == iDataSize )
		{
			float fData = *((float*)pbyData);
			wks.SetCell(iRow, iCol, fData);
		}
		else if( sizeof(double) == iDataSize )
		{
			double dData = *((double*)pbyData);
			wks.SetCell(iRow, iCol, dData);
		}
	}
	else if( IS_TYPE_INT(iDataType) )
	{
		if( bBigEndian )
			reverse_bytes(pbyData, iDataSize);

		int iData = 0;
		switch( iDataSize )
		{
		case sizeof(char):
			iData = *((char*)pbyData);
			break;
		case sizeof(short):
			iData = *((short*)pbyData);
			break;
		case sizeof(long):
			iData = *((long*)pbyData);
			break;
		}
		
		wks.SetCell(iRow, iCol, iData);
	}
	else if( IS_TYPE_UINT(iDataType) )
	{
		if( bBigEndian )
			reverse_bytes(pbyData, iDataSize);

		DWORD dwData = 0;
		switch( iDataSize )
		{
		case sizeof(char):
			dwData = *((BYTE*)pbyData);
			break;
		case sizeof(short):
			dwData = *((WORD*)pbyData);
			break;
		case sizeof(long):
			dwData = *((DWORD*)pbyData);
			break;
		}
		
		wks.SetCell(iRow, iCol, dwData);
	}
	else if( IS_TYPE_STRING(iDataType) )
	{
		string strData;
		LPSTR lpstr = strData.GetBuffer(iDataSize + 1);
		if( lpstr )
		{
			memcpy(lpstr, pbyData, iDataSize);
			*(lpstr + iDataSize) = 0; // be sure it is null terminated
			strData.ReleaseBuffer();
			wks.SetCell(iRow, iCol, strData);
		}
	}
	return 0;
}

static void reverse_bytes(BYTE *pbyData, int iSize)
{
	BYTE by;

	for( int i = 0; i < iSize/2; i++ )
	{
		by = *(pbyData + i);
		*(pbyData + i) = *(pbyData + iSize - i - 1);
		*(pbyData + iSize - i - 1) = by;
	}
}
#endif // IMPROVE_IMPORT_SPEED
/// end IMPROVE_IMPORT_SPEED
