/*------------------------------------------------------------------------------*
 * File Name: EarthProbeData.c 													*
 * Creation: July 1, 2002														*
 * Purpose: Import EarthProbe files into a matrix								*
 * Copyright (c) OriginLab Corp. 2002											*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
// you can include just this typical header file for most Origin built-in functions and classes
// and it takes a reasonable amount of time to compile, 
#include <origin.h>
////////////////////////////////////////////////////////////////////////////////////

#define EP_HDR_LINE_LEN				80
#define EP_HDR_LINE_COUNT			3

// The data is stored in the files as a string of numbers.
// Each value is always 3 characters long.
// Example:  123456789234 equals four values 123, 456, 789. and 234
#define EP_DATA_ELEMENT_STR_LEN		3

typedef struct {
	int iDayOfYear;
	int iMonth;
	int iDayOfMonth;
	int iYear;
	int iHour;
	int iMin;
	char szText[80];
	int iLongitudeBinCount;
	double dLongitudeStart;
	double dLongitudeStep;
	int iLatitudeBinCount;
	double dLatitudeStart;
	double dLatitudeStep;
} EPHEADER;

enum {
	EPR_SUCCESS = 0,

	EPR_ERR_FILE_OPEN,
	EPR_ERR_HEADER,
	EPR_ERR_READ_DATA,
	EPR_ERR_NO_FILE_SPECIFIED,
	EPR_ERR_TARGET_MATRIX_NOT_VALID,
	EPR_ERR_UNKNOWN_FILE_EXT,
	EPR_ERR_PARSE_DATA,
};

enum { // EarthProbe file type IDs
	EP_TYPE_UNKNOWN = 0,
	EP_TYPE_AEROSOL,
	EP_TYPE_OZONE,
	EP_TYPE_REFLECTIVE,
	EP_TYPE_UV,
};


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


//--------------------------------------------------------------------------
// EarthProbe_FileExtToType
//
// This function returns an integer that represents the type of the
// EarthProbe file based on the file's extension.
//
// return
// EP_TYPE_* code
//--------------------------------------------------------------------------
int EarthProbe_FileExtToType(LPCSTR lpcstr)
{
	string strFileExt(lpcstr);
	if( 0 == strFileExt.CompareNoCase("epa") )
		return EP_TYPE_AEROSOL;
	if( 0 == strFileExt.CompareNoCase("epe") )
		return EP_TYPE_UV;
	if( 0 == strFileExt.CompareNoCase("epr") )
		return EP_TYPE_REFLECTIVE;
	if( 0 == strFileExt.CompareNoCase("ept") )
		return EP_TYPE_OZONE;
	return EP_TYPE_UNKNOWN;	
}


//--------------------------------------------------------------------------
// monthtoi
//
// Return the index of the named month.  The comparison is case insensative.
// Example: Passing "January" or "JAN" will return 0.
//
// return:
// index of named month or -1 if not a month name.
//--------------------------------------------------------------------------
int monthtoi(LPCSTR lpcstrMonth)
{
/*	string strMonths[] = {
		"january",
		"february",
		"march",
		"april",
		"may",
		"june",
		"july",
		"august",
		"september",
		"october",
		"november",
		"december"
	};
*/
	string strMonths[12];
	strMonths[0] = "january";
	strMonths[1] = "february";
	strMonths[2] = "march";
	strMonths[3] = "april";
	strMonths[4] = "may";
	strMonths[5] = "june";
	strMonths[6] = "july";
	strMonths[7] = "august";
	strMonths[8] = "september";
	strMonths[9] = "october";
	strMonths[10] = "november";
	strMonths[11] = "december";
	
	string strMonth(lpcstrMonth);
	strMonth.MakeLower();
	
	for( int i = 0; i < 12; i++ )
	{
		if( strMonths[i].Find(strMonth) != -1 )
			return i;
	}
	return -1;
}


//--------------------------------------------------------------------------
// EarthProbe_ReadHeader
//
// This function reads the 3 lines of header, extracts the header info,
// and puts the header info into a structure.
//
// return:
// EPR_SUCCESS or an EPR_ERR_* code
//--------------------------------------------------------------------------
int EarthProbe_ReadHeader(stdioFile &fil, EPHEADER &eph)
{
	string strLine;

	// Read first header line.
	if( FALSE == fil.ReadString(strLine) || EP_HDR_LINE_LEN != strLine.GetLength() ) 
		return EPR_ERR_HEADER;
	
	eph.iDayOfYear = atoi(strLine.Mid(6, 3));
	eph.iMonth = monthtoi(strLine.Mid(10, 3));
	eph.iDayOfMonth = atoi(strLine.Mid(14, 2));
	eph.iYear = atoi(strLine.Mid(18, 4));
	lstrcpy(eph.szText, strLine.Mid(26, 24));
	eph.iHour = atoi(strLine.Mid(71, 2));
	eph.iMin = atoi(strLine.Mid(74, 2));
	if( 'A' == strLine[77] )
		eph.iHour += 12;
	
	// Read second header line.
	if( FALSE == fil.ReadString(strLine) || EP_HDR_LINE_LEN != strLine.GetLength() ) 
		return EPR_ERR_HEADER;
	
	eph.iLongitudeBinCount = atoi(strLine.Mid(14, 3));
	eph.dLongitudeStart = atof(strLine.Mid(35, 7));
	eph.dLongitudeStep = atof(strLine.Mid(60, 4));
	
	// Read third header line.
	if( FALSE == fil.ReadString(strLine) || EP_HDR_LINE_LEN != strLine.GetLength() ) 
		return EPR_ERR_HEADER;
		
	eph.iLatitudeBinCount = atoi(strLine.Mid(14, 3));
	eph.dLatitudeStart = atof(strLine.Mid(35, 7));
	eph.dLatitudeStep = atof(strLine.Mid(60, 4));
	
	return EPR_SUCCESS;
}


//--------------------------------------------------------------------------
// EarthProbe_ParseStrData
//
// The data is stored in the files as a string of numbers.
// Each value is always 3 characters long.
// Example:  123456789234 equals four values 123, 456, 789. and 234
// This function will take a string of numbers, parse the values, and puts
// the values into a vector.
//
// return:
// The number of values parsed.
// zero for failure.
//--------------------------------------------------------------------------
int EarthProbe_ParseStrData(vector<int> &v, LPCTSTR lpcstrData)
{
	string strData(lpcstrData);
	int i = strData.GetLength();
	if( (i % EP_DATA_ELEMENT_STR_LEN) )
		return 0; // invalid str length

	int iCount = i / EP_DATA_ELEMENT_STR_LEN;
	v.SetSize(iCount);

	string strDataElement;
	for( i = 0; i < iCount; i++ )
	{
		strDataElement = strData.Mid(i * EP_DATA_ELEMENT_STR_LEN, EP_DATA_ELEMENT_STR_LEN);
		v[i] = atoi(strDataElement);
	}
	return iCount;
}

//--------------------------------------------------------------------------
// EarthProbe_ReadLongitudeBins
//
// This function will read one row of data for the next latitude.
//
// return:
// EPR_SUCCESS or an EPR_ERR_* code
//--------------------------------------------------------------------------
int EarthProbe_ReadLongitudeBins(stdioFile &fil, vector<int> &v)
{
	BOOL bDone = FALSE;
	int iRet, iVecIdx = 0;
	vector<int> vTmp;
	string str;

	v.SetSize(0);
	
	while( !bDone )
	{
		if( FALSE == fil.ReadString(str) )
			return EPR_ERR_READ_DATA;
	
		str.Delete(0); // first char is always a space and not needed
	
		// The last line of data for any given latitude will contain "lat="
		// at the end of the line.  For the line that has this we need to scan
		// back from the "lat" to the first non-space char.
		int i = str.Find("lat");
		if( i != -1 ) // if found "lat"
		{
			bDone = TRUE;
			do
			{
				i--;
			} while( i && str[i] == ' ' );
			
			// currently we do nothing with the "lat" value.
			str = str.Left(i + 1);
		}
		
		if( 0 == EarthProbe_ParseStrData(vTmp, str) )
			return EPR_ERR_PARSE_DATA;
		
		// Append temp vector to target vector.
		v.Append(vTmp);
	}
	return EPR_SUCCESS;
}


//--------------------------------------------------------------------------
// EarthProbe_FileToMatrix
//
// Load an EarthProbe data file into a matrix.
//
// return:
// EPR_SUCCESS or an EPR_ERR_* code
//--------------------------------------------------------------------------
int EarthProbe_FileToMatrix(matrix &matEarthProbe, LPCSTR strFile)
{
	string strFileExt(strFile);
	int iType = EarthProbe_FileExtToType(strFileExt.Right(3));
	if( EP_TYPE_UNKNOWN == iType )
		return EPR_ERR_UNKNOWN_FILE_EXT;
	
	stdioFile fil;
	if( fil.Open(strFile, file::modeRead | file::shareDenyWrite) == FALSE )
		return EPR_ERR_FILE_OPEN;
	
	EPHEADER eph;
	int iRet = EarthProbe_ReadHeader(fil, eph);
	if( EPR_SUCCESS == iRet )
	{
		matEarthProbe.SetSize(eph.iLatitudeBinCount, eph.iLongitudeBinCount);
		
		vector<int> v;
		
		for( int i = 0; i < eph.iLatitudeBinCount; i++ )
		{
			// Read logitude bins into vector.
			v.SetSize(eph.iLongitudeBinCount);
			iRet = EarthProbe_ReadLongitudeBins(fil, v);
			if( EPR_SUCCESS != iRet )
				break;

			// Copy vector to matrix row.
			matEarthProbe.SetRow(v, i);
		}
	}

	// We do not need the file anymore.
	fil.Close();

	// Depending on the file type we need to do some conversions to
	// get the actual data values.
	switch( iType )
	{
	case EP_TYPE_AEROSOL: // *.EPA
		// Aerosol data needs to be divided by 10.
		// "  1" == 0.1
		// " 11" == 1.1
		// "111" == 11.1
		// "999" == missing value
		matEarthProbe /= 10;
		// Replace all 99.9 values with missing value.
		matEarthProbe.Replace(99.9, NANUM, 2);
		break;

	case EP_TYPE_REFLECTIVE: // *.EPR
		// Reflectivity data is integer data.
		// " 83" == 83%
		// "999" == fill value
		// Replace all 999 values with missing value.
		matEarthProbe.Replace(999, NANUM, 2);
		break;

	case EP_TYPE_OZONE: // *.EPT
		// Ozone data is integer data.
		// "234" == 234 du
		// "  0" == fill value
		// Replace all 0 values with missing value.
		matEarthProbe.Replace(0, NANUM, 2);
		break;

	case EP_TYPE_UV: // *.EPE
		// UV Erythemal Irradiance.
		// "342" == 4.2*10^3
		// "999" == fill value
		// Set all cells with a value of 999 to missing value.
		matEarthProbe.Replace(999, NANUM, 2);

		int iPow;
		double d;
		for( int iCol = 0; iCol < matEarthProbe.GetNumCols(); iCol++ )
		{
			for( int iRow = 0; iRow < matEarthProbe.GetNumRows(); iRow++ )
			{
				if( matEarthProbe[iCol][iRow] != NANUM )
				{
					iPow = matEarthProbe[iCol][iRow] / 100;
					d = (matEarthProbe[iCol][iRow] - (iPow * 100)) / 10;
					matEarthProbe[iCol][iRow] = d * 10 ^ iPow;
				}
			}
		}
		break;
	}
	return iRet;
}

//--------------------------------------------------------------------------
// EarthProbe_FileToActiveMatrix
//
// Load an EarthProbe data file into the active matrix.
//
// return:
// EPR_SUCCESS or an EPR_ERR_* code
//--------------------------------------------------------------------------
int EarthProbe_FileToActiveMatrix(LPCSTR lpcstrFile)
{
	if( NULL == lpcstrFile || '\0' == *lpcstrFile )
		return EPR_ERR_NO_FILE_SPECIFIED;

	MatrixLayer matlayer = Project.ActiveLayer();
	if( NULL == matlayer )
		return EPR_ERR_TARGET_MATRIX_NOT_VALID;

	Matrix mEP(matlayer);
	return EarthProbe_FileToMatrix(mEP, lpcstrFile)
}
	
//--------------------------------------------------------------------------
// LoadEarthProbe
//
// Load the specified EarthProbe data file into the specified matrix.
//
// Return:
//	zero for success
//	non-zero for error
//--------------------------------------------------------------------------
int LoadEarthProbe(string strWindow, int iLayer, string strFileName)
{
	Matrix mat(strWindow);
	if( !mat.IsValid() )
		return 1;
	return EarthProbe_FileToMatrix(mat, strFileName)	
}

//--------------------------------------------------------------------------
// DragDropEarthProbe
//
// Handle Drag-N-Drop for EarthProbe data files.
//
// Return:
//	zero for success
//	non-zero for error
//--------------------------------------------------------------------------
int DragDropEarthProbe()
{
	string strDDFile, strDDWindow;
	int iDDLayer, iDDIndex, iDDCount;
	Project.GetDragDropInfo(strDDWindow, iDDLayer, iDDIndex, iDDCount, strDDFile);

	BOOL bMakeNewPage = TRUE;
	PageBase pgb(strDDWindow);
	if( pgb.IsValid() )
	{
		// If dropped on a matrix window and is first file
		if( EXIST_MATRIX == pgb.GetType() && 0 == iDDIndex )
			bMakeNewPage = FALSE; // do not make a new matrix window
	}

	if( bMakeNewPage )
	{
		MatrixPage mpg;
		mpg.Create("origin.otm");
		strDDWindow = mpg.GetName();
		iDDLayer = 0;
	}

	return LoadEarthProbe(strDDWindow, iDDLayer, strDDFile);
}

//--------------------------------------------------------------------------
// EPOpen
//
// This is the top level function for importing EarthProbe files.
// It is the only function that should be called from LabTalk.
// It will prompt the user for a file.
//
// return:
// zero for success, non-zero for error
//--------------------------------------------------------------------------
int EPOpen()
{
	// Prompt user for EarthProbe file name.
	StringArray strType;
	strType.SetSize(4);
	strType[0] = "[EarthProbe Aerosol (*.EPA)] *.EPA";
	strType[1] = "[EarthProbe Ozone (*.EPT)] *.EPT";
	strType[2] = "[EarthProbe Reflectivity (*.EPR)] *.EPR";
	strType[3] = "[EarthProbe UV (*.EPE)] *.EPE";
	string strFile = GetOpenBox(strType, GetAppPath(), "", "Open Earth Probe File");
	if( strFile.IsEmpty() )
		return EPR_ERR_NO_FILE_SPECIFIED; // User canceled.

	// Get target matrix by name.
	MatrixLayer matlayer;
	Layer layer = Project.ActiveLayer();
	if( EXIST_MATRIX != layer.GetPage().GetType() )
		matlayer.Create();
	else
		matlayer = (MatrixLayer)layer;
	Matrix mEP(matlayer);

	// Import specified EPA file into specified matrix.
	int i = EarthProbe_FileToMatrix(mEP, strFile);
	if( i )
		printf("EarthProbe Import Error %d\n", i);
	return i;
}
