/* *** lots of this is by Eric Auer 3/2001, but its inspired *** */
/* *** by LoadingATextFile.c from the eyetracker toolkit     *** */


#define TRIALS_MAX 200
#define gDefKeyword TEXT("define")
#define gInlineKeyword TEXT("inline")
#define gGfxKeyword TEXT("image")
// *** ^- this is new 11.6.2001
#define gDefKeywordLen 6
#define ScriptWidth 1024

#include <windows.h>
#include <windowsx.h>

#include "eyelink.h"
#include "w32_exptsppt.h"
#include "w32_demo.h"	/* header file for this experiment */

#include <stdio.h>
#include <ctype.h>		// *** for isgraph
#include <string.h>		// *** for strlen
#include "AddUnicode.h"

#include "reading_loadscript.h" // ***

LPTCH glpScriptText = NULL;
TRIALPARAMS TheTrials[TRIALS_MAX];
long glTrialCount;
long glTrialLen;


long CreateTrials(TCHAR* src, long len)
{
  TCHAR pszBuffer[ScriptWidth+1];
  TCHAR* src_end;
  TRIALPARAMS trialdefines[42];	// special use for pszTrialIdent!
				// but some values are unused (time, filename)
  long bComment;	// *** ok, b implies bool, but I like int better
  long bDefine;	// *** see above
  long lDefCount;	// *** number of defines used up so far
  long lWord;	// *** counting the words in the row
  long i,j,k;	// *** used as define index and copying index and similar
// ***	long count = 0;
  glTrialCount = 0;
  lDefCount = 0;
  bComment = 0;
  lWord = 0;
  bDefine = 0;
  src_end = src + len; // *** hope this works

  while (src < src_end) // *** was count < len
  {
    sscanf(src, TEXT(" %1024s "), pszBuffer);
    // sscanf returns the no of % escapes that could be filled
    // *** whitespace in format string -> skip whitespace
    if ((pszBuffer[0] == ';') || (pszBuffer[0] == '#'))
    { // if bComment goes 0->1 we could do something
      bComment = 1;
    }
    if (bComment == 0) lWord++; // found non-comment word

	if (bComment == 0)
	{ // this is the fun part!
	
	if (lWord == 1)
	{
	  // if lWord is 1 - is the word "define" ?
	  // then inc lDefCount, check for < 42,
	  // set bDefine. ELSE look for the matching
	  // define, inc glTrialCount, check for < 100
	  if (lstrcmp(pszBuffer,gDefKeyword) == 0)
	  {
		bDefine = 1;	// go to "define mode"
		if (lDefCount > 41) 
		{
			MessageBox(full_screen_window,
				TEXT("42 or more defines in script: too many."),
				TEXT("Error"), MB_OK | MB_ICONERROR);
			return glTrialCount; // overflow!!!
		}
	  }
	  else
	  {
		bDefine = 0;	// go to "trial mode", init trial.
		if (glTrialCount > (TRIALS_MAX-1)) /* e.g. 99 */
		{
			MessageBox(full_screen_window,
				TEXT("More than TRIALS_MAX trials in script."),
				TEXT("Error"), MB_OK | MB_ICONERROR);
			return TRIALS_MAX-1; // overflow!!!
		}
		TheTrials[glTrialCount].pszTrialIdent[0] = '\0';
		TheTrials[glTrialCount].lTimeLimit = -1;
		TheTrials[glTrialCount].pszTextFilename[0] = '\0';
		TheTrials[glTrialCount].lUsedButtons = -1;
		TheTrials[glTrialCount].pszText[0] = '\0';	// *** NEW May 21 2001
		TheTrials[glTrialCount].bTextInline = 0;	// *** inline text ...
		// *** the following members are new 11.6.2001
		TheTrials[glTrialCount].bDriftCorrect = 0;
		TheTrials[glTrialCount].bHasImage = 0;
		TheTrials[glTrialCount].pszTplFilename[0] = '\0';
		TheTrials[glTrialCount].hTplBmp = NULL;
		TheTrials[glTrialCount].bHasSound = 0;
		TheTrials[glTrialCount].pszWavFilename[0] = '\0';

		for (i=0; i<lDefCount; i++)
		{
			if (lstrcmp(pszBuffer,trialdefines[i].pszTrialIdent) == 0)
			{
				// use the matching define
				TheTrials[glTrialCount].bGazeActivate =
				trialdefines[i].bGazeActivate;
				TheTrials[glTrialCount].bLogStream =
				trialdefines[i].bLogStream;
				TheTrials[glTrialCount].lUsedButtons =
				trialdefines[i].lUsedButtons;
				TheTrials[glTrialCount].bDriftCorrect =
				trialdefines[i].bDriftCorrect;
				for (j=0; j<TheTrials[glTrialCount].lUsedButtons; j++)
				{ TheTrials[glTrialCount].iButtonList[j] =
					   trialdefines[i].iButtonList[j];
				}
			}
		}
		if (TheTrials[glTrialCount].lUsedButtons < 0)
		{
			MessageBox(full_screen_window,
				TEXT("Class used before being defined in script."),
				TEXT("Error"), MB_OK | MB_ICONERROR);
			return glTrialCount; // failed to find matching define
		}
	  }
	} // EOF word 1

	if (lWord == 2) // second is always the ident (both for trials and defines)
	{
		if (bDefine == 0)
		{
			strncpy(TheTrials[glTrialCount].pszTrialIdent,
				   pszBuffer,255);
		}
		else
		{
			strncpy(trialdefines[lDefCount].pszTrialIdent,
				   pszBuffer,255);
		}
	} // EOF word 2

	if (lWord == 3)
	{
		if (bDefine == 0)	// if it is a trial...: time limit
		{
		// ********************************
		// *** sscanf crashes an TCHAR so
		// *** doing parsing by hand now!!!
		// ********************************
			// *** sscanf(pszBuffer,TEXT(" %ld "),
			// ***   TheTrials[glTrialCount].lTimeLimit);
			k = 0; // our result
			j = 0; // where we are
			// warning: does not handle \t yet
			while (pszBuffer[j]==' ' ||
				((pszBuffer[j]>='0') &&
				(pszBuffer[j]<='9')))
			{
				if (pszBuffer[j]==' ')
				{
					/* ignore spaces */
				}
				else
				{
					k = k * 10; // were we were gets more as we pass by
					k = k + (pszBuffer[j] - '0'); // hope TCHARS love this
				}
				j++; // dont forget this if you dont love endless loops
			}
			if (k < 100) 
			{
				MessageBox(full_screen_window,
					TEXT("Timeout less than 100ms or parse error in script."),
					TEXT("Error"), MB_OK | MB_ICONERROR);
				return glTrialCount; // bail out!
			}
			TheTrials[glTrialCount].lTimeLimit = k;
		}
		else			// if it is a define...: gaze activation
						// or Nogaze or DriftCorrect
						// that is, DC only if explicitly requested
						// and no gaze activation is used
						// *** this is new 11.6.2001
		{
			if ((pszBuffer[0] == 'n') || (pszBuffer[0] == 'N'))
			{
				trialdefines[lDefCount].bGazeActivate = FALSE;
				trialdefines[lDefCount].bDriftCorrect = FALSE;
			}
			else
			{
				if ((pszBuffer[0] == 'd') || (pszBuffer[0] == 'D'))
				{
					trialdefines[lDefCount].bGazeActivate = FALSE;
					trialdefines[lDefCount].bDriftCorrect = TRUE;
				}
				else
				{
					trialdefines[lDefCount].bGazeActivate = TRUE;
					// gaze activation has no separate DC by definition...
				}
			}
		}
	} // EOF word 3

	if (lWord == 4)
	{
		if (bDefine == 0)	// if it is a trial...: filename *** or "inline" NEW May 21 2001
		{
			if (strcmp(pszBuffer,gInlineKeyword)) // "inline"
			{
				// *** new 11.6.2001: image instead of text
				if (strcmp(pszBuffer,gGfxKeyword)) // "image"
				{
					strncpy(TheTrials[glTrialCount].pszTextFilename,
					   pszBuffer,254); // better use sizeof!
				}
				else
				{
					// TextFilename now used for image file name,
					// if none given, directly used as template
					TheTrials[glTrialCount].bHasImage = TRUE;
					// do not store the word "image" :-)
					TheTrials[glTrialCount].pszTextFilename[0] = '\0';
					TheTrials[glTrialCount].pszTplFilename[0] = '\0';
				}
			}
			else
			{
				TheTrials[glTrialCount].bTextInline = 1;	// true
				// do not store the word "inline" :-)
				TheTrials[glTrialCount].pszText[0] = '\0';
			}
		}
		else			// if it is a define...: stream
		{
			if ((pszBuffer[0] == 'n') || (pszBuffer[0] == 'N'))
			{
				trialdefines[lDefCount].bLogStream = FALSE;
			}
			else
			{
				trialdefines[lDefCount].bLogStream = TRUE;
			}
			trialdefines[lDefCount].lUsedButtons = 0; // init this!
			trialdefines[lDefCount].iButtonList[0] = '\0'; // int!
		}
	} // EOF word 4

			// "define" class gaze stream (next: button(s))
			// class ident timeout [filename|"inline"] (next: <unused>|<inlined text>)

	if ((lWord >= 5) && (bDefine == 0) &&
		(TheTrials[glTrialCount].bTextInline != 0)) // inlined text
	{
		strncat(TheTrials[glTrialCount].pszText, pszBuffer,
			1999 - strlen(TheTrials[glTrialCount].pszText)); // better use sizeof!
		if (strlen(TheTrials[glTrialCount].pszText)< 1998)
			strcat(TheTrials[glTrialCount].pszText," "); // else warn... buffer too full!
	} // EOF inlined text

	if ((lWord >= 5) && (bDefine == 0) &&
		(TheTrials[glTrialCount].bTextInline == 0))
	{ // *** 11.6.2001: additional parameters, which are:
		// *** bHasImage is false: an optional sound file name
		// *** bHasImage is true: first, an optional template file name,
		// ***                    then, an optional sound file name!
		if (TheTrials[glTrialCount].bHasImage == 0)
		{
			// no image -> if it is word 5, it is the sound file name
			if (lWord == 5)
			{
				strncpy(TheTrials[glTrialCount].pszWavFilename,
				   pszBuffer,254); // better use sizeof!
				TheTrials[glTrialCount].bHasSound = TRUE;
			}
		} // EOF text
		else
		{
			// image -> if it is word 5, it is the image file name
			// image -> if it is word 5, it is the template file name
			// image -> if it is word 6, it is the sound file name.
			if (lWord == 5)
			{
				strncpy(TheTrials[glTrialCount].pszTextFilename,
				   pszBuffer,254); // better use sizeof!
			}
			if (lWord == 6)
			{
				strncpy(TheTrials[glTrialCount].pszTplFilename,
				   pszBuffer,254); // better use sizeof!
			}
			if (lWord == 7)
			{
				strncpy(TheTrials[glTrialCount].pszWavFilename,
				   pszBuffer,254); // better use sizeof!
				TheTrials[glTrialCount].bHasSound = TRUE;
			}
		} // EOF image
	} // EOF additional parameters

	if ((lWord >= 5) && (bDefine == 1))	// button list ignore rest of the line otherwise
	{
		// pszBuffer may be a char -> concat to list, count it
		// or a string -> expresses a special char -> as above
		// if unknown special char, return from func, maybe warn!!!
		i = 0;	// our new button
		if (strlen(pszBuffer) > 1)	// need to resolve?
		{
			// *** this could be done with much more style!
			// *** w32_exptsppt.h has 4 arrows, pgup/pgdn, esc, cr
			if (lstrcmp(pszBuffer,TEXT("left")) == 0)
			{
				i = CURS_LEFT;
			}
			if (lstrcmp(pszBuffer,TEXT("right")) == 0)
			{
				i = CURS_RIGHT;
			}
			if (lstrcmp(pszBuffer,TEXT("up")) == 0)
			{
				i = CURS_UP;
			}
			if (lstrcmp(pszBuffer,TEXT("down")) == 0)
			{
				i = CURS_DOWN;
			}
			if (lstrcmp(pszBuffer,TEXT("space")) == 0)
			{
				i = ' '; // char to int! (see toolkit->getkey)
			}
		}
		if ((strlen(pszBuffer) == 1) || (i != 0))
		{
			if (strlen(pszBuffer) == 1) i = pszBuffer[0]; // char to int!
			if (trialdefines[lDefCount].lUsedButtons > 250)
			{
				MessageBox(full_screen_window,
					TEXT("Too many buttons defined in script."),
					TEXT("Error"), MB_OK | MB_ICONERROR);
				return glTrialCount;
			}
			trialdefines[lDefCount].iButtonList[
			trialdefines[lDefCount].lUsedButtons] = i;
			trialdefines[lDefCount].lUsedButtons++;
		}
		else	// buffer was empty or resolve failed
		{
			// ignore failure for now!!!
		}
	} // EOF button list

	} // end of fun part.

	while (!isgraph(*src)) { src++;	} // skip leading whitespace
					// dont confuse isprint/isgraph
	src = src + strlen(pszBuffer); 	// trailing whitespace is no
					// problem here
	if ((*src == '\r') || (*src == '\n'))
	{ 
		if (lWord) // -> this line was useful
		{
			if (bDefine)
			{
				lDefCount++;
			}
			else
			{
				glTrialCount++;
			}
		}
		lWord=0; bComment=0; bDefine=0;
		while ((*src == '\r') || (*src == '\n')) { src++; }
	}
 // ????   } // end of "no comment"
  }
  // count from 1..n (so 0 means failure)
  glTrialCount++;
  return glTrialCount;
}

BOOL LoadScript(TCHAR* pszFilename, long* plTrials)
{
	HANDLE hFile;
	BOOL bSucceeded;
	DWORD dwBytesRead;
	LPTCH lpTemp = NULL;
	UINT32 glScriptLen;	// forgot that!?

	hFile = CreateFile(pszFilename,
			GENERIC_READ,
			(DWORD) 0,
			NULL,
			OPEN_EXISTING, 
			FILE_ATTRIBUTE_NORMAL,
			(HANDLE) 0);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	// else
	glScriptLen = GetFileSize(hFile, NULL);
	lpTemp = (LPTCH) GlobalAlloc(0, glScriptLen+1);
		// *** 24.7.2001 see a few lines below !!!
	bSucceeded =  ReadFile(
		hFile,                // handle of file to read
		lpTemp,             // pointer to buffer that receives data
		glScriptLen,  // number of bytes to read
		&dwBytesRead, // pointer to number of bytes read
		NULL   // pointer to structure for data
	);
	lpTemp[glScriptLen] = 0; // *** 24.7.2001 forgot to close string, yuck !!!
	CloseHandle(hFile);
	if (!bSucceeded)
	{
		GlobalFree((HGLOBAL) lpTemp);
		return FALSE;
	}
	// else
// ***	glpScriptText = (LPTCH) GlobalAlloc(0, glScriptLen);
	plTrials[0] = CreateTrials(lpTemp, /* glpScriptText, */
		glScriptLen);
	GlobalFree((HGLOBAL) lpTemp);
	
	return TRUE;
}

void ReleaseScript()
{
// ***	GlobalFree((HGLOBAL) glpScriptText);
}

PTRIALPARAMS GetTrial(long trialno)
{
	if ( (trialno <= glTrialCount) && (trialno > 0) )
	{
		return &TheTrials[trialno-1];
	}
	else
	{
		MessageBox(full_screen_window,
			TEXT("GetTrial was asked for non existant trial."),
			TEXT("Error"), MB_OK | MB_ICONERROR);
		return NULL;
	}
}
