/*------------------------------------------------------------------------------*
 * File Name:	CSVFile		 													*
 * Creation: 	Hong	2006-12-21												*
 * Purpose: OriginC Source C file												*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 * Sim 03-19-2007 PARTIAL_IMPORT												*
 * Sim 03-20-2007 FIX_CSV_DATA_ERROR_FOR_REIMPORT								*
 * Hong 03/30/07 v8.0593 ADD_HEADER_LINE_OPTION									*
 * Hong 03/31/07 v8.0593 CLEAN_COMMENTS_BEFFORE_BEFORE_APPED					*
 * Hong 03/31/07 v8.0593 FIX_ERROR_DUPLICATE_READ_HEADER_LINES					*
 *	Hong 04/24/07 v8.0607 FIX_ERROR_RANGE_WHEN_PARTIAL							*
 *	Hong 04/24/07 QA80-9288-P7 v8.0607 FIX_OVERLAP_OF_LABELS					*
 *	CPY 8/16/2007 QA70-10260 CSV_FROM_MAC_WITH_LF_ONLY							*
 *	CPY 8/23/2007 QA70-10293	CSV_IMPORT_NEED_TO_HANDLE_CELLS_WITH_LF_AND_ALSO_NEED_MAC_IMPORT
 *	Hong 09/07/07 v8.0698b ADD_MAIN_HEADER_LINE_TO_SKIP_DATA					*
 *	Sim 06-23-2008 CLEAR_NO_NEED_COLUMN_INFO_FOR_IMPORT_CSV						*
 *	Hong 07/07/08 v8.0896b RECONSTRUCT_CODE										*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
// Including the system header file Origin.h should be sufficient for most Origin
// applications and is recommended. Origin.h includes many of the most common system
// header files and is automatically pre-compiled when Origin runs the first time.
// Programs including Origin.h subsequently compile much more quickly as long as
// the size and number of other included header files is minimized. All NAG header
// files are now included in Origin.h and no longer need be separately included.
//
// Right-click on the line below and select 'Open "Origin.h"' to open the Origin.h
// system header file.
#include <Origin.h>
////////////////////////////////////////////////////////////////////////////////////


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


////////////////////////////////////////////////////////////////////////////////////
// Start your functions here.
//#include <..\originlab\fu_utils.h>
#include <okocUtils.h>
#include "CSVFile.h"

#define _SAFE_RETURN(_nRet) { Close(); return _nRet; }

static void remove_quotation(vector<string>& vstrCells)
{
	string		strCell;
	for ( int ii = 0; ii < vstrCells.GetSize(); ii++ )
	{
		strCell = vstrCells[ii];
		if( '\"' != strCell[0] )
			continue ;

		// remove the redundant quotations of the string
		ASSERT( ('\"' == strCell[strCell.GetLength() - 1]) );
		strCell.Delete(0);		
		strCell.Delete(strCell.GetLength() - 1);		
		strCell.Replace("\"\"", "\"");
		
		vstrCells[ii] = strCell;
	}
}

static void partial_column(vector<string>& vs, int nColFrom, int nColTo)
{
	int nMaxCol;

	// trim the right of data
	if ( nColTo >= 0 )
	{
		nMaxCol = vs.GetSize() - 1;
		if ( nColTo < nMaxCol )
			vs.RemoveAt(nColTo + 1, nMaxCol - nColTo);
	}
	
	// trim the left of data
	if ( nColFrom > 0 )
	{
		nMaxCol = vs.GetSize() - 1;
		if ( nColFrom <= nMaxCol )
			vs.RemoveAt(0, nColFrom);
		else
			vs.RemoveAll();
	}
}

///---Sim 03-19-2007 PARTIAL_IMPORT
//int CSVFile::Import(LPCSTR lpcszFileName, Worksheet& wks, TreeNode& trFileInfo, int nC1, int nR1) // = 0, 0
/// Hong 03/30/07 v8.0593 ADD_HEADER_LINE_OPTION
//int CSVFile::Import(LPCSTR lpcszFileName, Worksheet& wks, TreeNode& trFileInfo, int nC1, int nR1,
	 //int nColFrom, int nColTo, int nRowFrom, int nRowTo) // = 0, 0, 0, -1, 0, -1
/// Hong 09/03/07 v8.0694 ALLOW_USER_SPECIFY_EXSCAPE_CHARACTER
//int CSVFile::Import(LPCSTR lpcszFileName, Worksheet& wks, TreeNode& trFileInfo, int nC1, int nR1,
//	 int nColFrom, int nColTo, int nRowFrom, int nRowTo, TreeNode& trOption) // = 0, 0, 0, -1, 0, -1, NULL
int CSVFile::Import(LPCSTR lpcszFileName, Worksheet& wks, TreeNode& trFileInfo, int nC1, int nR1,
	 int nColFrom, int nColTo, int nRowFrom, int nRowTo, TreeNode& trOption, int nEscapeCharacterFmt) // = 0, 0, 0, -1, 0, -1, NULL, ESCAPE_CHARACTER_FMT_AUTO
/// end ALLOW_USER_SPECIFY_EXSCAPE_CHARACTER
/// end ADD_HEADER_LINE_OPTION
///---END PARTIAL_IMPORT
{
	// check partial import
	if ( ( nColTo >= 0 && nColFrom > nColTo )
		|| ( nRowTo >= 0 && nRowFrom > nRowTo ) )
	{
		warning_msg_box(_L("Invalid input parameters for partial column or row import."), false);
		return CER_INVALID_ARGUMENT;
	}
	
	if ( m_orng )
		m_orng.Reset();
	
	int nRet = CER_NO_ERROR;

	if ( !Open(lpcszFileName, file::modeRead | file::typeBinary) ) 
		return IMPERR_FAILED_OPEN; 					

	///---Sim 03-20-2007 FIX_CSV_DATA_ERROR_FOR_REIMPORT
	//if ( 0 == nR1 && 0 == nC1 ) // new worksheet
		//wks.SetSize(-1, -1, true);	
	///---END FIX_CSV_DATA_ERROR_FOR_REIMPORT
		
	/// Hong 03/30/07 v8.0593 ADD_HEADER_LINE_OPTION
	if ( trOption && trOption.SubHeaderLines.nVal > 0 )
	{
		if ( trOption.CommentsTo.nVal < trOption.CommentsFrom.nVal )
			trOption.CommentsTo.nVal = trOption.SubHeaderLines.nVal;
			
		if ( trOption.ParamsTo.nVal < trOption.ParamsFrom.nVal )
			trOption.ParamsTo.nVal = trOption.SubHeaderLines.nVal;
		
		if ( trOption.UserParamsTo.nVal < trOption.UserParamsFrom.nVal )
			trOption.UserParamsTo.nVal = trOption.SubHeaderLines.nVal;
	}
	/// end ADD_HEADER_LINE_OPTION
	
	int nRow = 0;		
	m_nRowNum = m_nColNum = 0; // really number of input data
	string strContents;	
	bool bInit = true;
	bool bFirstHeader = true;
	bool bFirstMainHeader = true; /// Hong 09/07/07 v8.0698b ADD_MAIN_HEADER_LINE_TO_SKIP_DATA 
	/// Hong 07/07/08 v8.0896b RECONSTRUCT_CODE
	if ( !trOption )
	{
		bFirstHeader = false;
		bFirstMainHeader = false;
	}
	/// end RECONSTRUCT_CODE
	//--------CPY 8/23/2007 QA70-10293	CSV_IMPORT_NEED_TO_HANDLE_CELLS_WITH_LF_AND_ALSO_NEED_MAC_IMPORT
	//	//---- CPY 8/16/2007 QA70-10260 CSV_FROM_MAC_WITH_LF_ONLY
	//	//while( ReadString(strContents, FRS_EOL_CRLF_ONLY) )// only care it and of line by "\r\n", not "\n" or "\r"
	//	while( ReadString(strContents, FRS_EOL_LF_OR_CRLF) )
	//	//----
	stdioFileInfo fInfo;
	/// Hong 09/03/07 v8.0694 ALLOW_USER_SPECIFY_EXSCAPE_CHARACTER
	//int nn = Scan(1000, &fInfo);
	//DWORD dwReadCntrl = FRS_EOL_CRLF_ONLY;
	//if(fInfo.nLF > fInfo.nCRLF)
	//	dwReadCntrl = FRS_EOL_LF_OR_CRLF;
	DWORD dwReadCntrl;
	switch ( nEscapeCharacterFmt )
	{
	case ESCAPE_CHARACTER_FMT_AUTO:
		int nn = Scan(1000, &fInfo);
		dwReadCntrl = FRS_EOL_CRLF_ONLY;
		if(fInfo.nLF > fInfo.nCRLF)
			dwReadCntrl = FRS_EOL_LF_OR_CRLF;
		break;
	case ESCAPE_CHARACTER_FMT_DOS:
		dwReadCntrl = FRS_EOL_CRLF_ONLY;
		break;
	case ESCAPE_CHARACTER_FMT_OTHERS:
		dwReadCntrl = FRS_EOL_LF_OR_CRLF;
		break;
	default:
		dwReadCntrl = FRS_EOL_CRLF_ONLY;
		break;
	}
	
	vector<string>		vstrCells; /// Hong 07/07/08 v8.0896b RECONSTRUCT_CODE
	while( ReadString(strContents, dwReadCntrl) )
	//--------
	{
		/// Hong 07/07/08 v8.0896b RECONSTRUCT_CODE
		/*
		///---Sim 03-19-2007 PARTIAL_IMPORT
		if( bInit )
		{
			vector<string> vsInit;
			strContents.GetTokens(vsInit, ',');
			partial_column(vsInit, nColFrom, nColTo); // Hong 04/24/07 v8.0607 FIX_ERROR_RANGE_WHEN_PARTIAL
			
			m_nColNum = vsInit.GetSize();
			if ( 0 == m_nColNum ) //file is empty, first line can't be empty
				_SAFE_RETURN(IMPERR_INVALID_FILE);
			
			if ( wks.GetNumCols() < nC1 + m_nColNum )
				wks.SetSize(-1, nC1 + m_nColNum);
			bInit = false;
		}
		
		if ( -1 != nRowTo && nRow > nRowTo )
			break; // out the range of partial import, stop input
		
		/// Hong 09/07/07 v8.0698b ADD_MAIN_HEADER_LINE_TO_SKIP_DATA
		if ( bFirstMainHeader && trOption && trOption.MainHdrLns.nVal > nRow )
		{
			nRow++;
			
			continue;
		}
		
		if ( bFirstMainHeader )
		{
			nRow = 0; // rollback to default 0 after read main header lines
			bFirstMainHeader = false;
		}
		/// end ADD_MAIN_HEADER_LINE_TO_SKIP_DATA
		
		/// Hong 03/30/07 v8.0593 ADD_HEADER_LINE_OPTION
		//if( trOption && trOption.SubHeaderLines.nVal > nRow )
		if( bFirstHeader && trOption && trOption.SubHeaderLines.nVal > nRow ) // Hong 03/31/07 v8.0593 FIX_ERROR_DUPLICATE_READ_HEADER_LINES
		{
			nRow++;
			
			vector<string> vstrCell;
			strContents.GetTokens(vstrCell, ',');
			partial_column(vstrCell, nColFrom, nColTo);
			
			if ( vstrCell.GetSize() > 0 )
			{
				for(int ii=0; ii < vstrCell.GetSize(); ii++)
				{
					remove_quotation(vstrCell[ii]);		
					/// Hong 04/24/07 QA80-9288-P7  v8.0607 FIX_OVERLAP_OF_LABELS
					//Column cc(wks, ii);
					Column cc(wks, nC1 + ii);
					/// end FIX_OVERLAP_OF_LABELS
					
					if ( nRow == trOption.ShortNames.nVal )
						cc.SetName(vstrCell[ii]);
					
					if ( nRow == trOption.LongNames.nVal )
						cc.SetLongName(vstrCell[ii]);
					
					if ( nRow == trOption.Units.nVal )
						cc.SetUnits(vstrCell[ii]);
					
					if ( trOption.CommentsFrom.nVal > 0 && nRow >= trOption.CommentsFrom.nVal && nRow <= trOption.CommentsTo.nVal )
					{
						string strComments;
						if ( nRow == trOption.CommentsFrom.nVal )
						{
							strComments = vstrCell[ii];
							cc.SetComments(strComments); // Hong 03/31/07 v8.0593 CLEAN_COMMENTS_BEFFORE_BEFORE_APPED
						}
						else
						{
							strComments = cc.GetComments();
							strComments.Format("%s\n%s", strComments, vstrCell[ii]);
						}
						cc.SetComments(strComments);
					}
					
					if ( trOption.ParamsFrom.nVal > 0 && nRow >= trOption.ParamsFrom.nVal && nRow <= trOption.ParamsTo.nVal )
					{
						int nIndex = nRow - trOption.ParamsFrom.nVal;
						cc.SetParameter(vstrCell[ii], nIndex);
						
						Grid grid;   
						if( grid.Attach(wks) )
							grid.ShowLabels(RCLT_PARAM+nIndex);
					}
					
					if ( trOption.UserParamsFrom.nVal > 0 && nRow >= trOption.UserParamsFrom.nVal && nRow <= trOption.UserParamsTo.nVal )
					{
						vector<string> vsName, vsVal;
						string strName;
						strName.Format("UserParameters%d", nRow - trOption.UserParamsFrom.nVal);
						vsName.Add(strName);
						vsVal.Add(vstrCell[ii]);
						set_user_parameters(cc, vsName, vsVal);
					}
				}		
			}	
			continue;
		}
		
		if(bFirstHeader)
		{
			nRow = 0; // rollback to default 0 after read header lines
			bFirstHeader = false;		
		}		
		/// end ADD_HEADER_LINE_OPTION
		
		if ( nRow >= nRowFrom ) 
		{
			//bool bHaveQuotation = false;
			vector<string> vstrCell;
			strContents.GetTokens(vstrCell, ',');
			partial_column(vstrCell, nColFrom, nColTo);
	
			
			if ( vstrCell.GetSize() > 0 )
			{
				for(int ii=0; ii < vstrCell.GetSize(); ii++)
					remove_quotation(vstrCell[ii]);
		
				wks.SetCells(vstrCell, nR1 + m_nRowNum, nC1);
			}
			m_nRowNum++;
		}
		nRow++;
		///---END PARTIAL_IMPORT
		*/
		// main header lines
		if ( bFirstMainHeader && trOption.MainHdrLns.nVal > nRow )
		{
			nRow++;
			continue;
		}
		
		if ( bFirstMainHeader )
		{
			nRow = 0; // rollback to default 0 after read main header lines
			bFirstMainHeader = false;
		}
				
		strContents.GetTokens(vstrCells, ',');
		partial_column(vstrCells, nColFrom, nColTo);
		remove_quotation(vstrCells);
		// init worksheet
		if ( bInit )
		{			
			m_nColNum = vstrCells.GetSize();
			if ( 0 == m_nColNum ) //file is empty, first line can't be empty
				_SAFE_RETURN(IMPERR_INVALID_FILE);
			
			if ( wks.GetNumCols() < nC1 + m_nColNum )
				wks.SetSize(-1, nC1 + m_nColNum);
			bInit = false;
		}
		
		if( bFirstHeader && trOption.SubHeaderLines.nVal > nRow ) // Hong 03/31/07 v8.0593 FIX_ERROR_DUPLICATE_READ_HEADER_LINES
		{
			nRow++;
			
			for ( int ii=0; ii < vstrCells.GetSize(); ii++ )
			{
				/// Hong 04/24/07 QA80-9288-P7  v8.0607 FIX_OVERLAP_OF_LABELS
				//Column cc(wks, ii);
				Column cc(wks, nC1 + ii);
				/// end FIX_OVERLAP_OF_LABELS
				ASSERT(cc);
				
				if ( nRow == trOption.ShortNames.nVal )
					cc.SetName(vstrCells[ii]);
				
				if ( nRow == trOption.LongNames.nVal )
					cc.SetLongName(vstrCells[ii]);
				
				if ( nRow == trOption.Units.nVal )
					cc.SetUnits(vstrCells[ii]);
				
				if ( trOption.CommentsFrom.nVal > 0 && nRow >= trOption.CommentsFrom.nVal && nRow <= trOption.CommentsTo.nVal )
				{
					string strComments;
					if ( nRow == trOption.CommentsFrom.nVal )
					{
						strComments = vstrCells[ii];
						cc.SetComments(strComments); // Hong 03/31/07 v8.0593 CLEAN_COMMENTS_BEFFORE_BEFORE_APPED
					}
					else
					{
						strComments = cc.GetComments();
						strComments.Format("%s\n%s", strComments, vstrCells[ii]);
					}
					cc.SetComments(strComments);
				}
				
				if ( trOption.ParamsFrom.nVal > 0 && nRow >= trOption.ParamsFrom.nVal && nRow <= trOption.ParamsTo.nVal )
				{
					int nIndex = nRow - trOption.ParamsFrom.nVal;
					cc.SetParameter(vstrCells[ii], nIndex);
					
					Grid grid;   
					if( grid.Attach(wks) )
						grid.ShowLabels(RCLT_PARAM+nIndex);
				}
				
				if ( trOption.UserParamsFrom.nVal > 0 && nRow >= trOption.UserParamsFrom.nVal && nRow <= trOption.UserParamsTo.nVal )
				{
					vector<string> vsName, vsVal;
					string strName;
					strName.Format("UserParameters%d", nRow - trOption.UserParamsFrom.nVal);
					vsName.Add(strName);
					vsVal.Add(vstrCells[ii]);
					set_user_parameters(cc, vsName, vsVal);
				}
			}				
			continue;
		}
		
		if ( bFirstHeader )
		{
			nRow = 0; // rollback to default 0 after read header lines
			bFirstHeader = false;		
		}

		if ( -1 != nRowTo && nRow > nRowTo )
			break; // out the range of partial import, stop input
				
		if ( nRow >= nRowFrom ) 
		{				
			if ( vstrCells.GetSize() > 0 )
			{	
				wks.SetCells(vstrCells, nR1 + m_nRowNum, nC1);
			}
			m_nRowNum++;
		}
		nRow++;
		/// end RECONSTRUCT_CODE
	}
	Close();
	
	// clean redundant rows
	if ( 0 == nR1 && 0 == nC1 ) // new worksheet
	{
		if ( m_nRowNum < wks.GetNumRows() )
			wks.SetSize(m_nRowNum, -1);
	}
	//wks.SetName(GetFileName(lpcszFileName, true));
	
	///---Sim 06-23-2008 CLEAR_NO_NEED_COLUMN_INFO_FOR_IMPORT_CSV
	// Add filename to column info make no sense. It will waste storage, and it isn't any usage.
	// Only Justin designed it before.
	/*
	// set column user info tree
	for (int nCol = nC1; nCol < nC1 + m_nColNum; nCol++)
	{
		Column cc(wks, nCol);
		string strColumnInfo = "ColumnInfo";
		Tree trColumnInfo;
		trColumnInfo.AddTextNode(lpcszFileName, "ImportFile");
		trColumnInfo.Enable = ENABLE_READ_ONLY;
		trColumnInfo.SetAttribute(STR_ATTRIB_BRANCH, GETNBRANCH_OPEN);
		set_user_info(cc, strColumnInfo, trColumnInfo);
	}
	*/
	///---END CLEAR_NO_NEED_COLUMN_INFO_FOR_IMPORT_CSV
	
	// pass imported range info
	///---Sim 03-12-2007 TEMP_FIX_RANGE_FOR_SKIP_DATA
	//m_orng.Add(wks, nC1, "Range", nC1 + m_nColNum - 1, nR1, nR1 + m_nRowNum - 1);
	m_orng.Add(wks, nC1, "Range", nC1 + m_nColNum - 1);
	///---END TEMP_FIX_RANGE_FOR_SKIP_DATA
	
	return nRet;
}
