/*------------------------------------------------------------------------------*
 * File Name: kgraph.c															*
 * Creation: GRD 2002.08.09														*
 * Purpose: Origin C file														*
 * Copyright (c) OriginLab Corp. 2002, 2003, 2004, 2005, 2006, 2007				*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*	
 *	Cheney 2006-6-26 REWRITE													*
 *	Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA							*
 *	Hong 07/17/08 v8.0903 FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM*
 *	Hong 06/26/09 QA80-13841 FIX_SMALL_VALID_FILE_IS_CONSIDER_AS_INVALID_FILE	*
 *	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			*
 *------------------------------------------------------------------------------*/
 
// Includes
#include <origin.h>
#include "kgraph.h"
#include <..\Originlab\fu_utils.h> ///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE

#define	SIZE_OF_FLOAT	 4
#define	SIZE_OF_DOUBLE	 8
#define	DATEOFFSET1 	 2415018; 	// Dec. 30, 1899
#define	DATEOFFSET2 	 2416480; 	// Jan. 1, 1904
#define DATAINFOPOS		 512
#define SKIPCOLNAMEINFO  40
/// Hong 06/26/09 QA80-13841 FIX_SMALL_VALID_FILE_IS_CONSIDER_AS_INVALID_FILE
#ifdef			__FIX_KGRAPH_SMALL_VALID_FAIL_FAIL_IMPORT__
#define FILESIZELOWBOUND 876	// see attached file size in tracker #13841
#else			//__FIX_KGRAPH_SMALL_VALID_FAIL_FAIL_IMPORT__
#define FILESIZELOWBOUND 1000
#endif			//__FIX_KGRAPH_SMALL_VALID_FAIL_FAIL_IMPORT__
/// end FIX_SMALL_VALIDATE_FILE_IS_CONSIDER_AS_INVALID_FILE
#define COLUMNUPBOUND	 1000
#define ITEMCOUNT		 1
#define	TBLOCKSIZE1 	 21
#define	TBLOCKSIZE2 	 51
#define SKIPDECRIPSTF	 6
#define BUFSIZE1		 256
#define BUFSIZE2		 128
#define DATECONST	     86400
#define CHKBYTE			 4

// 1,2,4,8, default is 8
#pragma pack(push,1)

typedef struct tagExtra
{
	char	ColWidth;	// Column Width
	char	NumFormat;	// Numeric Format
	char	DateFormat;	// Date Format (Just use 2 - Windows Long)
	char	TimeFormat;	// Time Format (Just use 4 - hh:mm:ss.##)
	char	NumDigits;	// Number of Digits
	char	Unk1;		// 
	char	Unk2;		// 
	char	BitFlags;	// 0:?TrailZero/Off?, 1:?Commas/Off?, 2:Time/Date, 3:Euro/Amer, 4-7:??
} Extra;

#pragma pack(pop)

enum{
	FLOAT_TYPR = 0,
	TEXT_TYPR,
	DATE_TYPR,
	DOUBLE_TYPR,
	LONG_TYPR
};

/// Hong 07/17/08 v8.0903 FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM
//int readkgraph(LPCSTR lpcszFilename, Worksheet& wks, TreeNode& trInfo, int nC1)
int readkgraph(LPCSTR lpcszFilename, Worksheet& wks, TreeNode& trInfo, int nC1, DWORD dwOptions) // = 0, KGRAPH_RENAME_SHEET_BY_FILENAME
/// end FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM
{
	file fid;
	
	//if the file failed to open
	if( !fid.Open(lpcszFilename, file::modeRead) )
	{
		fid.Close();
		return KG_ERR_FAIL_TO_OPEN_FILE;
	}

	int nFileSize = fid.SeekToEnd();
	// If the file is too small to evaluate, just exit
	if(nFileSize < FILESIZELOWBOUND)
	{
		fid.Close();
		return KG_ERR_FILE_TOO_SMALL;
	}
	
	int nNumCols; 
	int nVersion;
	if(_read_file_info(fid, nNumCols, nVersion) < 0)
	{
		return KG_ERR_FAIL_TO_READ_FILE_INFO;
	}

	
	fid.Seek(DATAINFOPOS, file::begin);

	int nColEnd = nNumCols + nC1;
	//for( int nLoop = wks.GetNumCols(); nLoop < nColEnd; nLoop = wks.GetNumCols() ) 
	//{
		//wks.AddCol();
	//}
	if(nColEnd > wks.GetNumCols())
		wks.SetSize(-1, nColEnd);

	_read_data_info(fid, nC1, nColEnd, wks, nVersion);
	
	// Skip this column name information - Seem to be 'short' in some cases
	fid.Seek(SKIPCOLNAMEINFO * nNumCols, file::current);
	
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	int nColMaxRows = 0; //for set wks size
	//if(_read_data(fid, nC1, nColEnd, wks, nVersion) < 0)
	if(_read_data(fid, lpcszFilename, nC1, nColEnd, nColMaxRows, wks, nVersion) < 0)
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA
	{
		return KG_ERR_FAIL_TO_READ_DATA;
	}
		
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	//wks.SetSize(-1, nColEnd);
	
	/// Hong 07/17/08 v8.0903 FIX_SOME_IMPORT_HAVE_DUPLICATE_SHEET_NAME_NO_AUTO_ENUM
	//wks.SetName(GetFileName(lpcszFilename, TRUE));
	if ( KGRAPH_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
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA

	fid.Close();
	return nNumCols;
}


//read file infomation
static int _read_file_info(file& fid, int& nNumCols, int& nVersion)
{
	fid.SeekToBegin();
	int nLength = 2;
	int nByteRead = fid.ReadInt(&nVersion, nLength, ITEMCOUNT, false); // MAC byte order on either PC or MAC
	nByteRead = fid.ReadInt(&nNumCols, nLength, ITEMCOUNT, false);   
	
	if(nNumCols == 0 || nNumCols > COLUMNUPBOUND) // Stated limit for v3.5.2
	{
		fid.Close();
		return KG_ERR_NUMBER_OF_COLUMNS_INCONSISTENT;
	}
	
	return 0;
}


//read data infomation
static void _read_data_info(file& fid, int nColBegin, int nColEnd, Worksheet& wks, int nVersion)
{	
	int nByteRead;
	
	// Read the Number Of Rows for each column
	int	nNumRows;
	int nLength = 2;
	for(int nThisCol = nColBegin; nThisCol < nColEnd; nThisCol++)
	{
		if(nVersion > 8)
		{
			nByteRead = fid.ReadInt(&nNumRows, sizeof(nNumRows), ITEMCOUNT, false);
		}
		else
		{
			nByteRead = fid.ReadInt(&nNumRows, nLength, ITEMCOUNT, false);
		}
		
		#if _OC_VER >= 0x0800
		Column cc;
		cc.Attach(wks, nThisCol);
		cc.SetInternalData(FSI_DOUBLE);
		#endif //_OC_VER >= 0x0800
		wks.SetCell(0, nThisCol, (double)nNumRows);
	}
	
	// Read the Column Datatype for each column
	int	nColType;
	for(nThisCol = nColBegin; nThisCol < nColEnd; nThisCol++)
	{
		nByteRead = fid.ReadInt(&nColType, nLength, ITEMCOUNT, false);
		wks.SetCell(1, nThisCol, (double)nColType);
	}
}


//read data
///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
//static int _read_data(file& fid, int nColBegin, int nColEnd, Worksheet& wks, int nVersion)
static int _read_data(file& fid, LPCSTR lpcszFilename, int nColBegin, int nColEnd, int& nColMaxRows, Worksheet& wks, int nVersion)
///end SET_WKS_SIZE_AFTER_IMPORT_DATA
{
	for(int nThisCol = nColBegin; nThisCol < nColEnd; nThisCol++)
	{
		int nNumRows = wks.Cell(0, nThisCol);
		int nColType =  wks.Cell(1, nThisCol);
		
		switch(nColType)
		{
		case FLOAT_TYPR:	
			_read_float_data(fid, wks, nThisCol, nNumRows);
			_read_mask(fid, wks, nThisCol, nNumRows);
			break;
		case TEXT_TYPR:	
			_read_text_data(fid, wks, nThisCol, nNumRows, nVersion);
			break;
		case DATE_TYPR:
			_read_date_data(fid, wks, nThisCol, nNumRows, nVersion);
			_read_mask(fid, wks, nThisCol, nNumRows);
			break;
		case DOUBLE_TYPR:	
			_read_double_data(fid, wks, nThisCol, nNumRows);
			_read_mask(fid, wks, nThisCol, nNumRows);
			break;
		case LONG_TYPR:	 
			_read_long_data(fid, wks, nThisCol, nNumRows);
			_read_mask(fid, wks, nThisCol, nNumRows);
			break;
		default: // Unknown
			fid.Close();
			return KG_ERR_UNSUPPORTED_DATA_TYPE;
		}
		_read_extra(fid, wks, nThisCol);
		_read_name(fid, wks, nThisCol);
		///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
		nColMaxRows = nColMaxRows < nNumRows ? nNumRows : nColMaxRows;
		///end SET_WKS_SIZE_AFTER_IMPORT_DATA
		
		///---Sim 02-04-2010 QA81-15063 MOVE_IMP_FILE_INFO_OUT_FROM_COL_USER_INFO_TREE
		//string strColumnInfo = "ColumnInfo";
		//Tree trColumnInfo;
		//trColumnInfo.AddTextNode(lpcszFilename, "ImportFile");
		//trColumnInfo.Enable = ENABLE_READ_ONLY;
		//trColumnInfo.SetAttribute(STR_ATTRIB_BRANCH, GETNBRANCH_OPEN);
		//bool bRet = set_user_info(wks.Columns(nThisCol), strColumnInfo, trColumnInfo);
		fu_set_import_file_name_info(wks.Columns(nThisCol), lpcszFilename, IMPORT_INFO_TO_USER_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(wks.Columns(nThisCol), lpcszFilename);
		///---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 _OC_VER < 0x0800
	LT_execute("wks.labels();");
	#endif //_OC_VER < 0x0800
	
	return 0;
}


static void _read_float_data(file &fid, Worksheet& wks, int nTheColumn, int nNumRows)
{
	wks.Columns(nTheColumn).SetFormat(OKCOLTYPE_NUMERIC);
	
	vector<float> vect;
	vect.SetSize(nNumRows);	
	int nBytesRead = fid.ReadFloat(vect, SIZE_OF_FLOAT, nNumRows, FALSE);
	
	Dataset	ds;
	ds.Attach(wks, nTheColumn);
	ds.SetSize(nNumRows);
	ds = vect;
}


static void _read_double_data(file &fid, Worksheet& wks, int nTheColumn, int nNumRows)
{
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	_set_column_size(wks, nTheColumn, nNumRows);
	Dataset	ds(wks.Columns(nTheColumn));
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA
	wks.Columns(nTheColumn).SetFormat(OKCOLTYPE_NUMERIC);
	
	vector<double>	vect;
	vect.SetSize(nNumRows);
	int nBytesRead = fid.ReadFloat(vect, SIZE_OF_DOUBLE, nNumRows, FALSE);
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	//Dataset	ds;
	//ds.Attach(wks, nTheColumn);
	//ds.SetSize(nNumRows);
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA
	ds = vect;
}


static void _read_long_data(file &fid, Worksheet& wks, int nTheColumn, int nNumRows)
{
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	_set_column_size(wks, nTheColumn, nNumRows);
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA
	wks.Columns(nTheColumn).SetFormat(OKCOLTYPE_NUMERIC);

	int nValue;
	for(int nLoop = 0; nLoop < nNumRows; nLoop++)
	{
		int iByteRead = fid.ReadInt(&nValue, sizeof(nValue) , ITEMCOUNT, false);		
		wks.SetCell(nLoop, nTheColumn, (double) nValue);
	}
}


static void _read_text_data(file &fid, Worksheet& wks, int nTheColumn, int nNumRows, int nVersion)
{
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	_set_column_size(wks, nTheColumn, nNumRows);
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA
	wks.Columns(nTheColumn).SetFormat(OKCOLTYPE_TEXT);
		
	// Skip over descriptive stuff
	fid.Seek(nNumRows * SKIPDECRIPSTF, file::current);
	
	// Read text info
	char Buffer[BUFSIZE1];
	for(int nLoop = 0; nLoop < nNumRows; nLoop++)
	{
		int	nBytesRead;
		if(nVersion > 11)
		{
			nBytesRead = fid.Read(&Buffer, TBLOCKSIZE2);
		}
		else
		{
			nBytesRead = fid.Read(&Buffer, TBLOCKSIZE1);
		}
		wks.SetCell(nLoop, nTheColumn, Buffer);
	}
}


static void _read_mask(file &fid, Worksheet& wks, int nTheColumn, int nNumRows)
{
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	_set_column_size(wks, nTheColumn, nNumRows);
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA
	
	int	nMask;
	int nLength = 2;
	for(int nLoop = 0; nLoop < nNumRows; nLoop++)
	{
		int nByteRead = fid.ReadInt(&nMask, nLength, ITEMCOUNT, false);
		// This information is not used yet
	}
}


static void _read_extra(file &fid, Worksheet& wks, int nTheColumn)
{
	Extra		Hdr;

	fid.Read(&Hdr, sizeof(Hdr));
	wks.Columns(nTheColumn).SetWidth(Hdr.ColWidth);
	if(wks.Columns(nTheColumn).GetFormat() == OKCOLTYPE_DATE)
	{
		if(Hdr.BitFlags & CHKBYTE)
		{
			wks.Columns(nTheColumn).SetFormat(OKCOLTYPE_TIME);
			wks.Columns(nTheColumn).SetSubFormat(OKCOLTYPE_DATE);	// For now
		}
		else
		{
			wks.Columns(nTheColumn).SetSubFormat(OKCOLTYPE_TEXT);	// For now
		}
	}
}


static void _read_name(file &fid, Worksheet& wks, int nTheColumn)
{
	char Buffer[BUFSIZE2];
	// Names can be read elsewhere, but they seem shortened in some cases,
	// so I'll read them here
	int nBytesRead = fid.Read(&Buffer, BUFSIZE2);
	wks.Columns(nTheColumn).SetLabel(Buffer);
}


static void	_read_date_data(file& fid, Worksheet& wks, int nThisCol, int nNumRows, int nVersion)
{
	///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
	_set_column_size(wks, nThisCol, nNumRows);
	///end SET_WKS_SIZE_AFTER_IMPORT_DATA
	
	if(nVersion < 8)
	{
		_read_float_data(fid, wks, nThisCol, nNumRows);
		// This is days from 1900
		Dataset	ds(wks.Columns(nThisCol));
		ds += DATEOFFSET1;
	}
	else
	{
		_read_double_data(fid, wks, nThisCol, nNumRows);
		// This is seconds from 1904
		Dataset	ds(wks.Columns(nThisCol));
		ds /= DATECONST;
		ds += DATEOFFSET2;
	}
	wks.Columns(nThisCol).SetFormat(OKCOLTYPE_DATE);
}

///Cheney 2006-12-27 SET_WKS_SIZE_AFTER_IMPORT_DATA
static void _set_column_size(Worksheet& wks, int nColIndex, int nSize)
{
	Dataset	ds(wks.Columns(nColIndex));
	ds.SetSize(nSize);
}
///end SET_WKS_SIZE_AFTER_IMPORT_DATA




