//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Copyright  1997  Microsoft Corporation.  All Rights Reserved.

Module Name:

     keyengin.cpp  

Abstract:
Functions:
Notes:
--*/
/*++
 
 
Module Name:
 
	Select.cpp
 
Abstract:

	This file contains the source code for the Runtime TUX Test slection
	library.
  
Author:

	Uknown (unknown)
 
Notes:

   
--*/
#include <windows.h>

#include <tux.h>
#include <katoex.h>

#include "ErrMacro.h"
#include <tchar.h>

#include "testSequences.h"


#define WM_LAST_CHARS  (WM_USER + 1)        // get chars in last WM_CHAR msgs (keystroke, one time, may generate multiple WM_CHARs)
#define WM_CHNG_KEYBRD (WM_USER + 2)        // tell window to change keyboards.
#define WM_EMPTY_CHARS (WM_USER + 3)		// clear last chars array

#define TEST_CHARS_EVENT		0				// offset where to set hCharsEvent that is used to signal TestKeyMapChar for retriving last chars
#define TEST_EXTRA_CHAR_EVENT	4				// offset where to set hExtraCharEvent that is designed for signalling TestKeyMapChar to get the info whether extra char(s) is generated

/* #define  WM_INIT        7772
#define  WM_GOAL        7771
#define  zy  GetSystemMetrics(SM_CYSCREEN)
#define  zx  GetSystemMetrics(SM_CXSCREEN)
#define  WM_SWITCHPROC  7773
int       goKey = 32;   // Space

  */

HANDLE             g_hThread  = NULL;

#ifdef _DEBUG
static TCHAR szDebug[1024];
#endif

static const LPTSTR cszThisFile   =     TEXT("KeyEngin.cpp");

static INT g_nNumExpectChars = 0; // records the amount of expected chars that are generated by one keystroke
/* ------------------------------------------------------------------------
	The struct for creating a thread.
------------------------------------------------------------------------ */
typedef struct KEY_MAP_TEST_PARAMtag
{
    CKato       *pKato;
    HINSTANCE   hInstance;
    HANDLE      hInitEvent;
    HWND        hWnd;

} KEY_MAP_TEST_PARAM, *PKEY_MAP_TEST_PARAM;


/*++
 
KeyMapTestWndProc:
 
	This function is the windows proceedure for the Keyboard mapping test.
	This WndProc remembers the last WM_CHAR, WM_KEYDOWN and WM_SYSKEYDOWN
	received.  When the window receive one of the custom messages 
	WM_LAST_CHARS or WM_LAST_KEYS it returns the last chars or keys respectively.
	(One keystroke may generate multiple WM_CHARs)
 
Arguments:
 
	Standard Windows Procedure Aguments
 
Return Value:
 
	Standard Windows Procedure returns
 
Author:

    Uknown (unknown) 
Notes:
 
--*/
LRESULT CALLBACK KeyMapTestWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
{
	HDC 			hdc;
	PAINTSTRUCT		ps;
	RECT			rect;
	static TCHAR	caLastChars[MAX_NUM_GEN_CHARS]; // stores the chars generated by one keystroke
	static int		nIndexLastChars = 0;

    static CKato    *pKato    = NULL;
    static int		iMsg	  = 0;
    static LPTSTR	aszMsg[]  = { TEXT("Look in Log for test results"),
		                          TEXT("keymap -x50 for US-Keyboard -x60 for Japanese ... -l shows all tests"),
    						      TEXT("Test complete") };
    
    switch(message) 
    {

    case WM_CREATE:
		// if we are created/destroyed several times by the same test we have to 
		// init our variables ourselves.
		iMsg = 0;
		pKato = NULL;


        /* ----------------------------------------------------------------
            The pointer to kato is passed in as the lpCreateParams member
            of the CREATESTRUCT pointed to by lParam.
        ---------------------------------------------------------------- */
        pKato = (CKato *)((LPCREATESTRUCT)lParam)->lpCreateParams;

        __try 
        {
            if(pKato)
                pKato->Log( LOG_DETAIL, TEXT("In %s @ line %d: %s Created"),
                            cszThisFile, __LINE__, ((LPCREATESTRUCT)lParam)->lpszName );
        } 
        __except( (GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
                     EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) 
        {
            SetLastError( ERROR_INVALID_DATA );                        
            return -1;
        } // end __try-__except

        return 0;

    case WM_PAINT: 
    	hdc = BeginPaint( hwnd, &ps );
    	GetClientRect( hwnd, &rect );
		DrawText( hdc, aszMsg[iMsg], -1, &rect, 
			      DT_CENTER );	// | DT_VCENTER );    	
		if(iMsg < 2)
			DrawText( hdc, aszMsg[iMsg+1], -1, &rect, 
				      DT_CENTER | DT_VCENTER );    	

		EndPaint( hwnd, &ps);
		return 0;
        
    case WM_KEYDOWN:
		if(pKato)
            pKato->Log( LOG_DETAIL, 
                        TEXT("In %s @ line %d: Test window received WM_KEYDOWN 0x%04x %d"), 
                        cszThisFile, __LINE__, wParam, wParam);
        break;

    case WM_KEYUP:
        if(pKato)
            pKato->Log( LOG_DETAIL, 
                        TEXT("In %s @ line %d: Test window received WM_KEYUP 0x%04x %d"), 
                        cszThisFile, __LINE__, wParam, wParam);
        break;

    case WM_SYSKEYUP:
        if(pKato)
            pKato->Log( LOG_DETAIL, 
                        TEXT("In %s @ line %d: Test window received WM_SYSKEYUP 0x%04x %d"), 
                        cszThisFile, __LINE__, wParam, wParam);
        break;

	case WM_EMPTY_CHARS:
		// gets caLastChars ready
		nIndexLastChars = 0;
		for(int i = 0; i < MAX_NUM_GEN_CHARS; i++)
			caLastChars[i] = 0;
		return 1;

    case WM_CHAR:
		if(nIndexLastChars == g_nNumExpectChars)
		{
			if(nIndexLastChars == MAX_NUM_GEN_CHARS)
			{
				// records the extra char at the end, and loses the oldest one
				for(int i = 0; i < (nIndexLastChars - 1); i++)
					caLastChars[i] = caLastChars[i+1];
				nIndexLastChars--;
			}
			SetEvent((HANDLE)GetWindowLong(hwnd, TEST_EXTRA_CHAR_EVENT)); // signals extra char is generated
			if(pKato)
				pKato->Log(LOG_FAIL, TEXT("FAIL in %s @ line %d: Test window received extra character 0x%04x \"%c\""), cszThisFile, __LINE__, (TCHAR)wParam, (TCHAR)wParam);
		}
		
		caLastChars[nIndexLastChars] = (TCHAR)wParam;

        if(pKato)
            pKato->Log( LOG_DETAIL, 
                        TEXT("In %s @ line %d: Test window received character 0x%04x \"%c\""), 
                        cszThisFile, __LINE__, caLastChars[nIndexLastChars], caLastChars[nIndexLastChars] );
		nIndexLastChars ++;
		// signals that last chars are ready
		if((INT)nIndexLastChars == g_nNumExpectChars)
			SetEvent((HANDLE)GetWindowLong(hwnd, TEST_CHARS_EVENT));

        return 0;
        
	case WM_LAST_CHARS:
		// returns the address of generated-char array
		return (DWORD)caLastChars;

    case WM_CHNG_KEYBRD:
        if(pKato)
            pKato->Log(LOG_DETAIL, 
						TEXT("In %s @ line %d: Test window now using keyboard 0x%08x"), 
						cszThisFile, __LINE__, lParam);
        return (LRESULT) ActivateKeyboardLayout((HKL) lParam, NULL);

    case WM_DESTROY:
		iMsg = 2;
		InvalidateRect( hwnd, NULL, TRUE );
		UpdateWindow( hwnd );

        if(pKato)
            pKato->Log( LOG_DETAIL, TEXT("In %s @ line %d: Test window received WM_DESTROY"),
                    cszThisFile, __LINE__ );
        PostQuitMessage( 0 );
        return 0;
     
    } // end switch(message)
    
    return DefWindowProc(hwnd, message, wParam, lParam);
    
} // end LRESULT CALLBACK SelectWindProc(HWND , UINT ,WPARAM, LPARAM)


/*++
 
KeyMapTestThread:
 
	This function provides the test window that use used to determine if
	what characters or keys have been pressed. The Thread creates and
	window and then pumps its messages until the window is closed. 
 
Arguments:
 
	pKeyMapTestParams   a struct that has the parameter for creating the
	                    window and returning the handle to the parent
	                    thread
 
Return Value:
 
    The return value of the WndProc.	
 
Author:
 
	Uknown (unknown)
 
Notes:
 
--*/
DWORD KeyMapTestThread( PKEY_MAP_TEST_PARAM pKeyMapTestParams )
{

    HWND hWnd;
    WNDCLASS  wc;
    static TCHAR    szAppName[]     = TEXT("Key Map Test Window");
    static TCHAR    szClassName[]   = TEXT("KeyMapTestWindow");
    MSG msg;
    HINSTANCE       hInstance;
    HANDLE          hCharsEvent;
	HANDLE			hExtraCharEvent;

    // Set local variables
    hInstance = pKeyMapTestParams->hInstance;

    /* --------------------------------------------------------------------
    	The following event is used by the window thread to signal the
    	test thread when expected WM_CHAR(s) is received.
    -------------------------------------------------------------------- */
    hCharsEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    FUNCTION_ERROR( pKeyMapTestParams->pKato, NULL == hCharsEvent, return NULL );

	/* --------------------------------------------------------------------
    	The following event is used by the window thread to signal the
    	test thread when extra WM_CHAR is received.
    -------------------------------------------------------------------- */
    hExtraCharEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    FUNCTION_ERROR( pKeyMapTestParams->pKato, NULL == hExtraCharEvent, return NULL );
    
    /* --------------------------------------------------------------------
    	All the user to set the window name app name.
    -------------------------------------------------------------------- */
    wc.style          = 0L;
    wc.lpfnWndProc    = (WNDPROC)KeyMapTestWndProc;
    wc.cbClsExtra     = 0;
    wc.cbWndExtra     = sizeof(DWORD) + sizeof(DWORD); // stores hCharsEvent and hExtraCharEvent
    wc.hInstance      = hInstance;
	wc.hIcon          = NULL;
    wc.hCursor        = NULL;    
    wc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName   = NULL;
    wc.lpszClassName  = szClassName;
   
    if( !RegisterClass(&wc) )
    {
        ExitThread( GetLastError() );
        
    } // end if( !RegisterClass(&wc) )
    
    hWnd = CreateWindow(szClassName,
                        szAppName,
                        WS_OVERLAPPED,
                        0,
                        0,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        NULL, NULL,
                        hInstance,
                        (LPVOID)(pKeyMapTestParams->pKato));

    pKeyMapTestParams->pKato->Log( LOG_DETAIL,
                                   TEXT("In %s @ line %d: hWnd == %08Xh"),
                                   cszThisFile, __LINE__, hWnd );

    pKeyMapTestParams->hWnd = hWnd;
    SetEvent( pKeyMapTestParams->hInitEvent );

    if( NULL == hWnd )  
        ExitThread( GetLastError() );

	SetWindowLong( hWnd, TEST_CHARS_EVENT, (long)hCharsEvent );
    SetWindowLong( hWnd, TEST_EXTRA_CHAR_EVENT, (long)hExtraCharEvent );
    
    ShowWindow( hWnd, SW_SHOW );
    
    while( GetMessage(&msg,NULL,0,0) )
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        
    } // end while( GetMessage(&msg,NULL,0,0) )

    UnregisterClass( szClassName, hInstance );
    CloseHandle( hCharsEvent );
	CloseHandle( hExtraCharEvent);
    
    /* --------------------------------------------------------------------
    	If not zero then erro
    -------------------------------------------------------------------- */
    ExitThread( msg.wParam );
    // Return is never reached
    return msg.wParam;

}  // end LPFUNCTION_TABLE_ENTRY TuxSelectTests( LPFUNCTION_TABLE_ENTRY lpFTE )


/*++
 
CreateKeyMapTestWindow:
 
    This function creates the KeyMapTestThread that creates the Key Map Test
    Window that receives the key map test keys.
 
Arguments:
 
	pKato   pointer to the kato object to use or NULL if so specified.
 
Return Value:
 
	HWND    handle to the window created.
 
Author:
 
	Uknown (unknown)
 
Notes:
 
--*/
HWND CreateKeyMapTestWindow( HINSTANCE hInstance, CKato *pKato )
{
    KEY_MAP_TEST_PARAM KeyMapTestParams;    
    DWORD              dwRtn;

    KeyMapTestParams.hInstance  = hInstance;
    KeyMapTestParams.pKato      = pKato;
    KeyMapTestParams.hWnd       = NULL;
    
    KeyMapTestParams.hInitEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); 
    if( NULL == KeyMapTestParams.hInitEvent ) return NULL;

    __try {
    
    g_hThread = CreateThread( NULL, 0,
                            (LPTHREAD_START_ROUTINE)KeyMapTestThread,
                            (LPVOID)&KeyMapTestParams, 0,
                            &dwRtn ); 
    if( NULL == g_hThread ) __leave;

    // Wait for window to be created
    dwRtn = WaitForSingleObject( KeyMapTestParams.hInitEvent, 60000 );
    if( WAIT_TIMEOUT == dwRtn ) 
    {
        SetLastError( ERROR_GEN_FAILURE );
        __leave;
        
    } // end if( WAIT_TIMEOUT == dwRtn ) 

    // Insure that window creation didn't fail
    if( FALSE ==  GetExitCodeThread( g_hThread, &dwRtn ) ) __leave;
    
    if( STILL_ACTIVE != dwRtn )
    {
        SetLastError( dwRtn );
        __leave;

    } // end if( STILL_ACTIVE != dwRtn )

    } __finally {

    CloseHandle( KeyMapTestParams.hInitEvent );    

    } // end __try-__finally    

    return KeyMapTestParams.hWnd;

} // end HWND CreateKeyMapTestWindow( CKato *pKato )


/*++
 
RemoveTestWindow:
 
	This function sends a WM_CLOSE message to the test window.
 
Arguments:
 
	hWnd    the test window
 
Return Value:
 
    result returned by SendMessage
 
Author:
 
	Uknown (unknown)
 
Notes:
 
--*/

#define THREAD_WAIT_TIME        60000

DWORD RemoveTestWindow( HWND hWnd )
{
    DWORD   dwResult; 
    
    dwResult = SendMessage( hWnd, WM_CLOSE, 0, 0 ); 

    
    if(WAIT_OBJECT_0 == WaitForSingleObject(g_hThread, THREAD_WAIT_TIME))
        CloseHandle( g_hThread );

    return dwResult;

} // end DWORD RemoveTestWindow( HWND hWnd )


bool ChangeKeyboard(HWND hWnd, HKL newHKL)
{
    HKL oldHKL = (HKL) SendMessage(hWnd, WM_CHNG_KEYBRD, 0, (LPARAM) newHKL);
    return true; 
}

//
// 
//TestKeyMapChar:
//  This function performs a keymap test for characters (some keystrokes may generate more than one char, or even zero).
//
//Arguments:
//  pKato			Kato object to log too
//	hWnd			Handle of the window to receive the character
//	pKeys			Pointer to a list of keys to type
//	nKeys			Number of keys to type
//	caExpects The	characters to expect
//  nNumExpectChars The num of characters to expect
// 
//Return Value:
//	TRUE if pass, else FALSE
// 
//Author:
//	Uknown      (unknown)
//  11/30/04    chetl
// 
//Notes:
//  The keyboard must already be in the correct STATE for the test to pass.
// 
//

bool TestKeyMapChar(CKato *pKato, HWND hWnd, LPBYTE pKeys, DWORD nKeys, const TCHAR caExpects[], INT nNumExpectChars)
{
    DWORD   dwRtn;
	INT     iIdx;
	HANDLE  hCharsEvent;
	HANDLE  hExtraCharEvent;
    TCHAR   *cpChars = NULL;
	int nRetByWM = 0;
	bool bRet = true;

    if(nKeys > MAX_SEQUENCE_LENGTH)
    {
        if(pKato)
            pKato->Log(LOG_FAIL, TEXT("FAIL in %s @ line %d: TEST ERROR - %d keys is greater than %d maximum."),
                    cszThisFile, __LINE__, nKeys, MAX_SEQUENCE_LENGTH);
        return false;
    }

    if(!hWnd)
    {
        if(pKato)
            pKato->Log(LOG_FAIL, TEXT("FAIL in %s @ line %d: TEST ERROR - null window handle passed."),
                    cszThisFile, __LINE__);
        return false;
    }

	if(nNumExpectChars > (INT)MAX_NUM_GEN_CHARS)
	{
        if(pKato)
            pKato->Log(LOG_FAIL, TEXT("FAIL in %s @ line %d: TEST ERROR - %d expected generated chars greater than %d maximum."),
                    cszThisFile, __LINE__, nNumExpectChars, MAX_NUM_GEN_CHARS);
        return false;
    }

    __try
    {
        hCharsEvent = (HANDLE)GetWindowLong(hWnd, TEST_CHARS_EVENT);
		hExtraCharEvent = (HANDLE)GetWindowLong(hWnd, TEST_EXTRA_CHAR_EVENT);
		g_nNumExpectChars = nNumExpectChars;
		
		while(nRetByWM != 1) // waits until just created window is ready and able to response
			nRetByWM = (int)SendMessage(hWnd, WM_EMPTY_CHARS, 0, 0);
			
        for( iIdx = 0; iIdx < (INT)nKeys; iIdx++ )
            keybd_event( pKeys[iIdx], NULL, 0, 0 ); 

		// waits until (all WM_CHARs handled) hCharsEvent signaled
		if(nNumExpectChars > 0)
		{
			dwRtn = WaitForSingleObject(hCharsEvent, 5000);
			if(WAIT_OBJECT_0 != dwRtn)
			{
				if(pKato)
					pKato->Log(LOG_FAIL, TEXT("FAIL in %s @ line %d: Didn't receive Character read event"), cszThisFile, __LINE__);
				bRet = false;
			}
			else
				ResetEvent(hCharsEvent);
		}
		// whether any extra char is generated, and this will slow down the test
		dwRtn = WaitForSingleObject(hExtraCharEvent, 50);
		if(WAIT_OBJECT_0 == dwRtn)
		{
			if(pKato)
				pKato->Log(LOG_FAIL, TEXT("FAIL in %s @ line %d: generated chars more than expected"), cszThisFile, __LINE__);
			bRet = false;
			ResetEvent(hExtraCharEvent);
		}

		cpChars = (TCHAR *)SendMessage(hWnd, WM_LAST_CHARS, 0, 0);

		for( --iIdx; iIdx >= 0; iIdx-- )
            keybd_event(pKeys[iIdx], NULL, KEYEVENTF_KEYUP, 0);

		for(INT i = 0; i < nNumExpectChars; i++)
		{
			if(caExpects[i] != cpChars[i])
			{
				if(pKato)
					pKato->Log(LOG_FAIL, TEXT("FAIL in %s @ line %d:  # %d of %d chars. Window expected 0x%04x \"%c\" but received 0x%04x \"%c\""),
									cszThisFile, __LINE__, i+1, nNumExpectChars, caExpects[i], caExpects[i], cpChars[i], cpChars[i]);
				bRet = false;
			}
		}		
		      
        return bRet;
    } 
    __except( (GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
                 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) 
    {
        SetLastError(ERROR_INVALID_DATA);
        return false;
    }

    return false;
}