/*------------------------------------------------------------------------------*
 * File Name: CMATFile.h				 										*
 * Creation: Soapy 																*
 * Purpose: CMATFile class for MAT file import									*
 * Copyright (c) ABCD Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *		Soapy 9/02/04	QA6866 READ_COMPRESSED_MAT_DATA_FROM_MATLAB7			*
 *		Soapy 9/10/04	QA6866 USE_OZLIB_DLL									*
 *		Soapy 9/21/04	QA6866	SUPPORT_UNICODE_CHARACTER						*
 *		Soapy 9/29/04	QA6866	SPARSE_ARRAY_NONZERO_INDICES					*
 *		Soapy 10/09/04	QA6866	ADD_ARRAT_TYPE_SPARSE_COMPLEX					*
 *	Hong 04/09/07 QA80-9594 MORE_IN_80_IMPORT_MATLAB							*
 *	Hong 04/24/07 QA80-9594 v8.0607 FIX_RUNTIME_ERROR_WHEN_EMPTY_VARIABLE		*
 *	Sim 04-25-2007 QA80-9594-P1 FIX_DEAL_WITH_COMPLEX							*
 *	Hong 10/19/07 QA80-10564 FIX_REDUNDANT_EMPTY_COLUMN							*
 *	Fisher 2008-4-22 QA80-11442 END MAT_FILE_IMPORT_INCORRECT					*
 *	Sophy 9/21/2009 QA80-14336 FILE_WITH_LONG_VAR_NAME_RESULT_IN_RUNTIME_ERROR	*
 *	Sophy 9/21/2009 QA80-14336 MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA			*
 *	Sophy 12/7/2009 QA80-9594-P3 EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR				*
 *	Sophy 12/17/2009 QA80-14325-P2 LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET			*
 *	Sophy 1/18/2010 QA80-14325-P3 NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
 *------------------------------------------------------------------------------*/

#ifndef _CMATFILE_H
#define _CMATFILE_H
 
#include <origin.h>

#include <OArrayBase.h>

#include <..\originlab\fu_utils.h> // Hong 05/30/07 FIX_COMPILE_ERROR

/// Soapy 9/10/04	QA6866 USE_OZLIB_DLL
/*
///Soapy 9/02/04	QA6866 READ_COMPRESSED_MAT_DATA_FROM_MATLAB7
#define OCGZLIB_API
#include <ocZlib.h>
///END READ_COMPRESSED_MAT_DATA_FROM_MATLAB7
*/
#include <ozlib.h>
///END Soapy 9/10/04	QA6866 USE_OZLIB_DLL

///Sophy 9/21/2009 QA80-14336 FILE_WITH_LONG_VAR_NAME_RESULT_IN_RUNTIME_ERROR
//in the attached file in #14336, I fine the length of name as 324, which is more than 63(got by namelengthmax in matlab7.0, which is the length limit of variable name)
//maybe there is something wrong with the attached file, anyway, we can enlarge the buffer to store variable name without breaking other logic.
//#define MAX_VAR_NAME_SIZE 32		// maximum length of MAT file name
#define MAX_VAR_NAME_SIZE 512		// maximum length of MAT variable name
///end FILE_WITH_LONG_VAR_NAME_RESULT_IN_RUNTIME_ERROR
#define SUPPORT_DIMESION 2			// Currently, Origin only support array of 1 or 2 dimension
#define DATA_BOUNDARY_IN_BYTE 8		// Data Elements in MAT file are align to 8 byte boundary

enum MATArrayClass{
MAT_ARRAY_mxCELL_CLASS = 1,		// Not support currently
MAT_ARRAY_mxSTRUCTURE_CLASS = 2,// Not support currently
MAT_ARRAY_mxOBJECT_CLASS = 3,	// Not support currently
MAT_ARRAY_mxCHARACTER_CLASS = 4,
MAT_ARRAY_mxSPARSE_CLASS = 5,
MAT_ARRAY_mxDOUBLE_CLASS = 6,
MAT_ARRAY_mxSINGLE_CLASS = 7,
MAT_ARRAY_mxINT8_CLASS = 8,
MAT_ARRAY_mxUINT8_CLASS = 9,
MAT_ARRAY_mxINT16_CLASS = 10,
MAT_ARRAY_mxUINT16_CLASS = 11,
MAT_ARRAY_mxINT32_CLASS = 12,
MAT_ARRAY_mxUINT32_CLASS = 13,
MAT_ARRAY_mxCOMPLEX_CLASS = 14,
MAT_ARRAY_mxCOMPLEX_SPARSE_CLASS = 15 ///Soapy 10/09/04	QA6866	ADD_ARRAT_TYPE_SPARSE_COMPLEX 
};

enum MATDataType{
MAT_DataType_miINT8 = 1,
MAT_DataType_miUINT8 = 2,
MAT_DataType_miINT16 = 3,
MAT_DataType_miUINT16 = 4,
MAT_DataType_miINT32 = 5,
MAT_DataType_miUINT32 = 6,
MAT_DataType_miSINGLE = 7,
MAT_DataType_miReserved1 = 8,
MAT_DataType_miDOUBLE = 9,
MAT_DataType_miReserved2 = 10,
MAT_DataType_miReserved3 = 11,
MAT_DataType_miINT64 = 12,		// Not support currently
MAT_DataType_miUINT64 = 13,		// Not support currently
MAT_DataType_miMATRIX = 14,
MAT_DataType_miCOMPRESSED = 15,    ///READ_COMPRESSED_MAT_DATA_FROM_MATLAB7
///Soapy 9/21/04	QA6866	SUPPORT_UNICODE_CHARACTER
MAT_DataType_miUTF8 = 16,
MAT_DataType_miUTF16 = 17,
MAT_DataType_miUTF32 = 18
///END SUPPORT_UNICODE_CHARACTER
};

///Sophy 10/16/2009 QA80-14325 MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT
#define	IMPMODE_OPTIONS_LIST	_L("Start New Books|Start New Rows|Start New Columns|Start New Worksheets|Start New Workbooks|Start New Matrixsheets|Start New Matrixbooks")
enum {
	IMPMODE_START_NEW_BOOKS	= 0,
	IMPMODE_START_NEW_ROWS	= 1,
	IMPMODE_START_NEW_COLS	= 2,
	IMPMODE_ALL_TO_ONE_WKS	= 3,
	IMPMODE_ONE_TO_ONE_WKS	= 4,
	IMPMODE_ALL_TO_ONE_MAT	= 5,
	IMPMODE_ONE_TO_ONE_MAT	= 6,
};
#define	STR_SKIP_DATASET_WARN_FMT	_L("Some variables in %s are not supported by Origin and skipped!")
///end MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT

class CElementBase : public OObject
{
public:

	CElementBase(bool bIsLittleEndian, file *pfMATFile)
	{
		Init(bIsLittleEndian, pfMATFile);
	}

	~CElementBase()
	{
		
	}
	
private:
	void Init(bool bIsLittleEndian, file *pfMATFile)
	{
		m_bIsLittleEndian = bIsLittleEndian;
		m_pfMATFile = pfMATFile;
	}

public:
	virtual BOOL ReadTag() 			// Read the tag of the data element
	{
		if(!m_pfMATFile->IsOpen())
			return false;
		
		m_lPosition = m_pfMATFile->GetPosition();		
		
		unsigned int iTemp;
			
		if(1!=m_pfMATFile->ReadInt(&iTemp, sizeof(int), 1, m_bIsLittleEndian))
			return false;
	
		if ( iTemp > 0x10000 )		// Is the data stored in a compressed form?
		{
			m_dwSize = iTemp / 0x10000;
			m_dwDataType = mod(iTemp, 0x10000);
		}
		else
		{	
			m_dwDataType = iTemp;
			if(1!=m_pfMATFile->ReadInt(&m_dwSize, sizeof(int), 1, m_bIsLittleEndian))
				return false;
		}
		
		return TRUE;
	}
	
	BOOL SkipRemainBytes()			// Skip the remain bytes to align to the 8 byte boundary
	{
		if(m_pfMATFile->IsOpen())
		{
			LONG lOffset;
			if( 0 != mod(m_dwSize, DATA_BOUNDARY_IN_BYTE))  
			{
				if( m_dwSize <= sizeof(int))
					lOffset = DATA_BOUNDARY_IN_BYTE;
				else
					lOffset = (m_dwSize/DATA_BOUNDARY_IN_BYTE+2)*DATA_BOUNDARY_IN_BYTE; 
			}
			else
			{
				lOffset = m_dwSize+DATA_BOUNDARY_IN_BYTE;
			}
			
			m_pfMATFile->Seek(lOffset+m_lPosition, file::begin);

			return true;
		}
		
		return false;		
	}

public:
	LONG 			m_lPosition;		// the position of the data element in the file
	unsigned int	m_dwSize;			// the size of the data element
	unsigned int	m_dwDataType;		// the data type 
	BOOL			m_bIsLittleEndian;	// Big_Endian or Little_Endian
	file*			m_pfMATFile;		// the file pointer
};


// The Array_Flag Sub_element
class CArrayFlagEle : public CElementBase
{
public:
	CArrayFlagEle(bool bIsLittleEndian, file *pfMATFile) : CElementBase(bIsLittleEndian,pfMATFile)
	{		
	}
	
	BOOL GetData(bool& bIsComplex, uint& bClassType)
	{
		if ( !ReadTag() )
			return FALSE;
		int bArrayFlag[2];
		if( 2 != m_pfMATFile->ReadInt(&bArrayFlag, sizeof(int), 2, m_bIsLittleEndian))
			return false;
			
		bIsComplex = (bArrayFlag[0] & 0x0800)/ 0x100;
		bClassType = bArrayFlag[0] & 0xFF;
		return true;
	}
};


// The Dimension Sub_element
class CDimensionEle : public CElementBase
{
public:
	CDimensionEle(bool bIsLittleEndian, file *pfMATFile) : CElementBase(bIsLittleEndian,pfMATFile)
	{
	}
	
	BOOL GetData(vector<uint>& vecDimension)
	{
		if ( !ReadTag() )
			return FALSE;
		uint nDimensionSize = m_dwSize / sizeof(int);
			
		vecDimension.SetSize(nDimensionSize);
		uint nRet = m_pfMATFile->ReadInt(&vecDimension, sizeof(int), nDimensionSize, m_bIsLittleEndian);
		if(nRet != nDimensionSize)
			return false;
		
		SkipRemainBytes();
		
		return true;
	}
};


// The Array Name Sub_element
class CArrayNameEle : public CElementBase
{
public:
	CArrayNameEle(bool bIsLittleEndian, file *pfMATFile) : CElementBase(bIsLittleEndian,pfMATFile)
	{
	}
	
	BOOL GetData(string& strArrayName)
	{
		if ( !ReadTag() )
			return FALSE;
			
		char strName[MAX_VAR_NAME_SIZE];  ///Soapy 08/21/03 QA70-3572 v7.0664 CODE_UPDATE_FOR_IMPOTR_MAT_FILE_2
		if( m_dwSize != m_pfMATFile->ReadInt(strName, sizeof(char), m_dwSize, m_bIsLittleEndian))
			return false;
		strArrayName = strName;
		
		SkipRemainBytes();
		
		return true;
	}
};

// The Array Data Sub_element
class CArrayDataEle : public CElementBase
{
public:
	CArrayDataEle(bool bIsLittleEndian, file *pfMATFile) : CElementBase(bIsLittleEndian,pfMATFile)
	{
	}
	
	int GetData(vector& vData)   
	{
		if ( !ReadTag() )
			return FALSE;

		bool bSuccess = false;
		
		int nItem;
		
		switch(m_dwDataType)
		{
		case MAT_DataType_miINT8: // INT8
				nItem = m_dwSize / sizeof(char);
				vector<char> vecCh(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecCh, sizeof(byte), nItem, m_bIsLittleEndian))
				{
					vData=vecCh; 
					bSuccess = true;
					
				}
				break;
					
		case MAT_DataType_miUINT8: // Unsigned INT8
				nItem = m_dwSize / sizeof(byte);
				vector<byte> vecByte(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecByte, sizeof(byte), nItem, m_bIsLittleEndian))
				{
					vData=vecByte;
					bSuccess = true;
					
				}
				break;

		case MAT_DataType_miUTF8:  // Unicode UTF-8 character  ///Soapy 9/21/04	QA6866	SUPPORT_UNICODE_CHARACTER
				nItem = m_dwSize / sizeof(byte);
				vector<byte> vecByte(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecByte, sizeof(byte), nItem, m_bIsLittleEndian))
				{
					vData=vecByte;
					bSuccess = true;
					
				}
				break;
				
		case MAT_DataType_miINT16: // INT16
				nItem = m_dwSize / sizeof(short);
				vector<short> vecInt16(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecInt16, sizeof(short), nItem, m_bIsLittleEndian))
				{
					vData=vecInt16;
					bSuccess = true;
				}
				break;
			
		case MAT_DataType_miUTF16:  // Unicode UTF-16 character  ///Soapy 9/21/04	QA6866	SUPPORT_UNICODE_CHARACTER
				nItem = m_dwSize / sizeof(short);
				vector<unsigned short> vecUInt16(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecUInt16, sizeof(short), nItem, m_bIsLittleEndian))
				{
					vData=vecUInt16;
					bSuccess = true;
				}
				break;
			
		case MAT_DataType_miUINT16: // Unsigned INT16
				nItem = m_dwSize / sizeof(short);
				vector<unsigned short> vecUInt16(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecUInt16, sizeof(short), nItem, m_bIsLittleEndian))
				{
					vData=vecUInt16;
					bSuccess = true;
				}
				break;
			
		case MAT_DataType_miINT32: // INT32
				nItem = m_dwSize / sizeof(int);
				vector<int> vecInt32(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecInt32, sizeof(int), nItem, m_bIsLittleEndian))
				{
					vData=vecInt32;
					bSuccess = true;
				}
				break;
				
		case MAT_DataType_miUTF32:  // Unicode UTF-32 character  ///Soapy 9/21/04	QA6866	SUPPORT_UNICODE_CHARACTER
				nItem = m_dwSize / sizeof(int);
				vector<unsigned int> vecUInt32(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecUInt32, sizeof(int), nItem, m_bIsLittleEndian))
				{
					vData=vecUInt32;
					bSuccess = true;
				}
				break;

		case MAT_DataType_miUINT32: // Unsigned INT32
				nItem = m_dwSize / sizeof(int);
				vector<unsigned int> vecUInt32(nItem);
				if(nItem == m_pfMATFile->ReadInt(&vecUInt32, sizeof(int), nItem, m_bIsLittleEndian))
				{
					vData=vecUInt32;
					bSuccess = true;
				}
				break;
			
		case MAT_DataType_miSINGLE: // Single Format
				nItem = m_dwSize / sizeof(float);
				vector<float> vecFloat(nItem);
				if(nItem == m_pfMATFile->ReadFloat(&vecFloat, sizeof(float), nItem, m_bIsLittleEndian))
				{
					vData=vecFloat;
					bSuccess = true;
				}
				break;
					 
			
		case MAT_DataType_miDOUBLE: // Double Format
				nItem = m_dwSize / sizeof(double);
				vData.SetSize(nItem);
				if(nItem == m_pfMATFile->ReadFloat(&vData, sizeof(double), nItem, m_bIsLittleEndian))
				{
					//vecTemp=vecFloat;
					bSuccess = true;
				}
				break;
				
		case MAT_DataType_miINT64: // INT64 -- Not Support Now			
		case MAT_DataType_miUINT64: // Unsigned INT64 -- Not Support Now
				return 0;
				break;		
			
		default: return -1;// 8, 10, 11 Are Reserved
		}
		
		if(bSuccess)
		{
			SkipRemainBytes();
			return 1;
		}
		
		return -1;		
	}
		
};


///Sophy 9/21/2009 QA80-14336 MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
enum {
	RD_NO_ERROR			= 0x0000,	//success
	RD_TAG_FAIL			= 0x0001,	//fail on read tag
	RD_FLAG_FAIL		= 0x0002,	//fail on read flag
	RD_DIM_FAIL			= 0x0003,	//fail on read dimensinality
	RD_NAME_FAIL		= 0x0004,	//fail on read variable name
	RD_TYPE_NOT_SUPPORT	= 0x0005,	//fail on not supported type
	RD_DIM_NOT_SUPPORT	= 0x0006,	//not supported dimensionality
	RD_GET_DATA_FAIL	= 0x0007,	//fail to read data as array
};
///end MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
// The Array Element Which Contains Particular Sub_elements
class CDataElement : public CElementBase
{
public:
	CDataElement(bool bIsLittleEndian, file *pfMATFile) : CElementBase(bIsLittleEndian,pfMATFile)
	{
		reset();
	}

	BOOL ReadTag()
	{
		if(!CElementBase::ReadTag())
			return false;
		
		if(m_dwDataType != MAT_DataType_miMATRIX) 
			return false;
		
		return TRUE;
	}
	///Sophy 9/21/2009 QA80-14336 MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
	//BOOL GetData()
	//{
		//if ( !ReadTag() )
			//return FALSE;
		//
		//if(!readSubElement())
		//{
			//reset();
			//return false;
		//}
		//
		//return true;
	//}
	int GetData()
	{
		if ( !ReadTag() )
			return RD_TAG_FAIL;
		
		int nErr = readSubElement();
		if ( RD_NO_ERROR != nErr )
			reset();
		
		return nErr;
	}
	///end MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
	
private:
	void reset();
	
	int readSubElement();
	
	int readRegularArray(bool bIsComplex);
	
	int readSparseArray(bool bIsComplex);
	
public:
	string m_strVarName;					// Variable Name	
	vector<uint> m_vDimesion;  				// The size of each dimension
	bool m_bImportable;						// Origin import support or not
	vector m_vReal;							// The real part of the data
	vector m_vImaginary;
	
};
	
void CDataElement::reset()
{
	m_bImportable = false;
	m_vDimesion.SetSize(0); 
	m_vReal.SetSize(0);
	m_vImaginary.SetSize(0);
}
	
int	CDataElement::readSubElement()
{
	bool bIsComplex;
	
	// Read the Array_Flag
	CArrayFlagEle ArrayFlag(m_bIsLittleEndian, m_pfMATFile);
	if(!ArrayFlag.GetData(bIsComplex, m_dwDataType))
		return RD_TAG_FAIL;
	
	// Read the Dimension Array
	CDimensionEle DimensionArray(m_bIsLittleEndian, m_pfMATFile);
	if(!DimensionArray.GetData(m_vDimesion))
		return RD_DIM_FAIL;
	
	// Read the Array Name
	CArrayNameEle ArrayName(m_bIsLittleEndian, m_pfMATFile);
	if(!ArrayName.GetData(m_strVarName))
		return RD_NAME_FAIL;
	
	// Skip the remain bytes of this data element if it's not supported by Origin
	if(( m_dwDataType < MAT_ARRAY_mxCHARACTER_CLASS) ||
		(m_vDimesion.GetSize() > SUPPORT_DIMESION))		
	{
		SkipRemainBytes();
		return m_dwDataType < MAT_ARRAY_mxCHARACTER_CLASS ? RD_TYPE_NOT_SUPPORT : RD_DIM_NOT_SUPPORT;
	}
	
	// Read the Array Data
	int nRet;
	if( MAT_ARRAY_mxSPARSE_CLASS != m_dwDataType)    //regular array  
	{			
		nRet = readRegularArray(bIsComplex);                
	}
	else
	{
		nRet = readSparseArray(bIsComplex); 
	}
		
	// Skip the remain bytes
	if(nRet>=0)
	{
		SkipRemainBytes();
		if(nRet == 1)
			m_bImportable = true;
		return RD_NO_ERROR;
	}
		
	return RD_GET_DATA_FAIL;
}

// Read the regular Array	
int CDataElement::readRegularArray(bool bIsComplex)
{
	int nRet;

	CArrayDataEle ArrayData(m_bIsLittleEndian, m_pfMATFile);
	nRet = ArrayData.GetData(m_vReal);
	if(nRet <= 0)
		return nRet;
		
	if(bIsComplex)
	{
		m_dwDataType = MAT_ARRAY_mxCOMPLEX_CLASS;
		nRet = ArrayData.GetData(m_vImaginary);
		if(nRet <= 0)
			return nRet;
	}
		
	return 1;
}

// Read the Sparse Array which is stored in a packed form	
int CDataElement::readSparseArray(bool bIsComplex)
{
	int nRet;
	
	// Get the row index and column index of the non-zero array element
	vector<uint> vDimRows, vDimCols;
	CDimensionEle NonZDim(m_bIsLittleEndian, m_pfMATFile);
	if(!NonZDim.GetData(vDimRows))
		return -1;
	if(!NonZDim.GetData(vDimCols))
		return -1;
	///Soapy 9/29/04	QA6866	SPARSE_ARRAY_NONZERO_INDICES
	/*
	int nNonZero = vDimRows.GetSize();
	vDimCols.SetSize(nNonZero);
	*/
	int nNonZero = vDimCols[vDimCols.GetSize()-1];
	///END SPARSE_ARRAY_NONZERO_INDICES
	
	// Calculate the size of the array
	int nSize = m_vDimesion[0] * m_vDimesion[1];
	m_vReal.SetSize(nSize);
	m_vImaginary.SetSize(nSize);
	
	// Calculate the offset of each non-zero array element
	int nRows = m_vDimesion[0];
	vector<uint> vIndex(nNonZero);

	///Soapy 9/29/04	QA6866	SPARSE_ARRAY_NONZERO_INDICES
	//vIndex = vDimRows + vDimCols * nRows;
	int nIndex = 0;
	for(int ii = 0; ii<m_vDimesion[1]; ii++)
	{
		for(int jj = vDimCols[ii]; jj < vDimCols[ii+1]; jj++)
		{
			vIndex[nIndex++] = vDimRows[jj] + ii * nRows;
		}
	}
	///END SPARSE_ARRAY_NONZERO_INDICES
	
	// Get the values of the non-zero array element 
	vector vReal, vImaginary;
	CArrayDataEle ArrayData(m_bIsLittleEndian, m_pfMATFile);
	nRet = ArrayData.GetData(vReal);
	if(nRet <= 0)
		return nRet;

	// Assign the value to the Data Element
	for(int i=0; i<nNonZero; i++)
	{
		//uint nIndex = vDimRows[i]+vDimCols[i]*m_vDimesion[0]; ///Soapy 9/29/04	QA6866	SPARSE_ARRAY_NONZERO_INDICES
		m_vReal[vIndex[i]] = vReal[i];
	}
	
	// Read the imaginary part
	if(bIsComplex)
	{
		///Soapy 10/09/04	QA6866	ADD_ARRAT_TYPE_SPARSE_COMPLEX
		//m_dwDataType = MAT_ARRAY_mxCOMPLEX_CLASS;
		m_dwDataType = MAT_ARRAY_mxCOMPLEX_SPARSE_CLASS;
		
		nRet = ArrayData.GetData(vImaginary);
		if(nRet <= 0)
			return nRet;
		
		for(i=0; i<nNonZero; i++)
		{
			m_vImaginary[vIndex[i]]  = vImaginary[i];
		}
	}
	else
	{
		m_vImaginary.SetSize(0);
	}
		
	return 1;
}


#define MAT_FILE_HEAD_SIZE 126


class CPtrDataElementArray : public OPtrArray
{
public:
	~CPtrDataElementArray()
	{
		RemoveAll();
	}
	
public:
	void RemoveAt(int nIndex)
	{
		CDataElement* pDataElement = GetAt(nIndex);
		NICE_REMOVE(pDataElement);
		OPtrArray::RemoveAt(nIndex);
	}
	
	CDataElement* GetAt(int nIndex)
	{
		return (CDataElement*)OPtrArray::GetAt(nIndex);
	}
	
	int Add(CDataElement* pDataElement)
	{
		return OPtrArray::Add((DWORD)pDataElement);
	}
	
	void RemoveAll()
	{
		int nSize = OPtrArray::GetSize();
		for(int i=nSize-1; i>=0; i--)
		{
			RemoveAt(i);
		}
		OPtrArray::RemoveAll();
	}
};


class CMATFile 
{
public:
	
	CMATFile()
	{
		Init();
	}
	
	~CMATFile()
	{

	}
	
private:
	
	void Init();

	void Reset();

	// Add the data info to the tree
	bool SetDataInfoToTree(CDataElement* FileElement);
	
	/// Hong 04/09/07 QA80-9594 MORE_IN_80_IMPORT_MATLAB
	bool ImportToWks(Page& pg, TreeNode& trFileNode, TreeNode& trInfo);
	
	bool ImportToMatrix(Page& pg, TreeNode& trFileNode, TreeNode& trInfo);
	/// end MORE_IN_80_IMPORT_MATLAB
	
	///Sophy 10/16/2009 QA80-14325 MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT
	bool AppendRows(LPCSTR lpcszFileName, WorksheetPage& wksPage, TreeNode& trFileNode, int iStartCol = 0);
	
	bool AppendCols(LPCSTR lpcszFileName, WorksheetPage& wksPage, TreeNode& trFileNode, int iStartRow = 0);
	
	bool AppendWorksheet(LPCSTR lpcszFileName, WorksheetPage& wksPage, TreeNode& trFileNode);
	
	bool AppendMatrixsheet(LPCSTR lpcszFileName, MatrixPage& matPage, TreeNode& trFileNode);
	///end MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT
	///Sophy 12/3/2009 QA80-14325-S3 UPDATE_USEFUL_VARIABLE_INFO_AFTER_IMPORT
	bool UpdateUserTree(OriginObject& obj, LPCSTR lpcszFileName, int nVarIndex, BOOL bMultipleVars = FALSE);
	bool UpdateFileTree(OriginObject& obj, LPCSTR lpcszFileName, int nImportMode);
	///end UPDATE_USEFUL_VARIABLE_INFO_AFTER_IMPORT
	

public:
	// Read the .mat file
	bool ReadMATFile(LPCSTR lpcstrFileName);
	
	/// Hong 04/09/07 QA80-9594 MORE_IN_80_IMPORT_MATLAB
	// Import the .mat file into target page
	bool ImportMatFile(LPCSTR lpcstrFileName, Page& pg, TreeNode& trFileNode, TreeNode& trInfo);
	/// end MORE_IN_80_IMPORT_MATLAB
	
	///Sophy 10/16/2009 QA80-14325 MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT
	bool ImportMatFiles(vector<string>& vsFiles, WorksheetPage& wksPage, MatrixPage& matPage, TreeNode& trFiles, int nImportMode);
	///end MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT
	
	///Sophy 12/17/2009 QA80-14325-P2 LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET
	bool GetTargetPage(PageBase& pg, int nImportMode, int iFile);
	///end LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET
	
	/// Hong 05/30/07 v8.0629b MORE_IMPORT_INFO_FOR_ORGANIZER
	DataRange GetDataRange() { return m_orng; }
	/// end MORE_IMPORT_INFO_FOR_ORGANIZER
	
	// Set the element data to matrices
	bool SetMatrix(uint nIndex, matrix& mReal, matrix& mImaginary);
		
	// Get all the data elements Origin support
	bool GetAllImportable(vector<uint>& vecIndex);
	
	// Get all the real data elements Origin support
	bool GetAllRealImportable(vector<uint>& vecIndex);

	void OutMatFileInfo();
	
	
private:	
	file m_fMATFile;			// file object 
	bool m_bIsLittleEndian;		
	
	bool m_bReadFileStatus;		// Indicate the status of the reading
	
	CPtrDataElementArray m_Data;
	
	/// Hong 05/30/07 v8.0629b MORE_IMPORT_INFO_FOR_ORGANIZER
	DataRange m_orng; // only for import
	/// end MORE_IMPORT_INFO_FOR_ORGANIZER
	///Sophy 9/21/2009 QA80-14336 MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
	int	m_nSkippedVars; //count the number of skipped variables
	///end MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
public:
	Tree m_trFileInfo;			// the summary of the data elements in the file (for import dialog).

};


void CMATFile::Init()
{
	m_bReadFileStatus = false;	
	m_nSkippedVars = 0;	///Sophy 9/21/2009 QA80-14336 MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
}

void CMATFile::Reset()
{
	Init();
	if ( m_fMATFile.IsOpen() )
		m_fMATFile.Close();
		
	m_trFileInfo.Reset();
	
	m_Data.RemoveAll();

}
	
// Add the data info to the tree
bool CMATFile::SetDataInfoToTree(CDataElement* FileElement)
{ 
	// Set the TreeNode's name and ID
	int nID = m_Data.GetSize();
	TreeNode trNodeNew = m_trFileInfo.AddNode(FileElement->m_strVarName, nID);
	
	// The description of array dimension
	string strDim = FileElement->m_vDimesion[0];
	for(int ii=1; ii<FileElement->m_vDimesion.GetSize(); ii++)
	{
	   	strDim += " x " + FileElement->m_vDimesion[ii];
	}
	trNodeNew.Size.strVal=strDim;
	    
	// If the data element is imported
	trNodeNew.Imported.nVal = FileElement->m_bImportable;

	// The description of the array type
	string strType;	
	switch(FileElement->m_dwDataType)
	{
   		case MAT_ARRAY_mxINT8_CLASS: 
   				strType = "INT8";
		 		break;
		case MAT_ARRAY_mxUINT8_CLASS: 
				strType = "UINT8";
	  			break;
		case MAT_ARRAY_mxINT16_CLASS:
				strType = "INT16";
	    		break;
		case MAT_ARRAY_mxUINT16_CLASS:
				strType = "UINT16";
	    		break;
		case MAT_ARRAY_mxINT32_CLASS:
				strType = "INT32";
	    		break;
		case MAT_ARRAY_mxUINT32_CLASS:
				strType = "UINT32";
	    		break;
		case MAT_ARRAY_mxSINGLE_CLASS: 
				strType = "SINGLE";
	    		break;
		case MAT_ARRAY_mxDOUBLE_CLASS: 
				strType = "DOUBLE";
	    		break;
	    case MAT_ARRAY_mxCHARACTER_CLASS: 
	    		strType = "CHAR";
	    		break;
	    case MAT_ARRAY_mxCOMPLEX_CLASS:
	    		strType = "COMPLEX";
	    		break;
		case MAT_ARRAY_mxSPARSE_CLASS:
				strType = "DOUBLE SPARSE";
				break;
		case MAT_ARRAY_mxCOMPLEX_SPARSE_CLASS: ///Soapy 10/09/04	QA6866	ADD_ARRAT_TYPE_SPARSE_COMPLEX
				strType = "COMPLEX SPARSE";
				break;
				
		default: strType = "UNKNOWN";
	    		break;
	}
	  
    trNodeNew.Type.strVal=strType;	    
			
	return true;
}	

// Read the .mat file
bool CMATFile::ReadMATFile(LPCSTR lpcstrFileName)
{
	Reset();
	
	// Check the input file name
	string strFileName = lpcstrFileName;
	if((strFileName.GetLength() == 0) || !strFileName.IsFile() ||
			!m_fMATFile.Open( strFileName, file::modeRead ) || !m_fMATFile.IsOpen())
	{
		return false;
	}
	
	///Soapy 9/02/04	QA6866 READ_COMPRESSED_MAT_DATA_FROM_MATLAB7
	/*
	// Check the file length
	if(mod(m_fMATFile.GetLength(), DATA_BOUNDARY_IN_BYTE) != 0) 
	{
		m_fMATFile.Close();
		return false;
	}
	*/
	///END READ_COMPRESSED_MAT_DATA_FROM_MATLAB7
		
	// Skip the MAT-File Header
	m_fMATFile.Seek(MAT_FILE_HEAD_SIZE, file::begin);
		
	char cTemp[2];
	m_fMATFile.Read( cTemp, 2);
	if(cTemp[0] == 'I' && cTemp[1] == 'M')
	{
		m_bIsLittleEndian = true;
	}
	else if(cTemp[0] == 'M' && cTemp[1] == 'I')
	{
		m_bIsLittleEndian = false;
	}
	else
	{
		m_fMATFile.Close();
		return false;
	}
				
	// Read the Data Elements
	uint nFileSize = m_fMATFile.GetLength();

	///Soapy 9/02/04	QA6866 READ_COMPRESSED_MAT_DATA_FROM_MATLAB7
	file fMATtemp;
	/// Hong 05/27/07 FIX_IMPORT_MATLAT_FILE_WHEN_ACCESS_NETWORK_NOT_WRITALBE_ALLOW
	//char strTempFileName[] = "mattemp";
	string strTempFileName;
	if ( !GetTempFileName(strTempFileName) )
		return false;
	/// end FIX_IMPORT_MATLAT_FILE_WHEN_ACCESS_NETWORK_NOT_WRITALBE_ALLOW
	
	if(!fMATtemp.Open( strTempFileName, file::modeCreate | file::modeWrite ) || !fMATtemp.IsOpen())
	{
		m_fMATFile.Close();
		return false;
	}
	
	while(nFileSize > m_fMATFile.GetPosition())
	{
		unsigned int	dwSize;
		unsigned long  dwUncompr, dwCompr;
		unsigned int	dwDataTye;
		
		if(1!=m_fMATFile.ReadInt(&dwDataTye, sizeof(int), 1, m_bIsLittleEndian))
		{
			m_fMATFile.Close();
			fMATtemp.Close();
			return false;
		}
	
		if(1!=m_fMATFile.ReadInt(&dwSize, sizeof(int), 1, m_bIsLittleEndian) || dwSize <= 0)
		{
			m_fMATFile.Close();
			fMATtemp.Close();
			return false;
		}
		
		dwCompr = dwSize;
		// Fisher 2008-4-22 QA80-11442	MAT_FILE_IMPORT_INCORRECT 
		// dwUncompr = dwSize * 24;
		dwUncompr = dwSize * 16; 
		// END MAT_FILE_IMPORT_INCORRECT
		
		if(dwDataTye == MAT_DataType_miCOMPRESSED)
		{
			vector<byte> vbuffer(dwCompr), vUncompr(dwUncompr);
			m_fMATFile.Read(vbuffer, dwSize);
			
			// Fisher 2008-4-22 QA80-11442	MAT_FILE_IMPORT_INCORRECT 
			// uncompress(vUncompr, &dwUncompr, vbuffer, dwCompr); ///Soapy 9/10/04	QA6866 USE_OZLIB_DLL
			int oo = 4, nRet = uncompress(vUncompr, &dwUncompr, vbuffer, dwCompr);
			while( Z_BUF_ERROR == nRet ){
				if( oo >= 24 || dwUncompr < 0 )
					return false;
				dwUncompr = dwSize << ++oo;

				vUncompr.SetSize(dwUncompr);
				nRet = uncompress(vUncompr, &dwUncompr, vbuffer, dwCompr);
			}
			// END MAT_FILE_IMPORT_INCORRECT

			fMATtemp.Write(vUncompr, dwUncompr);
		}
		else if(dwDataTye == MAT_DataType_miMATRIX)
		{
			dwSize += 2 * sizeof(int); 
			vector<byte> vbuffer(dwSize);
			m_fMATFile.Seek(-2 * sizeof(int), file::current);
			m_fMATFile.Read(vbuffer, dwSize);
			fMATtemp.Write(vbuffer, dwSize);
		}
		else
		{
			m_fMATFile.Close();
			fMATtemp.Close();
			return false;
		}
	}
	
	m_fMATFile.Close();
	fMATtemp.Close();
	
	if(!m_fMATFile.Open( strTempFileName, file::modeRead ) || !m_fMATFile.IsOpen())
		return false;			
		
	nFileSize = m_fMATFile.GetLength();
	///END READ_COMPRESSED_MAT_DATA_FROM_MATLAB7
	
	while(nFileSize > m_fMATFile.GetPosition())
	{
		CDataElement* FileElement = new CDataElement(m_bIsLittleEndian,&m_fMATFile);
		///Sophy 9/21/2009 QA80-14336 MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
		//if(!FileElement->GetData())
		//{
			//Reset();
			//NICE_REMOVE(FileElement);
			//return false;
		//}
		//
		//SetDataInfoToTree(FileElement);
		//m_Data.Add(FileElement);
		int nErrCode = FileElement->GetData();
		if ( RD_NO_ERROR == nErrCode )
		{
			SetDataInfoToTree(FileElement);
			m_Data.Add(FileElement);
		}
		else if ( RD_TYPE_NOT_SUPPORT == nErrCode || RD_DIM_NOT_SUPPORT == nErrCode ) //some variable is not supported and skipped
		{
			m_nSkippedVars++;
		}
		else	//other kinds of error
		{
			Reset();
			NICE_REMOVE(FileElement);
			return false;
		}
		///end MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
	}
	
	m_fMATFile.Close();	
	DeleteFile(strTempFileName);
	
	m_bReadFileStatus = true;
	///Sophy 9/21/2009 QA80-14336 MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
	if ( m_nSkippedVars > 0 )
	{
		string strMsg;
		strMsg.Format(STR_SKIP_DATASET_WARN_FMT, lpcstrFileName);
		out_str(strMsg);
	}
	///end MORE_WORK_ON_HANDLE_NOT_SUPPORTED_DATA
	return true;
}

/*---- The following functions should be called after the CMATFile::ReadMATFile() and
		 m_bReadFileStatus = true ------
*/
bool CMATFile::SetMatrix(uint nIndex, matrix& mReal, matrix& mImaginary)
{
	if(!m_bReadFileStatus)
		return false;
	
	CDataElement* pDataElement = m_Data.GetAt(nIndex);
	if((!pDataElement)||(!pDataElement->m_bImportable))
		return false;
	
	int nRows = pDataElement->m_vDimesion[0];
	int nCols = pDataElement->m_vDimesion[1];
	
	/// Hong 04/24/07 QA80-9594 v8.0607 FIX_RUNTIME_ERROR_WHEN_EMPTY_VARIABLE
	if ( 0 == nRows || 0 == nCols )
		return false;
	/// end FIX_RUNTIME_ERROR_WHEN_EMPTY_VARIABLE
	mReal.SetSize(nRows, nCols);
	mImaginary.SetSize(nRows, nCols);
	
	mReal.SetByVector(pDataElement->m_vReal, false);
	if(pDataElement->m_dwDataType >= MAT_ARRAY_mxCOMPLEX_CLASS) ///Soapy 10/09/04	QA6866	ADD_ARRAT_TYPE_SPARSE_COMPLEX
	{
		mImaginary.SetByVector(pDataElement->m_vImaginary, false);
	}
	else
	{
		mImaginary = 0;
	}
	
	return true;
}

/// Hong 04/09/07 QA80-9594 MORE_IN_80_IMPORT_MATLAB
bool CMATFile::ImportToWks(Page& pg, TreeNode& trFileNode, TreeNode& trInfo)
{
	Worksheet wksTemp = pg.Layers();
	
	matrix mReal, mImaginary;	
	///---Sim 04-25-2007 QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
	matrix<complex> mComplex; 
	matrixbase *pMatBase;
	///---END QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
	//for(int ii = 0; ii<vecIndex.GetSize(); ii++)
	int nIndex = 0;
	/// Hong 10/19/07 QA80-10564 FIX_REDUNDANT_EMPTY_COLUMN
	//bool bFirstVar = true;	
	int nC1Wks = 0;	
	int nImportedNumCols = 0;
	/// end FIX_REDUNDANT_EMPTY_COLUMN
	foreach (TreeNode tn in trFileNode.Children)
	{	
		if ( tn.nVal )
		{
			TreeNode trNode = m_trFileInfo.Children.Item(nIndex);
			
			string strName;
			//m_fMatFile.SetMatrix(vecIndex[ii], mReal, mImaginary);
			///Sophy 12/7/2009 QA80-9594-P3 EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
			//SetMatrix(trNode.ID, mReal, mImaginary);
			if ( !SetMatrix(trNode.ID, mReal, mImaginary) )
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			///end EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR

			///---Sim 04-25-2007 QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
			/// Hong 10/19/07 QA80-10564 FIX_REDUNDANT_EMPTY_COLUMN
			/*
			int nNameCount = wksTemp.GetNumCols();
			
			if (bFirstVar)
			{
				nNameCount = 0;
				bFirstVar = false;
			}
			*/
			nC1Wks += nImportedNumCols;
			/// end FIX_REDUNDANT_EMPTY_COLUMN
			//nNameCount += MatToWks(mReal, wksTemp, vStrName[ii]);
			//nNameCount += MatToWks(mReal, wksTemp, strName);
			//mReal.CopyTo(wksTemp, 0, 0, -1, -1, 0, nNameCount, false);
			
			if ( trNode.Type.strVal.Find("COMPLEX", 0) == -1 )
			{// real type
				pMatBase = &mReal;
			}
			else
			{// complex type
				mComplex.MakeComplex(mReal, mImaginary);
				pMatBase = &mComplex;
			}
			//pMatBase->CopyTo(wksTemp, 0, 0, -1, -1, 0, nNameCount, false);
			pMatBase->CopyTo(wksTemp, 0, 0, -1, -1, 0, nC1Wks, false); /// Hong 10/19/07 QA80-10564 FIX_REDUNDANT_EMPTY_COLUMN
			///---END QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
			
			/// Hong 10/19/07 QA80-10564 FIX_REDUNDANT_EMPTY_COLUMN
			//for (int ii=nNameCount; ii < wksTemp.GetNumCols(); ii++)
			//{
				//strName.Format("%s %d", trNode.tagName, ii - nNameCount + 1);
				//wksTemp.Columns(ii).SetName(strName);
			nImportedNumCols = pMatBase->GetNumCols();
			for (int ii = 0; ii < nImportedNumCols; ii++)
			{
				strName.Format("%s %d", trNode.tagName, ii + 1);
				///Sophy 12/3/2009 QA80-9594-P8 ENUM_SHORTNAME_SLOW_DOWN_IMPORT_AND_BETTER_SET_LONGNAME
				//wksTemp.Columns(nC1Wks + ii).SetName(strName, OCD_ENUM_NEXT);
				wksTemp.Columns(nC1Wks + ii).SetLongName(strName);
				///end ENUM_SHORTNAME_SLOW_DOWN_IMPORT_AND_BETTER_SET_LONGNAME
			/// end FIX_REDUNDANT_EMPTY_COLUMN				
			}
		}
		nIndex++;
	}
	/// Hong 05/30/07 v8.0629b MORE_IMPORT_INFO_FOR_ORGANIZER
	m_orng.Add(wksTemp, 0, STR_RANGE_STR, wksTemp.GetNumCols() - 1);
	/// end MORE_IMPORT_INFO_FOR_ORGANIZER
	return true;
}

bool CMATFile::ImportToMatrix(Page& pg, TreeNode& trFileNode, TreeNode& trInfo)
{
	matrix mReal, mImaginary;
	///---Sim 04-25-2007 QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
	matrix<complex> mComplex; 
	///---END QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
	
	int nIndex = 0;
	int nNumLayer = 0;
	foreach (TreeNode tn in trFileNode.Children)
	{
		if (tn.nVal)
		{
			if ( nNumLayer == pg.Layers.Count() )
				pg.AddLayer();
			
			TreeNode trNode = m_trFileInfo.Children.Item(nIndex);
			
			string strName = trNode.tagName;
			//m_fMatFile.SetMatrix(vecIndex[ii], mReal, mImaginary);
			///Sophy 12/7/2009 QA80-9594-P3 EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
			//SetMatrix(trNode.ID, mReal, mImaginary);
			if ( !SetMatrix(trNode.ID, mReal, mImaginary) )
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			///end EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
					
			///---Sim 04-25-2007 QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
			MatrixLayer mlay(pg.Layers(nNumLayer));
			mlay.SetName(strName, OCD_ENUM_NEXT);
			
			/// Hong 05/30/07 v8.0629b MORE_IMPORT_INFO_FOR_ORGANIZER
			m_orng.Add(mlay, 0, STR_RANGE_STR);
			/// end MORE_IMPORT_INFO_FOR_ORGANIZER
	
			//if(vbIsComplex[ii] == 0)
			if ( trNode.Type.strVal.Find("COMPLEX", 0) == -1 )
			{// real type
				//MatrixLayer mlay(pg.Layers(nNumLayer));
				//mlay.SetName(strName, OCD_ENUM_NEXT);
				//get_Origin_matrix(vStrName[ii], mlay);
				//get_Origin_matrix(strName, mlay);
				mlay.SetInternalData(FSI_DOUBLE);
				
				Matrix<double> mat(mlay);
				mat = mReal;
			}
			else
			{// complex type
				//MatrixLayer mlay_r, mlay_i;
				//mlay_r = pg.Layers(nNumLayer);
				//mlay_r.SetName(strName+"r", OCD_ENUM_NEXT);
				//pg.AddLayer();
				//nNumLayer++;
				//mlay_i = pg.Layers(nNumLayer);
				//mlay_i.SetName(strName+"i", OCD_ENUM_NEXT);
				//
				//Matrix<double> matr(mlay_r), mati(mlay_i);
				//matr = mReal;
				//mati = mImaginary;
				mlay.SetInternalData(FSI_COMPLEX);
				
				mComplex.MakeComplex(mReal, mImaginary);
				
				Matrix<complex> mat(mlay);
				mat = mComplex;
			}
			///---END QA80-9594-P1 FIX_DEAL_WITH_COMPLEX
			
			nNumLayer++;
		}
		nIndex++;
	}
		
	return true;
}
/// end MORE_IN_80_IMPORT_MATLAB

/// Hong 04/09/07 QA80-9594 MORE_IN_80_IMPORT_MATLAB
bool CMATFile::ImportMatFile(LPCSTR lpcstrFileName, Page& pg, TreeNode& trFileNode, TreeNode& trInfo)
{
	if(!m_bReadFileStatus)
		ReadMATFile(lpcstrFileName);
	
	if ( EXIST_WKS == pg.GetType() )
	{
		return ImportToWks(pg, trFileNode, trInfo);
	}
	else
	{
		return ImportToMatrix(pg, trFileNode, trInfo);
	}
	
	return true;
}
/// end MORE_IN_80_IMPORT_MATLAB

///Sophy 10/16/2009 QA80-14325 MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT
bool CMATFile::AppendRows(LPCSTR lpcszFileName, WorksheetPage& wksPage, TreeNode& trFileNode, int iStartCol) //0
{
	if ( !ReadMATFile(lpcszFileName) )
		return false;
	
	if ( !wksPage )
		wksPage.Create("Origin");
	
	Worksheet wksLayer = wksPage.Layers(); //get active layer.
	///Sophy 1/18/2010 QA80-14325-P3 NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
	matrix mReal, mImaginary;
	matrix<complex> mComplex;
	matrixbase* pMatBase;
	int iR1, iStartRow, nImportedCols = 0;
	wksLayer.GetRange(iR1, iStartRow);
	if ( iStartRow > 0 )
		iStartRow += 1; //start from next row;
	///end NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
	int nIndex = 0;
	int nColIndex = 0;
	foreach (TreeNode tn in trFileNode.vars.Children)
	{	
		if ( tn.nVal )
		{
			TreeNode trNode =  m_trFileInfo.Children.Item(nIndex);
			CDataElement* pDataElement = m_Data.GetAt(trNode.ID);
			if((!pDataElement)||(!pDataElement->m_bImportable))
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			///Sophy 1/18/2010 QA80-14325-P3 NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
			/*
			int nRows = pDataElement->m_vDimesion[0];
			int nCols = pDataElement->m_vDimesion[1];
			
			///Sophy 12/7/2009 QA80-9594-P3 EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
			if ( 0 == nRows || 0 == nCols )
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			///end EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
			
			///Sophy 11/26/2009 IMPROVE_SPEED_ON_APPEND_ROWS_METHOD
			//vector vResult;
			//vector<string> vs;
			//vResult = pDataElement->m_vReal;
			//convert_double_vector_to_string_vector(vResult, vs, vResult.GetSize());
			//wksLayer.SetCells(vs, -1, nColIndex++);
			if ( nColIndex >= wksLayer.GetNumCols() )
				wksLayer.SetSize(-1, nColIndex + 1);
			Column col(wksLayer, nColIndex++);
			if ( pDataElement->m_dwDataType < MAT_ARRAY_mxCOMPLEX_CLASS )
			{
				vector vResult;
				vResult = pDataElement->m_vReal;
				vectorbase& vbInternal = col.GetDataObject();
				vbInternal.Append(vResult);
			}
			else
			{
				col.SetInternalDataType(FSI_COMPLEX);
				vector<complex> vComplex(pDataElement->m_vReal, pDataElement->m_vImaginary);
				vectorbase& vbInternal = col.GetDataObject();
				vbInternal.Append(vComplex);
			}
			///end IMPROVE_SPEED_ON_APPEND_ROWS_METHOD
			UpdateUserTree(col, lpcszFileName, nIndex, TRUE); ///Sophy 12/3/2009 QA80-14325-S3 UPDATE_USEFUL_VARIABLE_INFO_AFTER_IMPORT
			*/
			if ( !SetMatrix(trNode.ID, mReal, mImaginary) )
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			iStartCol += nImportedCols;
			if ( pDataElement->m_dwDataType < MAT_ARRAY_mxCOMPLEX_CLASS )
			{
				pMatBase = &mReal;
			}
			else
			{
				mComplex.MakeComplex(mReal, mImaginary);
				pMatBase = &mComplex;
			}
			pMatBase->CopyTo(wksLayer, 0, 0, -1, -1, iStartRow, iStartCol, false);
			UpdateUserTree(wksLayer.Columns(iStartCol), lpcszFileName, nIndex, TRUE); //only need to update the first column
			
			nImportedCols = pMatBase->GetNumCols();
			string	strColName;
			for ( int iSubCol = 0; iSubCol < nImportedCols; iSubCol++ )
			{
				strColName.Format("%s %d", trNode.tagName, iSubCol + 1);
				wksLayer.Columns(iStartCol + iSubCol).SetLongName(strColName);
			}
			///end NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
		}
		nIndex++;
	}
	autosize_rowcol(wksLayer);
	return true;
}
	
bool CMATFile::AppendCols(LPCSTR lpcszFileName, WorksheetPage& wksPage, TreeNode& trFileNode, int iStartRow) //0
{
	if ( !ReadMATFile(lpcszFileName) )
		return false;
	
	if ( !wksPage )
		wksPage.Create("Origin");
	
	Worksheet wksLayer = wksPage.Layers(); //get active layer.
	int iCols = wks_find_empty_column(wksLayer, 0);
	if ( iCols < 0 ) //no empty column
	{
		wksLayer.SetSize(-1, wksLayer.GetNumCols() + 1);
		iCols = wksLayer.GetNumCols() - 1;
	}
	///Sophy 1/18/2010 QA80-14325-P3 NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
	int nImportedCols = 0;
	matrix mReal, mImaginary;
	matrix<complex>	mComplex;
	matrixbase* pMatBase;
	///end NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
	int nIndex = 0;
	foreach (TreeNode tn in trFileNode.vars.Children)
	{	
		if ( tn.nVal )
		{
			TreeNode trNode = m_trFileInfo.Children.Item(nIndex);
			CDataElement* pDataElement = m_Data.GetAt(trNode.ID);
			if((!pDataElement)||(!pDataElement->m_bImportable))
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			///Sophy 1/18/2010 QA80-14325-P3 NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
			/*
			int nRows = pDataElement->m_vDimesion[0];
			int nCols = pDataElement->m_vDimesion[1];
			
			///Sophy 12/7/2009 QA80-9594-P3 EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
			if ( 0 == nRows || 0 == nCols )
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			///end EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
			
			if ( iCols >= wksLayer.GetNumCols() )
				wksLayer.SetSize(-1, iCols + 1);
			
			Column col(wksLayer, iCols++);
			if ( pDataElement->m_dwDataType < MAT_ARRAY_mxCOMPLEX_CLASS )
			{
				vectorbase& vInternal = col.GetDataObject();
				vInternal = pDataElement->m_vReal;
			}
			else
			{
				col.SetInternalDataType(FSI_COMPLEX);
				vectorbase& vInternal = col.GetDataObject();
				vector<complex> vComplex(pDataElement->m_vReal, pDataElement->m_vImaginary);
				vInternal = vComplex;
			}
			UpdateUserTree(col, lpcszFileName, nIndex, TRUE); ///Sophy 12/3/2009 QA80-14325-S3 UPDATE_USEFUL_VARIABLE_INFO_AFTER_IMPORT
			*/
			if ( !SetMatrix(trNode.ID, mReal, mImaginary) )
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			iCols += nImportedCols;
			if ( pDataElement->m_dwDataType < MAT_ARRAY_mxCOMPLEX_CLASS )
			{
				pMatBase = &mReal;
			}
			else
			{
				mComplex.MakeComplex(mReal, mImaginary);
				pMatBase = &mComplex;
			}
			pMatBase->CopyTo(wksLayer, 0, 0, -1, -1, iStartRow, iCols, false);
			UpdateUserTree(wksLayer.Columns(iCols), lpcszFileName, nIndex, TRUE); //only need to update the first column
			
			nImportedCols = pMatBase->GetNumCols();
			string	strColName;
			for ( int iSubCol = 0; iSubCol < nImportedCols; iSubCol++ )
			{
				strColName.Format("%s %d", trNode.tagName, iSubCol + 1);
				wksLayer.Columns(iCols + iSubCol).SetLongName(strColName);
			}
			///end NEW_OUTPUT_OPTION_SHOULD_KEEP_ORIGINAL_DATA_DIMENSION_INFO
		}
		nIndex++;
	}
	autosize_rowcol(wksLayer);
	return true;
}
	
bool CMATFile::AppendWorksheet(LPCSTR lpcszFileName, WorksheetPage& wksPage, TreeNode& trFileNode)
{
	if ( !wksPage )
		return false;
	
	Worksheet wksLayer = wksPage.Layers(wksPage.Layers.Count() - 1); //get last layer;
	if ( !is_sheet_empty(wksLayer) )
	{
		int iLayer = wksPage.AddLayer();
		wksLayer = wksPage.Layers(iLayer);
	}
	wksLayer.CheckShowActivate(); //activate it

	return AppendCols(lpcszFileName, wksPage, trFileNode);
}
	
bool CMATFile::AppendMatrixsheet(LPCSTR lpcszFileName, MatrixPage& matPage, TreeNode& trFileNode)
{
	//assume the page should be prepared outside
	if ( !matPage || !ReadMATFile(lpcszFileName) )
		return false;
	
	matrix mReal, mImaginary;
	matrix<complex> mComplex;
	int nIndex = 0;
	foreach (TreeNode tn in trFileNode.vars.Children)
	{
		if (tn.nVal)
		{
			int iLayer = matPage.Layers.Count() - 1; //last layer;
			MatrixLayer ml = matPage.Layers(iLayer);
			if ( !is_sheet_empty(ml) )
			{
				iLayer = matPage.AddLayer();
				ml = matPage.Layers(iLayer);
			}
		
			TreeNode trNode = m_trFileInfo.Children.Item(nIndex);
			
			string strName = trNode.tagName;
			///Sophy 12/7/2009 QA80-9594-P3 EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
			//SetMatrix(trNode.ID, mReal, mImaginary);
			if ( !SetMatrix(trNode.ID, mReal, mImaginary) )
			{
				nIndex++;
				m_nSkippedVars++;
				continue;
			}
			///end EMPTY_VARIABLE_CAUSE_RUNTIME_ERROR
					
			ml.SetName(strName, OCD_ENUM_NEXT);
			
			m_orng.Add(ml, 0, STR_RANGE_STR);
	
			if ( trNode.Type.strVal.Find("COMPLEX", 0) == -1 )
			{// real type
				ml.SetInternalData(FSI_DOUBLE);
				Matrix<double> mat(ml);
				mat = mReal;
			}
			else
			{// complex type
				ml.SetInternalData(FSI_COMPLEX);
				
				mComplex.MakeComplex(mReal, mImaginary);
				
				Matrix<complex> mat(ml);
				mat = mComplex;
			}
			UpdateUserTree(ml, lpcszFileName, nIndex, TRUE); ///Sophy 12/3/2009 QA80-14325-S3 UPDATE_USEFUL_VARIABLE_INFO_AFTER_IMPORT
		}
		nIndex++;
	}
	matPage.Refresh();
	return true;
}

///Sophy 12/3/2009 QA80-14325-S3 UPDATE_USEFUL_VARIABLE_INFO_AFTER_IMPORT
#define	STR_VARIABLES	"Variables"
#define	STR_FILES		"Files"

bool CMATFile::UpdateUserTree(OriginObject& obj, LPCSTR lpcszFileName, int nVarIndex, BOOL bMultipleVars) //FALSE
{
	TreeNode trNode = m_trFileInfo.Children.Item(nVarIndex);
	if ( !trNode || !obj )
		return false;
	
	string strLabel;
	if ( !trNode.GetAttribute(STR_LABEL_ATTRIB, strLabel) )
		strLabel = trNode.tagName;
	
	trNode.Imported.Remove(); //no need to save this node.
	trNode.FileName.strVal = lpcszFileName;
	trNode.FileName.SetAttribute(STR_LABEL_ATTRIB, _L("File Name"));
	
	tree_set_attribute_to_all_nodes(trNode, STR_ENABLE_ATTRIB, "2");
	if ( bMultipleVars )
	{
		Tree trVars;
		string strVarsNode = STR_VARIABLES;
		if ( !get_user_info(obj, strVarsNode, trVars) )
			trVars.Reset();
		if ( trVars.Children.Count() > 0 )
		{
			string strOld;
			strOld = obj.GetLongName();
			strLabel = strOld + ", " + strLabel;
		}
		trVars.AddNode(trNode);
		obj.SetLongName(strLabel);
		set_user_info(obj, strVarsNode, trVars);
		return true;
	}
	set_user_info(obj, strLabel, trNode); //single variable
	return true;
}

bool CMATFile::UpdateFileTree(OriginObject& obj, LPCSTR lpcszFileName, int nImportMode)
{
	if ( !obj )
		return false;
	
	Tree trFiles;
	string strFiles = STR_FILES;
	if ( !get_user_info(obj, strFiles, trFiles) )
		trFiles.Reset();
	
	string strFileName(lpcszFileName);
	strFileName.MakeValidCName();
	TreeNode trFile = trFiles.AddNode(strFileName);
	trFile.SetAttribute(STR_LABEL_ATTRIB, GetFileName(lpcszFileName, TRUE));
	
	string strImpModeList = IMPMODE_OPTIONS_LIST;
	vector<string> vsModes;
	strImpModeList.GetTokens(vsModes, '|');
	if ( nImportMode >= vsModes.GetSize() )
		return false;
	trFile.ImportMode.strVal = vsModes[nImportMode];
	trFile.ImportMode.SetAttribute(STR_LABEL_ATTRIB, _L("Import Mode"));
	impinfo_FileInfoToFileNode(trFile, lpcszFileName, FILTER_TYPE_XFUNC, NULL);
	trFile.Info.FileType.strVal = "Matlab";
	
	tree_set_attribute_to_all_nodes(trFile, STR_ENABLE_ATTRIB, "2");
		
	set_user_info(obj, strFiles, trFiles);
	return true;
}

///end UPDATE_USEFUL_VARIABLE_INFO_AFTER_IMPORT

///Sophy 12/17/2009 QA80-14325-P2 LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET
#define	STR_LABTALK_VAR_ISE	"@ISE"
bool CMATFile::GetTargetPage(PageBase& pg, int nImportMode, int iFile)
{
	if ( nImportMode == IMPMODE_START_NEW_ROWS || nImportMode == IMPMODE_START_NEW_COLS )
		return false; //no need to handle
	
	if ( iFile == 0 ) //need to check whether skip empty layer
	{
		double dISE = 0;
		LT_get_var(STR_LABTALK_VAR_ISE, &dISE);

		Page wmPage(pg);
		if ( wmPage && wmPage.Layers.Count() > 0 && is_sheet_empty(wmPage.Layers(wmPage.Layers.Count() - 1)) && dISE == 0 )
			return false; //can use existing empty layer

		if ( IMPMODE_ALL_TO_ONE_WKS == nImportMode || IMPMODE_ONE_TO_ONE_WKS == nImportMode )
		{
			if ( !wmPage )
			{
				WorksheetPage wksPage;
				wksPage.Create("Origin");
				pg = wksPage;
				return true;
			}
			if ( wmPage.Layers.Count() > 0 && is_sheet_empty(wmPage.Layers(wmPage.Layers.Count() - 1)) )
			{
				if ( IMPMODE_ALL_TO_ONE_WKS == nImportMode && dISE != 0 )
				{
					wmPage.AddLayer();	
					return false;
				}
				else if ( IMPMODE_ONE_TO_ONE_WKS == nImportMode && dISE != 0 )
				{
					WorksheetPage wksPage;
					wksPage.Create("Origin");
					pg = wksPage;
					return true;
				}
				return false; //no need to change anything
			}
			if ( IMPMODE_ALL_TO_ONE_WKS == nImportMode )
			{
				 wmPage.AddLayer();
				 return false;
			}
			else
			{
				WorksheetPage wksPage;
				wksPage.Create("Origin");
				pg = wksPage;
			}
		}
		else if ( IMPMODE_ALL_TO_ONE_MAT == nImportMode || IMPMODE_ONE_TO_ONE_MAT == nImportMode )
		{
			if ( !wmPage )
			{
				MatrixPage matPage;
				matPage.Create("Origin");
				pg = matPage;
				return true;
			}
			if ( wmPage.Layers.Count() > 0 && is_sheet_empty(wmPage.Layers(wmPage.Layers.Count() - 1)) )
			{
				if ( IMPMODE_ALL_TO_ONE_MAT == nImportMode && dISE != 0 )
				{
					wmPage.AddLayer();	
					return false;
				}
				else if ( IMPMODE_ONE_TO_ONE_MAT == nImportMode && dISE != 0 )
				{
					MatrixPage matPage;
					matPage.Create("Origin");
					pg = matPage;
					return true;
				}
				return false;
			}
			if ( IMPMODE_ALL_TO_ONE_MAT == nImportMode )
			{
				 wmPage.AddLayer();
				 return false;
			}
			else
			{
				MatrixPage matPage;
				matPage.Create("Origin");
				pg = matPage;
				return true;
			}
		}
		else
		{
			ASSERT(FALSE); //should not come here.
			return false;
		}
		return true;
	}
	//iFile != 0, here append layers to same book doesn't need to create new book.
	if ( IMPMODE_ONE_TO_ONE_WKS == nImportMode )
	{
		WorksheetPage wksPage;
		wksPage.Create("Origin");
		pg = wksPage;
	}
	if ( IMPMODE_ONE_TO_ONE_MAT == nImportMode )
	{
		MatrixPage matPage;
		matPage.Create("Origin");
		pg = matPage;
	}
	return true;
}
///end LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET

bool CMATFile::ImportMatFiles(const vector<string>& vsFiles, WorksheetPage& wksPage, MatrixPage& matPage, TreeNode& trFiles, int nImportMode)
{
	if ( nImportMode == IMPMODE_START_NEW_BOOKS ) //should use old code
		return false;
	
	int iFile = 0;
	bool bRet = true;
	for ( iFile = 0; iFile < vsFiles.GetSize(); iFile++ )
	{
		TreeNode trFileSetting = trFiles.Children.Item(iFile);
		Page pg;
		switch(nImportMode)
		{
		case IMPMODE_START_NEW_ROWS:
			bRet &= AppendRows(vsFiles[iFile], wksPage, trFileSetting);
			if ( bRet )
				pg = wksPage;
			break;
			
		case IMPMODE_START_NEW_COLS:
			bRet &= AppendCols(vsFiles[iFile], wksPage, trFileSetting);
			if ( bRet )
				pg = wksPage;
			break;
			
		case IMPMODE_ALL_TO_ONE_WKS:
			///Sophy 12/17/2009 QA80-14325-P2 LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET
			GetTargetPage(wksPage, nImportMode, iFile);
			///end LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEETb
			bRet &= AppendWorksheet(vsFiles[iFile], wksPage, trFileSetting);
			if ( bRet )
				pg = wksPage;
			break;
			
		case IMPMODE_ONE_TO_ONE_WKS:
			///Sophy 12/17/2009 QA80-14325-P2 LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET
			//WorksheetPage wksNew;
			//if ( wksPage && wksPage.Layers.Count() == 1 && is_sheet_empty(wksPage.Layers(0)) )
				//wksNew = wksPage;
			//if ( !wksNew )
				//wksNew.Create("Origin");
			GetTargetPage(wksPage, nImportMode, iFile);
			///end LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEETb
			bRet &= AppendWorksheet(vsFiles[iFile], wksPage, trFileSetting);
			if ( bRet )
				pg = wksPage;
			break;
			
		case IMPMODE_ALL_TO_ONE_MAT:
			///Sophy 12/17/2009 QA80-14325-P2 LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET
			//if ( !matPage )
				//matPage.Create("Origin");
			GetTargetPage(matPage, nImportMode, iFile);
			///end LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEETb
			bRet &= AppendMatrixsheet(vsFiles[iFile], matPage, trFileSetting);
			if ( bRet )
				pg = matPage;
			break;
			
		case IMPMODE_ONE_TO_ONE_MAT:
			///Sophy 12/17/2009 QA80-14325-P2 LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEET
			//MatrixPage mpNew;
			//if ( matPage && matPage.Layers.Count() == 1 && is_sheet_empty(matPage.Layers(0)) )
				//mpNew = matPage; //first file that may not need new matrix page
			//if ( !mpNew )
				//mpNew.Create("Origin");
			GetTargetPage(matPage, nImportMode, iFile);
			///end LABTALK_CTRL_TO_SKIP_EMPTY_BOOKSHEETb
			bRet &= AppendMatrixsheet(vsFiles[iFile], matPage, trFileSetting);
			if ( bRet )
				pg = matPage;
			break;
			
		default:
			ASSERT(false);
			break;
		}
		UpdateFileTree(pg, vsFiles[iFile], nImportMode);
	}
	return bRet;
}
///end MAKE_OUTPUT_SELECTABLE_FOR_MATLAB_IMPORT

// Get all the data elements Origin support
bool CMATFile::GetAllImportable(vector<uint>& vecIndex)
{
	if(!m_bReadFileStatus)
		return false;
		
	vecIndex.SetSize(0);
		
	for(int ii=0; ii<m_Data.GetSize(); ii++)
	{
		CDataElement* pDataElement = m_Data.GetAt(ii);
		if(pDataElement->m_bImportable)
			vecIndex.Add(ii);
	}
		
	return true;
}
	
// Get all the real data elements Origin support
bool CMATFile::GetAllRealImportable(vector<uint>& vecIndex)
{
	if(!m_bReadFileStatus)
		return false;
		
	vecIndex.SetSize(0);
		
	for(int ii=0; ii<m_Data.GetSize(); ii++)
	{
		CDataElement* pDataElement = m_Data.GetAt(ii);
		if(pDataElement->m_bImportable && (pDataElement->m_dwDataType < MAT_ARRAY_mxCOMPLEX_CLASS))///Soapy 10/09/04	QA6866	ADD_ARRAT_TYPE_SPARSE_COMPLEX
			vecIndex.Add(ii);
	}
		
	return true;
}
	
void CMATFile::OutMatFileInfo()
{
	out_tree(m_trFileInfo);
}

#endif  //_CMATFILE_H


	
	
