// $Id: windows_threads.c,v 1.14 2008/08/07 01:54:20 lpoulsen Exp $
// Copyright (C) 2001 Wi_LAN Technologies, Inc
//
// $Log: windows_threads.c,v $
// Revision 1.14  2008/08/07 01:54:20  lpoulsen
// Fix hang in script processing (unitialized event flag)
//
// Revision 1.13  2008/08/05 23:58:37  lpoulsen
// User interface updates.
//
// Revision 1.12  2008/07/28 22:54:26  lpoulsen
// Tight handshake for V4 scripting
//
// Revision 1.11  2008/05/10 01:17:37  lpoulsen
// Making econ -x filename work (not yet complete)
//
// Revision 1.10  2005/06/23 00:13:30  lpoulsen
// Reduce dependency on pthread.h implementation specifics.
//
// Revision 1.9  2003/08/21 23:46:51  lpoulsen
// Improved handling of -r option to allow selection by name
// Miscellaneous minor cleanup
//
// Revision 1.8  2002/10/16 16:59:47  mourlouk
// Improved way of retrieving MAC addresses - VAL support
//
// Revision 1.6  2002/05/16 17:14:58  mourlouk
// 1.19 Release candidate
//
// Revision 1.5  2002/05/14 23:44:01  mourlouk
// more minor changes ...
//
// Revision 1.4  2002/04/19 19:04:44  mourlouk
// Release 1.15 - It fixes a bug that was introduced in version 1.13 (econ unable to
// read properly multiple MAC addresses)
//
// Revision 1.2  2001/10/30 01:07:45  lpoulsen
// Add "module ID" statement, audit trail generator and copyright line
// Remove "win_or_linux.h" and use a -D in Linux makefile instead.
//
//
#include <stdio.h>
#include <windows.h>

#include "windows_threads.h"
#include "ucon.h"

/* Windows Event Handling */
#define  EVENT_CONSOLE_CARET             0x4001
#define  EVENT_CONSOLE_UPDATE_REGION     0x4002
#define  EVENT_CONSOLE_UPDATE_SIMPLE     0x4003
#define  EVENT_CONSOLE_UPDATE_SCROLL     0x4004
#define  EVENT_CONSOLE_LAYOUT            0x4005
#define  EVENT_CONSOLE_START_APPLICATION 0x4006
#define  EVENT_CONSOLE_END_APPLICATION   0x4007

/* Start of Event Handling Hook *
   #define WINEVENTDLL_API __declspec(dllexport)
   DECLARE_HANDLE(HWINEVENTHOOK);

   typedef BOOL (WINAPI* PFN_AttachConsole)(DWORD);

   typedef VOID (CALLBACK* WINEVENTPROC)(
   HWINEVENTHOOK  hWinEventHook,
   DWORD          event,
   HWND           hwnd,
   LONG           idObject,
   LONG           idChild,
   DWORD          idEventThread,
   DWORD          dwmsEventTime);

   HWINEVENTHOOK WINAPI SetWinEventHook(
   UINT eventMin,
   UINT eventMax,
   DWORD hmodWinEventProc,
   WINEVENTPROC lpfnWinEventProc,
   DWORD idProcess,
   DWORD idThread,
   UINT dwflags
   );
   BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hWinEventHook);
   PFN_AttachConsole fpAttachConsole;

   DWORD  g_dwCurrentProc = 0;
   HWINEVENTHOOK   hEventHook;

   WINEVENTDLL_API BOOL InstallWinEventsHook();
   WINEVENTDLL_API BOOL UninstallWinEventsHook(void);
   * End of Event Handling Hook headers
*/

extern HANDLE Receive_ACK;
extern HANDLE File_ACK;
extern HANDLE available_data;
extern HANDLE buffer_space;
extern HANDLE download_Completed;
extern HANDLE command_Completed;
extern HANDLE never_set;
extern HANDLE server_flags_updated;

HANDLE read_thread_terminated;
extern BOOL gotoConnect;

extern unsigned _stdcall WatchDogThread(void *sesID);

extern unsigned _stdcall BabySitterThread(void *sesID);

void 
self_init_thread()
{
    ; // nothing special in the Windows - currently used only by Linux pthreads
}

void 
self_terminate_thread()
{
    _endthreadex(0);
}

extern struct ether_session active_session;

void 
start_all_threads()
{
    unsigned sendThreadID;
    unsigned readThreadID;
    unsigned receiveThreadID;
    unsigned fileThreadID;
    unsigned watchdogThreadID;
    unsigned babysitterThreadID;
    static int rant;

    struct ether_session *session_id = &active_session;

    econ_phase = ECON_INIT_THREADS;

    // The folowing thread, called "BabySitter" is currently suspended
    // BabySitterThreadHandle = (HANDLE)_beginthreadex
    //     (NULL, 0, &BabySitterThread, 0, 0, &babysitterThreadID);   

    Receive_ACK = CreateEvent(NULL, FALSE, FALSE, NULL);
    File_ACK = CreateEvent(NULL, FALSE, FALSE, NULL);
    available_data = CreateEvent(NULL, FALSE, FALSE, NULL);
    buffer_space = CreateEvent(NULL, FALSE, FALSE, NULL);
    download_Completed = CreateEvent(NULL, FALSE, FALSE, NULL);
    command_Completed = CreateEvent(NULL, FALSE, FALSE, NULL);
    never_set         = (HANDLE) CreateEvent(NULL, FALSE, FALSE, "never_set");
    server_flags_updated = CreateEvent(NULL, FALSE, FALSE, "server_flags_upd");

    SendThreadHandle = (HANDLE) _beginthreadex(NULL, 0,
                           &SendThread, (void *) session_id, 0, &sendThreadID);
    ReadThreadHandle = (HANDLE) _beginthreadex(NULL, 0,
                           &ReadThread, (void *) session_id, 0, &readThreadID);
    ReceiveThreadHandle = (HANDLE) _beginthreadex(NULL, 0,
                     &ReceiveThread, (void *) session_id, 0, &receiveThreadID);
    FileThreadHandle = (HANDLE) _beginthreadex(NULL, 0,
                           &FileThread, (void *) session_id, 0, &fileThreadID);

    if (WatchDogThreadHandle)
    {
        SuspendThread(WatchDogThreadHandle);
        CloseHandle(WatchDogThreadHandle);
    }
    WatchDogThreadHandle = (HANDLE) _beginthreadex(NULL, 0,
                   &WatchDogThread, (void *) session_id, 0, &watchdogThreadID);

    econ_phase = ECON_STARTING;
}

void 
wait_for_all_threads_to_terminate()
{
    int rc;
    DWORD dwMilliseconds = 2000;
    // couldn't find the corresponding posix "join thread" for the windows' threads 
    ThreadTerminationEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    rc = WaitForSingleObject(ThreadTerminationEvent, dwMilliseconds);
    while (rc != WAIT_OBJECT_0)
    {
        if ((FileThreadHandle == NULL) &&
            (SendThreadHandle == NULL) &&
            (ReceiveThreadHandle == NULL) &&
            (ReadThreadHandle == NULL))
            break;

        if (rc == WAIT_ABANDONED)
            break;

        rc = WaitForSingleObject(ThreadTerminationEvent, dwMilliseconds);
    }

    econ_phase = ECON_STARTING;
}

/* code taken from pthreads for win32 

   static void 
   ptw32_cancel_self(void)
   {
   ptw32_throw(PTW32_EPS_CANCEL);

   // Never reached 
   }

   void
   ptw32_cancel_thread(HANDLE threadH)
   {  

   SuspendThread(threadH);

   if (WaitForSingleObject(threadH, 0) == WAIT_TIMEOUT)
   {
   CONTEXT context;
   context.ContextFlags = CONTEXT_CONTROL;
   GetThreadContext(threadH, &context);
   PROGCTR(context) = (DWORD) ptw32_cancel_self;
   SetThreadContext(threadH, &context);
   ResumeThread(threadH);
   }

   }
 */

void
readThreadInit()
{
    initCIOSettings();
    read_thread_terminated = CreateEvent(NULL, FALSE, FALSE, NULL);
}

void
readThreadTerminate()
{
    restoreCIOSettings();
    // if (gotoConnect)
       SetEvent(read_thread_terminated);
}

void
SuspendReadThread()
{
    SuspendThread(ReadThreadHandle);
}

void
ResumeReadThread()
{
    ResumeThread(ReadThreadHandle);
}

void 
terminate_all_threads(int read_thread_is_last)
{
    fflush(stdout);
    econ_phase = ECON_CLOSE_THREADS;
    SetEvent(ActivateWatchDog);
}

unsigned _stdcall 
WatchDogThread(void *sesID)
{

    ActivateWatchDog = CreateEvent(NULL, FALSE, FALSE, NULL);
    WaitForSingleObject(ActivateWatchDog, INFINITE);

    if (FileThreadHandle)
    {
        SuspendThread(FileThreadHandle);
        CloseHandle(FileThreadHandle);
    }

    if (SendThreadHandle)
    {
        SuspendThread(SendThreadHandle);
        CloseHandle(SendThreadHandle);
    }

    if (ReceiveThreadHandle)
    {
        SuspendThread(ReceiveThreadHandle);
        CloseHandle(ReceiveThreadHandle);
    }

    if (ReadThreadHandle)
    {
    //  if (gotoConnect)
    //    WaitForSingleObject(read_thread_terminated, INFINITE);

        SuspendThread(ReadThreadHandle);
        CloseHandle(ReadThreadHandle);
    }

    FileThreadHandle = SendThreadHandle =
    ReceiveThreadHandle = ReadThreadHandle = NULL;

    _flushall();
    SetEvent(ThreadTerminationEvent);
    _endthreadex(0);
}

unsigned _stdcall 
BabySitterThread(void *sesID)
{

#define BS_CLOSE_ADAPTER_DELAY 6000
#define BS_CLOSE_THREADS_DELAY 6000
#define BS_CONNECT_RADIO_DELAY 15000
#define BS_DISCOVERY_DELAY 12000

    while (TRUE)
    {
        Sleep(1500);
        if (econ_phase != ECON_STARTING)
        {
            int msdelay;
            // give an extra second
            switch (econ_phase)
            {
            case ECON_CLOSE_ADAPTER:
                msdelay = BS_CLOSE_ADAPTER_DELAY;
                break;
            case ECON_INIT_THREADS:
            case ECON_CLOSE_THREADS:
                msdelay = BS_CLOSE_THREADS_DELAY;
                break;
            case ECON_CONNECT_DEV:
                msdelay = BS_CONNECT_RADIO_DELAY;  // note: it should always be
                                                   //  greater than 
                                            // CONNECTION_ESTABLISHMENT_TIMEOUT
                break;
            case ECON_DISCOVER:
                msdelay = BS_DISCOVERY_DELAY;  // note: it should always be
                                               //  greater than
                                               //  RADIO_DISCOVERY_TIMEOUT
                break;
            default:
                msdelay = 1500;
                break;
            }

            Sleep(msdelay);
            if (econ_phase != ECON_STARTING)
            {
                if (econ_phase == ECON_CONNECT_DEV)
                    printf("Program will terminate. Connection error (code BS-1706)\n");
                else if (econ_phase == ECON_DISCOVER)
                    printf("Program will terminate. Discovery timeout error (code BS-1707)\n");
                else
                    printf("Program will terminate. Possibly low system resources error (code BS-1709)\n");
                fflush(stdout);
                Sleep(3000);
                exit(0);
            }
        }
    }
    _endthreadex(0);
}

// ************************************************************************* //
// **********             Close Button [X] handling            ************* //
// ************************************************************************* //

#ifdef CLOSE_BTN_HANDLING
/*******************************************************************
WinEventProc() - Callback function handles events
*******************************************************************/
WINEVENTDLL_API VOID CALLBACK 
WinEventProc(HWINEVENTHOOK
             hWinEventHook, DWORD event, HWND hwnd,
             LONG idObject, LONG idChild,
             DWORD dwEventThread, DWORD dwmsEventTime)
{
    DWORD dwProcessId;
    char Buf[64];

    GetWindowThreadProcessId(hwnd, &dwProcessId);

    if ((dwProcessId != g_dwCurrentProc) || (g_dwCurrentProc == 0))
        return;                 // that event is for another process   

    if (event == EVENT_CONSOLE_END_APPLICATION)
    {
        // send a close connection packet ...
    }
    return;

    // Source: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnacc/html/atg_consoleaccessibility.asp
}

/*************************************************************************
InstallWinEventsHook() - Initalize the Active Accessibility subsystem
*************************************************************************/
WINEVENTDLL_API BOOL 
InstallWinEventsHook()
{
    BOOL bRet;
    HINSTANCE hDLLInst;
    // get my process ID
    g_dwCurrentProc = GetCurrentProcessId();

    // Check for AttachConsole
    hDLLInst = LoadLibrary("kernel32.dll");
    fpAttachConsole = (BOOL(_stdcall *) (DWORD)) GetProcAddress(hDLLInst, "AttachConsole");

    if (fpAttachConsole == NULL)
        return FALSE;

    FreeConsole();
    bRet = fpAttachConsole(g_dwCurrentProc);
    if (!bRet)
        return FALSE;

    // Set up event call back
    hEventHook =
        SetWinEventHook(EVENT_CONSOLE_START_APPLICATION, // We want all events
                        EVENT_CONSOLE_END_APPLICATION,  //
                        NULL,                          // Use our own module
                        WinEventProc,                 // Our callback function
                        0,                           // All processes
                        0,                          // All threads
                        0);

    // Did we install correctly? 
    if (hEventHook)
        return TRUE;
    else
        return FALSE;
}

/*************************************************************************
UninstallWinEventsHook() - Shuts down the Active Accessibility subsystem
*************************************************************************/
WINEVENTDLL_API BOOL 
UninstallWinEventsHook(void)
{
    // Remove the WinEvent hook    
    UnhookWinEvent(hEventHook);

    return (TRUE);
}

#endif
// ************************************************************************* //
// **********                 End of Handling                  ************* //
// ************************************************************************* //
