/*
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Copyright (c) 1999-2001 Apple Computer, Inc.  All Rights Reserved. The
 * contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License.  Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  Please
 * see the License for the specific language governing rights and
 * limitations under the License.
 *
 *
 * @APPLE_LICENSE_HEADER_END@
 *
 */
/*
	File:		win32ev.cpp

	Contains:	WSA implementation of socket event queue functions.
	
	Written By:	Denis Serenyi

	
*/

#include "ev.h"
#include "OSHeaders.h"
#include "OSThread.h"
#include "MyAssert.h"

//
// You have to create a window to get socket events? What's up with that?
static HWND sMsgWindow = NULL;

//
LRESULT CALLBACK select_wndproc(HWND inWIndow, UINT inMsg, WPARAM inParam, LPARAM inOtherParam);

void select_startevents()
{
	//
	// This call occurs from the main thread. In Win32, apparently, you
	// have to create your WSA window from the same thread that calls GetMessage.
	// So, we have to create the window from select_waitevent
}

int select_removeevent(int /*which*/)
{
	//
	// Not needed for WSA.
	return 0;
}

int select_watchevent(struct eventreq *req, int which)
{
	return select_modwatch(req, which);
}

int select_modwatch(struct eventreq *req, int which)
{
	//
	// If our WSAAsyncSelect window is not constructed yet, wait
	// until it is construected. The window gets constructed when the server
	// is done starting up, so this should only happen when select_modwatch
	// is being called as the server is starting up.
	while (sMsgWindow == NULL)
		OSThread::Sleep(10);
		
	// Convert EV_RE and EV_WR to the proper WSA event codes.
	// WSA event codes are more specific than what POSIX provides, so
	// just wait on any kind of read related event for EV_RE, same for EV_WR
	long theEvent = 0;
	
	if (which & EV_RE)
		theEvent |= FD_READ | FD_ACCEPT | FD_CLOSE;
	if (which & EV_WR)
		theEvent |= FD_WRITE | FD_CONNECT;
	
	// This is a little bit of a hack, because we are assuming that the caller
	// is actually putting a UInt32 in the void*, not a void*, and we are also
	// assuming caller is not using the 0 - WM_USER range of values, but
	// both of these things are true in the EventContext.cpp code, and this
	// mechanism of passing around cookies is just too convienent to ignore.
	unsigned int theMsg = (unsigned int)(req->er_data);
	
	return ::WSAAsyncSelect(req->er_handle, sMsgWindow, theMsg, theEvent);
}

int select_waitevent(struct eventreq *req, void* /*onlyForMacOSX*/)
{
	if (sMsgWindow == NULL)
	{
		//
		// This is the first time we've called this function. Do our
		// window initialization now.
		
		// We basically just want the simplest window possible.
		WNDCLASSEX theWndClass;
		theWndClass.cbSize = sizeof(theWndClass);
		theWndClass.style = 0;
		theWndClass.lpfnWndProc = &select_wndproc;
		theWndClass.cbClsExtra = 0;
		theWndClass.cbWndExtra = 0;
		theWndClass.hInstance = NULL;
		theWndClass.hIcon = NULL;
		theWndClass.hCursor = NULL;
		theWndClass.hbrBackground = NULL;
		theWndClass.lpszMenuName = NULL;
		theWndClass.lpszClassName = "DarwinStreamingServerWindow";
		theWndClass.hIconSm = NULL;
		
		ATOM theWndAtom = ::RegisterClassEx(&theWndClass);
		Assert(theWndAtom != NULL);
		if (theWndAtom == NULL)
			::exit(-1); // Poor error recovery, but this should never happen.
				
		sMsgWindow = ::CreateWindow(	"DarwinStreamingServerWindow",	// Window class name
										"DarwinStreamingServerWindow",	// Window title bar
										WS_POPUP,	// Window style ( a popup doesn't need a parent )
										0,			// x pos
										0,			// y pos
										CW_USEDEFAULT,	// default width
										CW_USEDEFAULT,	// default height
										NULL,			// No parent
										NULL,			// No menu handle
										NULL,			// Ignored on WinNT
										NULL);			// data for message proc. Who cares?
		Assert(sMsgWindow != NULL);
		if (sMsgWindow == NULL)
			::exit(-1);
	}
	
	MSG theMessage;
	
	//
	// Get a message for my goofy window. 0, 0 indicates that we
	// want any message for that window.
	//
	// Convienently, this function blocks until there is a message, so it works
	// much like waitevent would on Mac OS X.
	UInt32 theErr = ::GetMessage(&theMessage, sMsgWindow, 0, 0);
	
	if (theErr > 0)
	{
		UInt32 theSelectErr = WSAGETSELECTERROR(theMessage.lParam);
		UInt32 theEvent = WSAGETSELECTEVENT(theMessage.lParam);
		
		req->er_handle = theMessage.wParam; // the wParam is the FD
		req->er_eventbits = EV_RE;			// WSA events & socket events don't map...
											// but the server state machines never care
											// what the event is anyway.

		// we use the message # as our way of passing around the user data.
		req->er_data = (void*)(theMessage.message);
		
		//
		// We should prevent this socket from getting events until modwatch is called.
		(void)::WSAAsyncSelect(req->er_handle, sMsgWindow, 0, 0); 
		
		return 0;
	}
	else
	{
		//
		// Do we ever get WM_QUIT messages? Can there ever be an error?
		Assert(0);
		return EINTR;
	}
}


LRESULT CALLBACK select_wndproc(HWND /*inWIndow*/, UINT inMsg, WPARAM /*inParam*/, LPARAM /*inOtherParam*/)
{
	// If we don't return true for this message, window creation will not proceed
	if (inMsg == WM_NCCREATE)
		return TRUE;
	
	// All other messages we can ignore and return 0
	return 0;
}
