/* MessageLoop.xs
 *
 *  (c) 2013 Michael Roberts. All rights reserved.
 *
 *  You may distribute under the terms of either the GNU General Public
 *  License or the Artistic License, as specified in the README file.
 *
 *  This is all just cut down from Win32::OLE, so some of it
 *  probably isn't needed. But if it ain't broke, I'm not going to fix it.
 */

// #define _DEBUG

#define register /* be gone */

#define MY_VERSION "Win32::MessageLoop(" XS_VERSION ")"

#include <math.h>	/* this hack gets around VC-5.0 brainmelt */
#define _WIN32_DCOM
#include <windows.h>

#ifdef _DEBUG
#   include <crtdbg.h>
#   define DEBUGBREAK _CrtDbgBreak()
#else
#   define DEBUGBREAK
#endif

// MingW is missing these 2 macros
#ifndef V_RECORD
#   ifdef NONAMELESSUNION
#       define V_RECORDINFO(X) ((X)->__VARIANT_NAME_1.__VARIANT_NAME_2.__VARIANT_NAME_3.__VARIANT_NAME_4.pRecInfo)
#       define V_RECORD(X)     ((X)->__VARIANT_NAME_1.__VARIANT_NAME_2.__VARIANT_NAME_3.__VARIANT_NAME_4.pvRecord)
#   else
#       define V_RECORDINFO(X) ((X)->pRecInfo)
#       define V_RECORD(X)     ((X)->pvRecord)
#   endif
#endif

extern "C" {
#ifndef GUIDKIND_DEFAULT_SOURCE_DISP_IID
#   define GUIDKIND_DEFAULT_SOURCE_DISP_IID 1
#endif

#ifdef __CYGWIN__
#   undef WIN32			/* don't use with Cygwin & Perl */
#   include <netdb.h>
#   include <sys/socket.h>
#   include <unistd.h>

#   ifndef strrev
#     define strrev my_strrev

static char *
my_strrev(char *str)
{
    char *left = str;
    char *right = left + strlen(left) - 1;
    while (left < right) {
        char temp = *left;
        *left++ = *right;
        *right-- = temp;
    }
    return str;
}

#   endif /* strrev */
#endif

#define PERL_NO_GET_CONTEXT
#define NO_XSLOCKS
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "patchlevel.h"

#undef WORD
typedef unsigned short WORD;

#ifndef _WIN64
#  define DWORD_PTR	DWORD
#endif

#ifndef _DEBUG
#   define DBG(a)
#else
#   define DBG(a)  MyDebug a
void
MyDebug(const char *pat, ...)
{
    DWORD thread = GetCurrentThreadId();
    void *context = PERL_GET_CONTEXT;
    char szBuffer[512];
    char *szMessage = szBuffer + sprintf(szBuffer, "[%d:%p] ", thread, context);
    va_list args;
    va_start(args, pat);
    vsprintf(szMessage, pat, args);
    OutputDebugString(szBuffer);
    va_end(args);
}
#endif


//------------------------------------------------------------------------


inline void
SpinMessageLoop(void)
{
    MSG msg;

    DBG(("SpinMessageLoop\n"));
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
	TranslateMessage(&msg);
	DispatchMessage(&msg);
    }

}   /* SpinMessageLoop */

UINT_PTR timer;
inline void
MyTimerProc(HWND     hwnd, 
            UINT     uMsg, 
            UINT_PTR idEvent, 
            DWORD    dwTime)
{
	if (timer) {
		KillTimer(hwnd, idEvent);
		timer = 0;
	}
	PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0);
}

inline void
MessageLoop(UINT timeout)
{
	MSG message;

    if (timeout) {
    	timer = SetTimer (NULL, 0, timeout, &MyTimerProc);
    }
	DBG(("MessageLoop\n"));
	while (GetMessage(&message, NULL, 0, 0)) {
	    if (message.hwnd == NULL && message.message == WM_USER)
		break;
	    TranslateMessage(&message);
	    DispatchMessage(&message);
	}

}


}   /* extern "C" */

/*##########################################################################*/

MODULE = Win32::MessageLoop		PACKAGE = Win32::MessageLoop

PROTOTYPES: DISABLE

void
SpinMessageLoop()
PPCODE:
{
	SpinMessageLoop();
    XSRETURN_EMPTY;
}

void
MessageLoop(...)
PPCODE:
{
    HV *stash = gv_stashsv(ST(0), TRUE);

    UINT timeout = 0;
    if (items > 1) {
    	timeout = SvUV(ST(1));
    }
	MessageLoop(timeout);	
    XSRETURN_EMPTY;
}

void
QuitMessageLoop(...)
PPCODE:
{
	PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0);
    XSRETURN_EMPTY;
}