/*------------------------------------------------------------------------------*
 * File Name:				 													*
 * Creation: 																	*
 * Purpose: OriginC Source C file												*
 * Copyright (c) ABCD Corp.	2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007		*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *------------------------------------------------------------------------------*/
 
////////////////////////////////////////////////////////////////////////////////////
#include <origin.h>
#include "ocWAV.h"
#include "local.h"
////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////
// Read a WAV file
int	ocWAVReadFile(string strFileName, string strWorksheetName)
{
	file	fid;
	int		iStatus = 0;
	int		iFileSize = 0;
	int		iBytesRead = 0;
	int		iResult = 0;
	int		iFmtRead = 0;
	int		iPos = 0;
	
	// fmt info
	FORMATCHUNK		fmt;	// struct defined in header
	
	unsigned int	iDataSize;
	unsigned int	iSamples;

	string	strTemp;
	int		iChunkSize = 0;
	char	cChunkName[9];

	if( fid.Open(strFileName, file::shareDenyNone) )	// fid.Open(strFileName, file::modeRead)
	{
		// What size is the file?
		iFileSize = fid.SeekToEnd();
		// Rewind
		fid.SeekToBegin();
		// First chunk name is RIFF.
		iBytesRead = fid.Read(&cChunkName,4);
		cChunkName[4] = 0;
		strTemp = cChunkName;
		if(strTemp.Compare("RIFF"))
		{
			iStatus = 2;
			printf( OCWAV_NOTWAV ); // "Not a WAV file.\n"
			fid.Close(); // Not needed, but clean
			return iStatus;
		}
		// File size should be consistent with description.
		iBytesRead = fid.Read(&iChunkSize,4);
		if(iChunkSize + 8 > iFileSize)
		{
			iStatus = 3;
			printf( OCWAV_NOTGOOD ); // "File size and description inconsistent.\n"
			fid.Close(); // Not needed, but clean
			return iStatus;
		}
		// Should be a WAVE.
		iBytesRead = fid.Read(&cChunkName,4);
		cChunkName[4] = 0;
		strTemp = cChunkName;
		if(strTemp.Compare("WAVE"))
		{
			iStatus = 4;
			printf( OCWAV_NOTWAV) ;
			fid.Close(); // Not needed, but clean
			return iStatus;
		}
		// Should be a good WAV file. Still need to verify that it's PCM encoded
		// Many chunk types. Only process "fmt " and "data" (in that order).
		iPos = fid.GetPosition();
		while (iPos < iFileSize)
		{
			iBytesRead = fid.Read(&cChunkName,4);
			cChunkName[4] = 0;
			strTemp = cChunkName;
			iBytesRead = fid.Read(&iChunkSize,4);
			if(!strTemp.Compare("fmt "))
			{
				if(iFmtRead == 0)
				{
					iFmtRead = 1;
//					printf("Reading fmt ...\n");
					iBytesRead = fid.Read(&fmt, sizeof(fmt));
					if(fmt.iWAVFormat != 1)
					{
						iStatus = 7;
						printf( OCWAV_PCMONLY ); // "Only PCM formatting supported.\n"
						fid.Close(); // Not needed, but clean
						return iStatus;
					}
					Page pp(strWorksheetName);
					pp.Info.Add("WAVE");
					pp.Info.WAVE.AddSection("fmt");
					pp.Info.WAVE.fmt.AddInt("Channels",fmt.iWAVChannels);
					pp.Info.WAVE.fmt.AddInt("Frequency", fmt.iWAVFrequency);
					pp.Info.WAVE.fmt.AddInt("BitsPerSample", fmt.iWAVBitsPerSample);
				}
				else
				{
					iStatus = 5;
					printf( OCWAV_ONEFMT ); // "Duplicate fmt chunk not allowed.\n"
					fid.Close(); // Not needed, but clean
					return iStatus;
				}
//				printf("%s : CHAN %u, FREQ %u, BiPS %u\n",strFileName, fmt.iWAVChannels, fmt.iWAVFrequency, fmt.iWAVBitsPerSample);
			}
			if(!strTemp.Compare("data"))
			{
				if(iFmtRead == 1)
				{
//					printf("Reading data ...\n");
					// Setup worksheet with correct columns
					Worksheet	wks(strWorksheetName);
					// Remove existing columns
					int	iNumCols = wks.GetNumCols();
					while(iNumCols > 0)
					{
						wks.DeleteCol(0);
						iNumCols = wks.GetNumCols();
					}
					wks.AddCol();
					wks.Columns(0).SetLabel("Time");
					if(fmt.iWAVChannels == 1)
					{
						wks.AddCol();
						wks.Columns(1).SetLabel("Data");
					}
					else
					{
						wks.AddCol();
						wks.Columns(1).SetLabel("Left");
					}
					if(fmt.iWAVChannels == 2)
					{
						wks.AddCol();
						wks.Columns(2).SetLabel("Right");
					}
					wks.ShowLabels();
					// Attach to columns, calculate sample and data sizes
					Dataset			dsTime(strWorksheetName, 0);
					wks.Columns(0).SetType(OKDATAOBJ_DESIGNATION_X);
					Dataset 		dsLeft(strWorksheetName, 1);
					iDataSize = fmt.iWAVBitsPerSample / 8;
					iSamples = iChunkSize / (fmt.iWAVChannels * iDataSize);
					double dSeconds = (double) iSamples / (double) fmt.iWAVFrequency;
					Page pp(strWorksheetName);
					pp.Info.WAVE.fmt.AddInt("Samples", iSamples);
					pp.Info.WAVE.fmt.AddDouble("Seconds", dSeconds);

					// Fill Time column - Using OriginC equivalent of data(min,max,inc)
					dsTime.Data((double) 1 / fmt.iWAVFrequency, (double) iSamples / fmt.iWAVFrequency, (double) 1 / fmt.iWAVFrequency);

 					// Read data
					switch(fmt.iWAVBitsPerSample)
					{
					case 8:
						vector<unsigned char>	tempdata;
						tempdata.SetSize(iChunkSize);
						fid.Read(tempdata, iChunkSize);
						dsLeft = tempdata;
						break;
					case 16:
						vector<short>	tempdata;
						tempdata.SetSize(iChunkSize / 2);
						fid.Read(tempdata, iChunkSize);
						dsLeft = tempdata;
						break;
					default:
						iStatus = 8;
						printf( OCWAV_BITSIZE ); // "Unsupported bit size.\n"
						fid.Close(); // Not needed, but clean
						return iStatus;
					}
					// Split stereo data into two channels - LabTalk
					if(fmt.iWAVChannels == 2)
						LT_execute("wo -a 1;get col(2) -e last;copy -u col(2) col(4) col(3);copy col(4) col(2);del col(4);set %H -er last/2;");
				}
				else
				{
					iStatus = 6;
					printf( OCWAV_FMTORDER ); // "fmt chunk must precede data chunk.\n"
					fid.Close(); // Not needed, but clean
					return iStatus;
				}
			}
			if(!strTemp.Compare("LIST"))
			{
				iBytesRead = fid.Read(&cChunkName,4);
				cChunkName[4] = 0;
				strTemp = cChunkName;
				// I only handle LISTINFO chunks
				if(!strTemp.Compare("INFO"))
				{
					string	strTemp2;
					Page pp(strWorksheetName);
					pp.Info.WAVE.AddSection("LIST");
					pp.Info.WAVE.LIST.AddSection("INFO");
					int	iChunkBytesRead = 4;
				}
			}
			// Position to next Chunk
			fid.Seek(iPos + 8 + iChunkSize, file::begin);
			iPos = fid.GetPosition();
			// Align on 16 bit boundary
			if(iPos&1) fid.Seek(1, file::current);
			// Reset cChunkName
			cChunkName[0] = 0;
		}
		fid.Close();
	}
	else
	{
		iStatus = 1;
		printf( OCWAV_OPENFAIL , strFileName); // "Failed to open file %s.\n"
		fid.Close(); // Not needed, but clean
	}
	LT_set_str("%N",strFileName);
	LT_execute("page.label $= %N; page.title = 3;");

	return iStatus;
}


// Write a WAV file
// This function expects appropriate info storage for a WAV file
// The ocWAVAnalyzeData function can setup this storage
int ocWAVWriteFile(string strFilename, string strWorksheetName, int iColL, int iColR, double dFreqMult = 1, double dAmpMult = 1)
{
	file		fid;
	int			iStatus = 0;
	FORMATCHUNK	sFormat;
	int			iCol = 0;
	int			iChannels = 1;
	int			iFrequency = 0;
	int			iBits = 0;
	int			iSamples = 0;
	int			iBytes = 0;
	int			iTemp = 0;

	// Stereo or Mono?
	iColL -= 1;	// OriginC columns are indexed from zero, function written as one-based
	iColR -= 1;	// OriginC columns are indexed from zero, function written as one-based
	if(iColL != iColR) iChannels = 2;

	// Frequency and data format
	string	strTemp;
	double	dValue;
	strTemp = "value = " + strWorksheetName + "!page.info.wave.fmt.frequency";
	LT_execute(strTemp);
	LT_get_var("value",&dValue);
	iFrequency = (int) dValue;
	strTemp = "value = " + strWorksheetName + "!page.info.wave.fmt.bitspersample";
	LT_execute(strTemp);
	LT_get_var("value",&dValue);
	iBits = (int) dValue;
	
	// If Frequency and Bits are missing then derive them
	if(iFrequency == 0 || iBits == 0)
	{
		Dataset ds(strWorksheetName,1);
		string	strDatasetName;
		int iResult;
		
		ds.GetName(strDatasetName);
		iResult = ocWAVAnalyzeData(strDatasetName);
		if(iResult)
		{
			iFrequency = 8000;
			iBits = 16;
		}
	}
	
	// Multiply by Frequency factor
	iFrequency = (int)(iFrequency * dFreqMult);

	if( fid.Open(strFilename, file::modeCreate + file::modeReadWrite ) )
	{
		sFormat.iWAVFormat = 1;
		sFormat.iWAVChannels = iChannels;
		sFormat.iWAVFrequency = iFrequency;
		sFormat.iWAVAlign = iChannels * iBits / 8;
		sFormat.iWAVBytesPerSecond = iFrequency * sFormat.iWAVAlign;
		sFormat.iWAVBitsPerSample = iBits;

		// Get size of data - based on Left Channel
		Worksheet	wks(strWorksheetName);
		iBytes = iBits / 8;
		Dataset	ds1(wks, iColL);
		iSamples = ds1.GetUpperBound();

		// Need one or the other - just create both
		vector<short>			vChan16;
		vector<unsigned char>	vChan8;

		// Combine Left and Right Channels (interleaved)
		iCol = iColL;
		if(iColL != iColR)
		{
			LT_set_var("col1", iColL + 1);
			LT_set_var("col2", iColR + 1);
			LT_execute("wo -a 1;copy -z wcol(col1) wcol(col2) wcol(wks.ncols);");
			iSamples *= 2;
			iCol = wks.GetNumCols() - 1;
		}

		// Copy datasets to vectors
		Dataset	ds(wks, iCol);
		if(iBytes == 1)
		{
			vChan8 = ds;
			vChan8 = (vChan8 - 128) * dAmpMult + 128;
		}
		else
		{
			vChan16 = ds;
			vChan16 = vChan16 * dAmpMult;
			iSamples *= 2;
		}

		// Now we can write the file
		fid.Write("RIFF", 4);
		// Calculate final file size - 8 and write to file (see comments below)
		iTemp = 84 + iSamples;
		fid.Write(&iTemp, sizeof(iTemp));
		// Always a WAVE file					- ADD 4 BYTES
		fid.Write("WAVE", 4);
		// fmt chunk							- ADD 4 BYTES
		fid.Write("fmt ", 4);
		// fmt chunk size						- ADD 4 BYTES
		iTemp = 16;
		fid.Write(&iTemp, sizeof(iTemp));
		// fmt chunk data						- ADD 16 BYTES
		fid.Write(&sFormat, sizeof(sFormat));
		// data chunk							- ADD 4 BYTES
		fid.Write("data", 4);
		// data chunk size						- ADD 4 BYTES
		fid.Write(&iSamples,sizeof(iSamples));
		// data chunk data						- ADD iSamples BYTES
		if(iBytes == 1)
			fid.Write(vChan8, iSamples);
		else
			fid.Write(vChan16, iSamples);
		// Our stamp chunk 						- ADD 4 BYTES
		fid.Write("LIST",4);
		// Chunk size							- ADD 4 BYTES
		iTemp = 24;
		fid.Write(&iTemp, sizeof(iTemp));
		// Chunk data							- ADD 40 BYTES
		fid.Write("INFOISFT",8);
		iTemp = 16;
		fid.Write(&iTemp, sizeof(iTemp));
		fid.Write("Modified by Origin software.",28);

		if(iColL != iColR)
	 		LT_execute("del wcol(wks.ncols);");
 		fid.Flush();
		fid.Close();
	}
	else
	{
		iStatus = 1;
		printf( OCWAV_OPENFAIL ); // "Failed to open file %s.\n"
	}
//	printf("Done.\n");
	return iStatus;
}


// Play a WAV file
// The default method is to use mci command strings
// If this method fails, an alternate is provided to use the Windows SNDREC32.EXE program
void	ocWAVPlayFile(string strFileName, int iMethod = 0)
{
	int iReturn = 0;

	if(iMethod)
	{
		LT_set_str("%Z",strFileName);
		LT_execute("run -e sndrec32.exe /play /close %Z;");
	}
	else
	{		
		string	strTemp = "open ";
		strTemp += strFileName;
		iReturn = mciSendString(strTemp, "", 0, 0);
	
		strTemp = "play ";
		strTemp += strFileName;
		strTemp += " wait";
		iReturn = mciSendString(strTemp, "", 0, 0);
	
		strTemp = "close all ";
		iReturn = mciSendString(strTemp, "", 0, 0);
	}
}


// Listen to a dataset
// If the Info storage for WAVE exists, then use it
void	ocWAVPlayData(string strDatasetName, double dFreqMult = 1, double AmpMult = 1)
{
	int		iResult;

	iResult = ocWAVAnalyzeData(strDatasetName);
	printf("Returned %u from analysis.\n",iResult);
	
}

int		ocWAVAnalyzeData(string strDasetName1, string strDatasetName2)
{
	printf("Executing the overloaded two strings version.\n");
	return 0;
}


// Analyze a Dataset for export to WAV file
// Create the appropriate info storage
int		ocWAVAnalyzeData(string strDatasetName)
{
	printf("This function not available yet.\n");
	return 1;
}


// The filter function version
int	ImportWAV(Page& pgTarget, TreeNode& trFilter, LPCSTR lpcszFile, int nFile)
{
	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;");
	return ocWAVReadFile(lpcszFile, strName);
}
