/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Netscape Portable Runtime (NSPR).
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/*
** File:        winevent.c
** Description: Test functions in plevent.c using Windows
**
** The winevent test exercises the PLEvent library in a maner
** similar to how the Mozilla (or NGLayout) Client will use
** it in a Windows environment.
**
** This test is based on ideas taken from Charles Petzold's
** book "Programming Windows 3.1". License to use is in the
** book. It has been ported to Win32.
**
** Operation:
** The initialization is a standard Windows GUI application
** setup. When the main window receives its WM_CREATE
** message, a child window is created, a edit control is
** instantiated in that window.
**
** A thread is created; this thread runs in the function:
** TimerThread(). The new thread sends a message every second
** via the PL_PostEvent() function. The event handler
** HandlePadEvent() sends a windows message to the edit
** control window; these messages are WM_CHAR messages that
** cause the edit control to place a single '.' character in
** the edit control. 
**
** After a deterministic number of '.' characters, the
** TimerThread() function is notified via a global variable
** that it's quitting time.
** 
** TimerThread() callse TestEvents(), an external function
** that tests additional function of PLEvent.
**
*/

#include "nspr.h"
#include "plevent.h"

#include <windows.h>
#include <commdlg.h>

#define ID_EDIT     1

/* 
** Declarations for NSPR customization
** 
*/
typedef struct PadEvent
{
    PLEvent plEvent;
    int     unused;    
} PadEvent;

static void PR_CALLBACK TimerThread( void *arg);
static void PR_CALLBACK HandlePadEvent( PadEvent *padEvent );
static void PR_CALLBACK DestroyPadEvent( PadEvent *padevent );

static PRThread *tThread;
static PLEventQueue *padQueue; 
static long ThreadSleepTime = 1000; /* in milli-seconds */
static long timerCount = 0;
static HWND hDlgModeless ;
static HWND hwndEdit ;
static PRBool testFinished = PR_FALSE;
static HWND     hwnd ;

LRESULT CALLBACK WinProc (HWND, UINT, WPARAM, LPARAM);

TCHAR appName[] = TEXT ("WinEvent") ;

int WINAPI WinMain(
    HINSTANCE hInstance, 
    HINSTANCE hPrevInstance,
    PSTR szCmdLine, 
    int iCmdShow
    )
{
    MSG      msg ;
    WNDCLASS wndclass ;
    HANDLE   hAccel ;
    
    PR_Init(0, 0, 0);

    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WinProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
    wndclass.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = appName;
     
    if ( !RegisterClass( &wndclass ))
    {
        MessageBox( NULL, 
            TEXT( "This program needs Win32" ),
            appName, 
            MB_ICONERROR );
        return 0; 
    }
     
    hwnd = CreateWindow( appName, 
        appName,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 
        CW_USEDEFAULT,
        CW_USEDEFAULT, 
        CW_USEDEFAULT,
        NULL, 
        NULL, 
        hInstance, 
        NULL);
     
     ShowWindow( hwnd, iCmdShow );
     UpdateWindow( hwnd ); 
     
     for(;;)
     {
        if ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE  ))
        {
            if ( GetMessage( &msg, NULL, 0, 0 ))
            {
                if ( hDlgModeless == NULL || !IsDialogMessage( hDlgModeless, &msg ))
                {
                    if ( !TranslateAccelerator( hwnd, hAccel, &msg ))
                    {
                        TranslateMessage( &msg );
                        DispatchMessage( &msg );
                    } /* end if !TranslateAccelerator */
                } 
            }
            else
            {
                break;    
            } /* end if GetMessage() */
        } 
        else /* !PeekMessage */
        {
            PR_Sleep(50);
        }/* end if PeekMessage() */
     } /* end for() */

     PR_JoinThread( tThread );
     PL_DestroyEventQueue( padQueue );
     PR_Cleanup();
     return msg.wParam ;
}

LRESULT CALLBACK WinProc(
    HWND hwnd, 
    UINT message, 
    WPARAM wParam, 
    LPARAM lParam
)
{
     switch (message)
     {
     case WM_CREATE :
          hwndEdit = CreateWindow(
                        TEXT( "edit" ), 
                        NULL,
                        WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | 
                            WS_BORDER | ES_LEFT | ES_MULTILINE |
                            ES_AUTOHSCROLL | ES_AUTOVSCROLL,
                         0, 0, 0, 0, 
                         hwnd, 
                         (HMENU)ID_EDIT,
                         ((LPCREATESTRUCT)lParam)->hInstance, 
                         NULL);
     
          /* Initialize Event Processing for NSPR
          ** Retrieve the event queue just created
          ** Create the TimerThread
          */

/*
          PL_InitializeEventsLib( "someName" );
          padQueue = PL_GetMainEventQueue();
*/
          padQueue = PL_CreateEventQueue("MainQueue", PR_GetCurrentThread());
		  PR_ASSERT( padQueue != NULL );
          tThread = PR_CreateThread( PR_USER_THREAD,
                    TimerThread,
                    NULL,
                    PR_PRIORITY_NORMAL,
                    PR_LOCAL_THREAD,
                    PR_JOINABLE_THREAD,
                    0 );
          return 0 ;
          
     case WM_SETFOCUS :
          SetFocus( hwndEdit );
          return 0;
          
     case WM_SIZE : 
          MoveWindow( hwndEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE );
          return 0 ;
          
     case WM_COMMAND :
          if ( LOWORD(wParam) == ID_EDIT )
               if ( HIWORD(wParam ) == EN_ERRSPACE || 
                         HIWORD( wParam ) == EN_MAXTEXT )

                    MessageBox( hwnd, TEXT( "Edit control out of space." ),
                        appName, MB_OK | MB_ICONSTOP );
          return 0;
               
     case WM_DESTROY :
          PostQuitMessage(0);
          return 0;
     }
     return DefWindowProc( hwnd, message, wParam, lParam );
}



/*
** TimerThread() -- The Main function of the timer pop thread
**
*/
static void PR_CALLBACK TimerThread( void *arg )
{
    PRIntn  rc;

    do {
        PadEvent   *ev;
        
        /*
        ** Create and Post the event the event
        */
        PL_ENTER_EVENT_QUEUE_MONITOR( padQueue );
        ev = (PadEvent *) PR_NEW( PadEvent );
        PL_InitEvent( &ev->plEvent, NULL, 
                (PLHandleEventProc)HandlePadEvent, 
                (PLDestroyEventProc)DestroyPadEvent );
        PL_PostEvent( padQueue, &ev->plEvent );
        PL_EXIT_EVENT_QUEUE_MONITOR( padQueue );
            
        PR_Sleep( PR_MillisecondsToInterval(ThreadSleepTime) );
    } while( testFinished == PR_FALSE );

    PR_Sleep( PR_SecondsToInterval(4) );

    /*
    ** All done now. This thread can kill the main thread by sending 
    ** WM_DESTROY message to the main window.
    */
    SendMessage( hwnd, WM_DESTROY, 0, 0 );
    return;
}    

static char *startMessage = "Poppad: NSPR Windows GUI and event test program.\n"
                            "Every 1 second gets a '.'.\n"
                            "The test self terminates in less than a minute\n"
                            "You should be able to type in the window.\n\n";

static char *stopMessage = "\n\nIf you saw a series of dots being emitted in the window\n"
                           " at one second intervals, the test worked.\n\n";

/*
** HandlePadEvent() -- gets called because of PostEvent
*/
static void PR_CALLBACK HandlePadEvent( PadEvent *padEvent )
{
    char *cp;
    static const long lineLimit = 10;   /* limit on number of '.' per line */
    static const long timerLimit = 25;  /* limit on timer pop iterations */

    if ( timerCount++ == 0 )
    {
        
        for ( cp = startMessage; *cp != 0 ; cp++ )
        {
            SendMessage( hwndEdit, WM_CHAR, *cp, 1 );
        }
    }
    /* 
    ** Send a WM_CHAR event the edit Window
    */
    SendMessage( hwndEdit, WM_CHAR, '.', 1 );
    
    /*
    ** Limit the number of characters sent via timer pop to lineLimit
    */
    if ( (timerCount % lineLimit) == 0)
    {
        SendMessage( hwndEdit, WM_CHAR, '\n', 1 );
    }

    if ( timerCount >= timerLimit )
    {
        for ( cp = stopMessage; *cp != 0 ; cp++ )
        {
            SendMessage( hwndEdit, WM_CHAR, *cp, 1 );
        }
        testFinished = PR_TRUE;
    }

    return;
}

/*
** DestroyPadEvent() -- Called after HandlePadEvent()
*/
static void PR_CALLBACK DestroyPadEvent( PadEvent *padevent )
{
   PR_Free( padevent );
   return;
}