/*
 *  CHOICE.C - internal command.
 *
 *
 *  History:
 *
 *    12 Aug 1999 (Eric Kohl)
 *        started.
 *
 *    01 Sep 1999 (Eric Kohl)
 *        Fixed help text.
 *
 *    26 Sep 1999 (Paolo Pantaleo)
 *        Fixed timeout.
 */

#include "config.h"

#ifdef INCLUDE_CMD_CHOICE

#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <tchar.h>

#include "cmd.h"
#include "batch.h"


#define GC_TIMEOUT	-1
#define GC_NOKEY	0	//an event occurred but it wasn't a key pressed
#define GC_KEYREAD	1	//a key has been read


static INT
GetCharacterTimeout (LPTCH ch, DWORD dwMilliseconds)
{
//--------------------------------------------
//  Get a character from standard input but with a timeout.
//  The function will wait a limited amount
//  of time, then the function returns GC_TIMEOUT.
//
//	dwMilliseconds is the timeout value, that can
//	be set to INFINITE, so the function works like
//	stdio.h's getchar()

	HANDLE hInput;
	DWORD  dwRead;

	INPUT_RECORD lpBuffer;

	hInput = GetStdHandle (STD_INPUT_HANDLE);

	//if the timeout experied return GC_TIMEOUT
	if (WaitForSingleObject (hInput, dwMilliseconds) == WAIT_TIMEOUT)
		return GC_TIMEOUT;

	//otherwise get the event
	ReadConsoleInput (hInput, &lpBuffer, 1, &dwRead);

	//if the event is a key pressed
	if ((lpBuffer.EventType == KEY_EVENT) &&
		(lpBuffer.Event.KeyEvent.bKeyDown == TRUE))
	{
		//read the key
#ifdef _UNICODE
		*ch = lpBuffer.Event.KeyEvent.uChar.UnicodeChar;
#else
		*ch = lpBuffer.Event.KeyEvent.uChar.AsciiChar;
#endif
		return GC_KEYREAD;
	}

	//else return no key
	return GC_NOKEY;
}

static INT
IsKeyInString (LPTSTR lpString, TCHAR cKey, BOOL bCaseSensitive)
{
	LPTCH p = lpString;
	INT val = 0;

	while (*p)
	{
		if (bCaseSensitive)
		{
			if (*p == cKey)
				return val;
		}
		else
		{
			if (_totlower (*p) == _totlower (cKey))
				return val;
		}

		val++;
		p++;
	}

	return -1;
}


INT
CommandChoice (LPTSTR cmd, LPTSTR param)
{
	LPTSTR lpOptions = "YN";
	LPTSTR lpText    = NULL;
	BOOL   bNoPrompt = FALSE;
	BOOL   bCaseSensitive = FALSE;
	BOOL   bTimeout = FALSE;
	INT    nTimeout = 0;
	TCHAR  cDefault = _T('\0');
	INPUT_RECORD ir;
	LPTSTR p, np;
	LPTSTR *arg;
	INT    argc;
	INT    i;
	INT    val;

	INT GCret;
	TCHAR Ch;
	DWORD amount,clk;

	if (_tcsncmp (param, _T("/?"), 2) == 0)
	{
		ConOutPuts (_T("Waits for the user to choose one of a set of choices.\n"
		               "\n"
		               "CHOICE  [/C[:]choices][/N][/S][/T[:]c,nn][text]\n"
		               "\n"
		               "  /C[:]choices  Specifies allowable keys. Default is YN.\n"
		               "  /N            Do not display choices and ? at the end of the prompt string.\n"
		               "  /S            Treat choice keys as case sensitive.\n"
		               "  /T[:]c,nn     Default choice to c after nn seconds.\n"
		               "  text          Prompt string to display.\n"
		               "\n"
		               "ERRORLEVEL is set to offset of key user presses in choices."));
		return 0;
	}

	/* retrieve text */
	p = param;

	while (TRUE)
	{
		if (*p == _T('\0'))
			break;

		if (*p != _T('/'))
		{
			lpText = p;
				break;
		}
		np = _tcschr (p, _T(' '));
		if (!np)
			break;
		p = np + 1;
	}

	/* build parameter array */
	arg = split (param, &argc);

	/* evaluate arguments */
	if (argc > 0)
	{
		for (i = 0; i < argc; i++)
		{
			if (_tcsnicmp (arg[i], _T("/c"), 2) == 0)
			{
				if (arg[i][2] == _T(':'))
					lpOptions = &arg[i][3];
				else
					lpOptions = &arg[i][2];

				if (_tcslen (lpOptions) == 0)
				{
					ConErrPuts (_T("Invalid option. Expected format: /C[:]options"));
					freep (arg);
					return 1;
				}
			}
			else if (_tcsnicmp (arg[i], _T("/n"), 2) == 0)
			{
				bNoPrompt = TRUE;
			}
			else if (_tcsnicmp (arg[i], _T("/s"), 2) == 0)
			{
				bCaseSensitive = TRUE;
			}
			else if (_tcsnicmp (arg[i], _T("/t"), 2) == 0)
			{
				LPTSTR s;

				if (arg[i][2] == _T(':'))
				{
					cDefault = arg[i][3];
					s = &arg[i][4];
				}
				else
				{
					cDefault = arg[i][2];
					s = &arg[i][3];
				}

				if (*s != _T(','))
				{
					ConErrPuts (_T("Invalid option. Expected format: /T[:]c,nn"));
					freep (arg);
					return 1;
				}

				s++;
				nTimeout = _ttoi(s);
				bTimeout = TRUE;
			}
			else if (arg[i][0] == _T('/'))
			{
				ConErrPrintf (_T("Illegal Option: %s"), arg[i]);
				freep (arg);
				return 1;
			}
		}
	}

	/* print text */
	if (lpText)
		ConOutPrintf (_T("%s"), lpText);

	/* print options */
	if (bNoPrompt == FALSE)
	{
		ConOutPrintf (_T("[%c"), lpOptions[0]);

		for (i = 1; (unsigned)i < _tcslen (lpOptions); i++)
			ConOutPrintf (_T(",%c"), lpOptions[i]);

		ConOutPrintf (_T("]?"));
	}

	ConInFlush ();

	if(!bTimeout)
	{
		while (TRUE)
		{
			ConInKey (&ir);

			val = IsKeyInString (lpOptions,
#ifdef _UNICODE
			                     ir.Event.KeyEvent.uChar.UnicodeChar,
#else
			                     ir.Event.KeyEvent.uChar.AsciiChar,
#endif /* _UNICODE */
			                     bCaseSensitive);

			if (val >= 0)
			{
				ConOutPrintf (_T("%c\n"), lpOptions[val]);

				nErrorLevel = val + 1;

				break;
			}

			Beep (440, 50);
		}

		freep (arg);
		return 0;
	}

	clk = GetTickCount ();
	amount = nTimeout*1000;

loop:
	GCret = GetCharacterTimeout (&Ch, amount - (GetTickCount () - clk));

	switch (GCret)
	{
		case GC_TIMEOUT:
#ifdef _DEBUG
			DebugPrintf (_T("GC_TIMEOUT\n"));
			DebugPrintf (_T("elapsed %d msecs\n"), GetTickCount () - clk);
#endif /* _DEBUG */
			break;

		case GC_NOKEY:
#ifdef _DEBUG
			DebugPrintf(_T("GC_NOKEY\n"));
			DebugPrintf(_T("elapsed %d msecs\n"), GetTickCount () - clk);
#endif /* _DEBUG */
			goto loop;

		case GC_KEYREAD:
#ifdef _DEBUG
			DebugPrintf(_T("GC_KEYREAD\n"));
			DebugPrintf(_T("elapsed %d msecs\n"), GetTickCount () - clk);
			DebugPrintf(_T("read %c"), Ch);
#endif /* _DEBUG */
			if ((val=IsKeyInString(lpOptions,Ch,bCaseSensitive))==-1)
			{
				Beep (440, 50);
				goto loop;
			}
			cDefault=Ch;
			break;
	}

#ifdef _DEBUG
	DebugPrintf(_T("exiting wait loop after %d msecs\n"),
	            GetTickCount () - clk);
#endif /* _DEBUG */

	val = IsKeyInString (lpOptions, cDefault, bCaseSensitive);
	ConOutPrintf (_T("%c\n"), lpOptions[val]);

	nErrorLevel = val + 1;

	freep (arg);

#ifdef _DEBUG
	DebugPrintf (_T("ErrorLevel: %d\n"), nErrorLevel);
#endif /* _DEBUG */

	return 0;
}
#endif /* INCLUDE_CMD_CHOICE */

/* EOF */
