/*------------------------------------------------------------------------------*
 * File Name: minitab.c															*
 * Creation: GD 7/10/2001														*
 * Purpose: Origin C file														*
 * Copyright (c) OriginLab Corp. 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007	*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 * EJP 11-11-2003 v7.5750 QA70-5516 CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
// you must include this header file for all Origin built-in functions and classes
#include <origin.h>
//
////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////
// start your functions here

#include "minitab.h"

#define		DATATYPE_UNKNOWN		0
#define		DATATYPE_NUMERIC		1
#define		DATATYPE_DATE			2
#define		DATATYPE_TEXT			3

#define		SORTORDER_ALPHA			0
#define		SORTORDER_OCCUR			1
#define		SORTORDER_USER			2

#define		MINIMISSING				1.23456E30
#define		DATEOFFSET				2415018


// main
int readminitab( String filename, String strWksName )
{
	file		fid;
	int			iFileSize=0;

	Header1		Hdr1;			// size is 80
	Header2		Hdr2;			// size is 60
	Header3		Hdr3;			// size is 37
	Pre			DataPreamble;	// size is 60
	int			iDataType=0;
	PostN		NumericHdr;		// size is 43
	PostN12		Numeric12Hdr;	// size is 34
	PostD		DateHdr;		// size is 47
	PostD12		Date12Hdr;		// size is 38?
	PostT		TextHdr;		// size is 35
	PostT12		Text12Hdr;		// size is 26

	int			iBytesRead = 0;
	char		cVersion[3];	// file version : 12 and 13 supported
	int			iVersion = 0;	// file version : 12 and 13 supported
	int			iLength = 0;	// Generic, used for string reads
	char		Buffer[256];	// Generic, used for string reads
	int			iLoop = 0;		// Generic, used for loops
	int			iPos = 0;		// Generic, used for file position
	int			idatasize = 0;
	int 		iTemp = 0;
	int			iStatus = 0;
	int			iStrSize = 0;

	const double	DateOffset = 2415018; 	// 2415018 days difference in date systems
	const int		Err1 = 1;				// Failed to open file.
	const int		Err2 = 2;				// Column synchronization failure (v12).
	const int		Err3 = 3;				// Column synchronization failure.
	const int		Err4 = 4;				// Field width and string length do not match.
	const int		Err5 = 5;				// Unknown datatype.
	const int		Err6 = 6;				// File too small.
	const int		Err7 = 7;				// Unsupported version for datatype 0.
	const int		Err9 = 9;				// Special error 9. Contact OriginLab Corporation.

	// Begin
	if( fid.Open(filename, file::modeRead) )
	{
		iFileSize = fid.SeekToEnd();

		// If the file is too small to evaluate, just exit
		if(iFileSize<sizeof(Hdr1)+sizeof(Hdr2)+sizeof(Hdr3)+sizeof(DataPreamble))
		{
			LT_set_var("mntbversion", iVersion);
			LT_execute("type -a MiniTab v$(mntbversion);");
			LT_execute("type $OMRDMNTB.errors.err6;");
			fid.Close();
			return Err6;
		}
		fid.SeekToBegin();
		
		// Read Header1 and determine version
		// I will let pass versions greater than 13 and attempt to read them as v13 (with warning)
		// I will let pass versions less than 12 and attempt to read them as v12 (with warning)
		iBytesRead = fid.Read(&Hdr1, sizeof(Hdr1));
		iVersion=0;
		cVersion[0]=Hdr1.ID[3];
		cVersion[1]=Hdr1.ID[4];
		cVersion[2]='\x00';
		iVersion = atoi(cVersion);
		// Let them try any version - so what if it fails, we warned them!
		if(13 != iVersion && 12 != iVersion) {
			LT_set_var("mntbversion", iVersion);
			LT_execute("type -a MiniTab v$(mntbversion).;");
			LT_execute("type -a $OMRDMNTB.warning.warn1;");
		}
		
		// Setup %Z in LabTalk - which will contain header text information
		// Strings in Minitab are an integer containing Length followed by Length characters
		Buffer[0]=0;
		LT_set_str("%Z", Buffer);
		
		// Read and append Name field (to %Z)
		iBytesRead = fid.Read(&iLength, sizeof(iLength));
		if(iLength)
		{
			fid.Read(&Buffer, iLength);
			Buffer[iLength] = 0;
			LT_set_str("%N",Buffer);
			LT_execute("page.label$=%N;page.title=3;%Z=%Z (%N)");
		}
		
		// Read and append User Name field (to %Z)
		iBytesRead = fid.Read(&iLength, sizeof(iLength));
		if(iLength)
		{
			fid.Read(&Buffer, iLength);
			Buffer[iLength] = 0;
			LT_set_str("%N", Buffer);
			LT_execute("%Z=%Z (%N);");
		}
		
		// Read and append Date (to %Z) - could be any text actually
		iBytesRead = fid.Read(&iLength, sizeof(iLength));
		if(iLength)
		{
			fid.Read(&Buffer, iLength);
			Buffer[iLength] = 0;
			LT_set_str("%N", Buffer);
			LT_execute("%Z=%Z (%N);");
		}
		
		// Read and append Notes (to %Z)
		iBytesRead = fid.Read(&iLength, sizeof(iLength));
		if(iLength)
		{
			fid.Read(&Buffer, iLength);
			Buffer[iLength] = 0;
			LT_set_str("%N", Buffer);
			LT_execute("%Z=%Z (%N);");
		}
		
		// Read Header2 - I don't know what any of this means
		iBytesRead = fid.Read(&Hdr2, sizeof(Hdr2)); // All unknowns

		// Read Data Font - the font for Data display - Don't use, but we could
		iBytesRead = fid.Read(&iLength, sizeof(iLength));
		if(iLength)
		{
			fid.Read(&Buffer, iLength);
			Buffer[iLength] = 0;
		}
		
		// Read Label Font - the font for Label Display - Don't use, and we could not anyway
		iBytesRead = fid.Read(&iLength, sizeof(iLength));
		if(iLength)
		{
			fid.Read(&Buffer, iLength);
			Buffer[iLength] = 0;
		}
		
		// Read Header3 - I don't know what any of this means, either
		iBytesRead = fid.Read(&Hdr3, sizeof(Hdr3)); // More unknowns
		
		// Check for valid column data
		// This corrects a problem with a few version 13 files that are missing Hdr3.var9
		if(13 <= iVersion && 3 == Hdr3.var9)
		{
			// Just backup file pointer
			iPos = fid.GetPosition();
			iPos -= 4;
			fid.Seek(iPos, file::begin);
		}
		
		// This corrects a difference for version 13 files that do not have Hdr3.var8 and Hdr3.var9
		if(12 >= iVersion)
		{
			// Backup file pointer by Hdr3.var8 (a double) and Hdr3.var9 (an integer)
			iPos = fid.GetPosition();
			// following line of code did not compile (sizeof fails with structure member)
			// iPos -= (sizeof(Hdr3.var8) + sizeof(Hdr3.var9));
			iPos -= 12;
			fid.Seek(iPos, file::begin);
			// Now do our test for valid column data. This integer should be 3
			iBytesRead = fid.Read(&iTemp, sizeof(iTemp));
			if(iTemp!=3)
			{
				LT_set_var("mntbversion", iVersion);
				LT_execute("type -a MiniTab v$(mntbversion).;");
				LT_execute("type -a $OMRDMNTB.errors.err2;");
				fid.Close();
				return Err2;
			}
			fid.Seek(iPos, file::begin);
		}
		
		// Setup the worksheet with the correct number of columns
		LT_set_var("ncols",Hdr1.NCols);
		LT_execute("wo -a $(ncols-wks.ncols);");
		
		// Use this to replace MiniTab missing values with Origin missing values
		LT_set_var("MNTBMISSING", MINIMISSING);
		
		// For each column
		for(iLoop = 1;iLoop <= Hdr1.NCols; iLoop++)
		{
			// Read the Preamble - Column Number, Number of Rows and SortOrder flag
			iBytesRead = fid.Read(&DataPreamble, sizeof(DataPreamble));
			
			// This is my test for valid column data - see Check for valid column data
			if(3 != DataPreamble.var1)
			{
				LT_set_var("mntbversion", iVersion);
				LT_execute("type -a MiniTab v$(mntbversion).;");
				LT_execute("type $OMRDMNTB.Errors.Err3;");
				fid.Close();
				return Err3;
			}
			
			// Read the Amble - contains Column Label, Column Description and DataType
			
			// Column Label
			iBytesRead = fid.Read(&iLength, sizeof(iLength));
			if(iLength)
			{
				fid.Read(&Buffer, iLength);
				Buffer[iLength] = 0;
				LT_set_var("mntbthiscol",DataPreamble.ColNum);
				LT_set_str("%N", Buffer);
				LT_execute("wks.col$(mntbthiscol).label$=%N;");
			}
			
			// Column Description
			iBytesRead = fid.Read(&iLength, sizeof(iLength));
			if(iLength)
			{
				fid.Read(&Buffer, iLength);
				Buffer[iLength] = 0;
				LT_set_str("%N", Buffer);
				LT_execute("%Z=%Z (%N);");
			}
			
			// Column DataType
			iBytesRead = fid.Read(&iDataType, sizeof(iDataType));
			
			// Read the Postamble based on the DataType - the actual data is read here
			switch(iDataType)
			{
				case DATATYPE_UNKNOWN:
					if(12 == iVersion || 13 == iVersion)
					{
							if(12 == iVersion)
							{
								iBytesRead = fid.Read(&Buffer, 26);
							}
							else
							{
								iBytesRead = fid.Read(&NumericHdr, sizeof(NumericHdr));
							}
					}
					else
					{
							LT_set_var("mntbversion", iVersion);
							LT_execute("type -a MiniTab v$(mntbversion).;");
							LT_execute("type -a $OMRDMNTB.errors.err7;");
							fid.Close();
							return Err7;
					}
					break;
					
				case DATATYPE_NUMERIC:
					if(13 <= iVersion)
					{	
						iBytesRead = fid.Read(&NumericHdr, sizeof(NumericHdr));
						LT_set_var("mntbthiscol",DataPreamble.ColNum);
						LT_set_var("mntbthiswidth", NumericHdr.Width);
						LT_execute("wks.col$(mntbthiscol).width=mntbthiswidth;");
					}
					else
					{
						iBytesRead = fid.Read(&Numeric12Hdr, sizeof(Numeric12Hdr));
					}
					iStatus = read_numeric_data(fid, strWksName, DataPreamble.ColNum, DataPreamble.Rows1);
					break;
					
				case DATATYPE_DATE:
					if(13 <= iVersion)
					{	
						iBytesRead = fid.Read(&DateHdr, sizeof(DateHdr));
						LT_set_var("mntbthiscol",DataPreamble.ColNum);
						LT_set_var("mntbthiswidth", NumericHdr.Width);
						LT_execute("wks.col$(mntbthiscol).width=mntbthiswidth;");
					}
					else
					{
						iBytesRead = fid.Read(&Date12Hdr, sizeof(Date12Hdr));
					}
					iStatus = read_date_data(fid, strWksName, DataPreamble.ColNum, DataPreamble.Rows1);
					break;
					
				case DATATYPE_TEXT:
					if(13 <= iVersion)
					{	
						iBytesRead = fid.Read(&TextHdr, sizeof(TextHdr));
						iStrSize = TextHdr.NumChars;
						LT_set_var("mntbthiscol",DataPreamble.ColNum);
						LT_set_var("mntbthiswidth", NumericHdr.Width);
						LT_set_var("mntbthislen", iStrSize + 1);
					}
					else
					{
						iBytesRead = fid.Read(&Text12Hdr, sizeof(Text12Hdr));
						iStrSize = Text12Hdr.NumChars;
					}
					iStatus = read_text_data(fid, strWksName, DataPreamble.ColNum, DataPreamble.Rows1, DataPreamble.SortOrder, iStrSize, iVersion);
					break;
					
				default:
					LT_set_var("mntbversion", iVersion);
					LT_execute("type -a Minitab $(mntbversion).;");
					LT_execute("type -a $OMRDMNTB.errors.err5;");
					fid.Close();
					return Err5;
			}	// end of switch based on DATATYPE
		
		}	// end of 'for each column'
		
		fid.Close();
		LT_execute("wks.labels();");
	}	// end if - opening file
	else
	{
		LT_set_var("mntbversion", iVersion);
		LT_execute("type -a MiniTab v$(mntbversion).;");
		LT_execute("type -a $OMRDMNTB.errors.err1");
		fid.Close();
		return Err1;
	}
	return 0;
}


// Read Numeric data into the correct column of the worksheet
int read_numeric_data(file &fid, string TheWorksheet, uint TheColumn, uint NumRows)
{
	int	iBytesRead;
	
	// link to dataset, set column format to Numeric, set Dataset size
	/// EJP 11-11-2003 v7.5750 QA70-5516 CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	///Dataset	ThisCol(TheWorksheet, TheColumn - 1);
	/// end CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	LT_set_var("mntbthiscol",TheColumn);
	LT_execute("wks.col$(mntbthiscol).format=1;");
	/// EJP 11-11-2003 v7.5750 QA70-5516 CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	Dataset	ThisCol(TheWorksheet, TheColumn - 1);
	/// end CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	ThisCol.SetSize(NumRows);
	
	// Read the data into Origin, replace MiniTab Missing with Origin Missing
	iBytesRead = fid.Read(&ThisCol, NumRows * sizeof(double));
	LT_execute("wcol(mntbthiscol)=wcol(mntbthiscol)==MNTBMISSING?0/0:wcol(mntbthiscol);");
	return 0;
}


// Read Date data into the correct column of the worksheet
int read_date_data(file &fid, string strWksName, uint TheColumn, uint NumRows)
{
	int	iBytesRead;
	
	// link to dataset, set column format to Date, set Dataset size
	/// EJP 11-11-2003 v7.5750 QA70-5516 CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	///Dataset	ThisCol(strWksName, TheColumn - 1);
	/// end CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	LT_set_var("mntbthiscol",TheColumn);
	LT_set_var("mntbDateOffset", DATEOFFSET);
	LT_execute("wks.col$(mntbthiscol).format=4;");
	/// EJP 11-11-2003 v7.5750 QA70-5516 CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	Dataset	ThisCol(strWksName, TheColumn - 1);
	/// end CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	ThisCol.SetSize(NumRows);
	
	// Read the data into Origin, replace MiniTab Missing wit Origin Missing
	iBytesRead = fid.Read(&ThisCol, NumRows * sizeof(double));
	LT_execute("wcol(mntbthiscol)=wcol(mntbthiscol)==MNTBMISSING?0/0:wcol(mntbthiscol);");
	// Adjust for MiniTab Date system
	LT_execute("wcol(mntbthiscol)+=mntbDATEOFFSET;");
	return 0;
}


// Read Text data into the correct column of the worksheet
int read_text_data(file &fid, string strWksName, uint TheColumn, uint NumRows, uint TheSortOrder, uint TheStringSize, uint TheVersion)
{
	int 	iLoop = 0;
	int		iBytesRead = 0;
	int		iLength = 0;
	char	Buffer[256];
	int		iPos = 0;
	
	// link to dataset, set column format to Text, set dataset size
	/// EJP 11-11-2003 v7.5750 QA70-5516 CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	///Dataset ThisCol(strWksName, TheColumn - 1);
	///ThisCol.SetSize(NumRows);
	///LT_execute("wks.col$(mntbthiscol).format=2;");
	LT_execute("wks.col$(mntbthiscol).format=7;");
	/// end CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	if(13 <= TheVersion)
	{
		LT_execute("wks.col$(mntbthiscol).width=mntbthiswidth;");
		LT_execute("wks.col$(mntbthiscol).twidth=mntbthislen;");
	}
	/// EJP 11-11-2003 v7.5750 QA70-5516 CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	Dataset ThisCol(strWksName, TheColumn - 1);
	ThisCol.SetSize(NumRows);
	/// end CHANGING_COL_FORMAT_CAUSES_DATASET_TO_DETACH
	
	// For each row
	for(iLoop = 0; iLoop < NumRows; iLoop++)
	{
		iBytesRead = fid.Read(&iLength, sizeof(iLength));
		iBytesRead = fid.Read(&Buffer, iLength);
		Buffer[iLength] = 0;
		ThisCol.SetText(iLoop, Buffer);
	}	// end 'For each row'

	// for Text - check for SortOrder of User AND that the first integer is not 3
	iPos = fid.GetPosition();		// mark position
	iBytesRead = fid.Read(&iLength, sizeof(iLength));
	fid.Seek(iPos, file::begin);	// go back
	
	// Only Text data has this feature, just guessing when datablock is 'doubled
	if(SORTORDER_USER == TheSortOrder && 3 != iLength)
	{
		if(13 <= TheVersion)
		{
			iPos += NumRows * (sizeof(iLength) + TheStringSize);
		}
		else
		{
			iPos += NumRows * (sizeof(iLength) + TheStringSize);
		}
		fid.Seek(iPos, file::begin);
	}
	return 0;
}


// The filter function version
int	ImportMinitab(Page& pgTarget, TreeNode& trFilter, LPCSTR lpcszFile, int nFile)
{
	double	dResult;
	string	strName;

	if(pgTarget.GetType() == EXIST_WKS)
	{
		pgTarget.GetName(strName);
	}
	else
	{
		WorksheetPage	pg;
		pg.Create("origin.otp");
		pg.GetName(strName);
	}
	LT_set_str("%B",strName);
	LT_execute("win -a %B;");
	LT_set_str("%A", lpcszFile );
	LT_execute("run.section(ordMNTB,Init);result = run.section(ordMNTB,Import);");
	LT_get_var("result", &dResult);
	return 0;
}
