/********************************************************************
*   DrvEvents.c
*
*   Macintosh device specific event sub-system implementation.
*
*   Copyright (c) 1994-1997, Willows Software Inc.  All rights reserved.  
*
********************************************************************/

#include <string.h>
#include <stdio.h>
#include <unix.h>
#include <AppleEvents.h>
#include <SIOUX.h>

#include "DrvHook.h"
#include "DrvDP.h"
#include "DrvSystem.h"
#include "DrvEvents.h"
#include "DrvErrors.h"
#include "StringUtils.h"
#include "DrvKeyboard.h"
#include "DrvWindows.h"
#include "DrvGlobals.h"

#define NO_POSIX_REDIRECT		TRUE
#include "UnixCompatibility.h"


/* This is EXPORTED from the shared library for now */
DWORD DriverWaitEvent(BOOL noWait);		// LATER:  This should be called down through the PrivateDriverHook()

/* Imported prototypes */
long CursorMoved(EventRecord *event);

/* Local prototypes */
static DWORD DrvSwapMouseButton(BOOL fSwap);
static DWORD DrvBeep(DWORD dwBeep);
static DWORD DrvDblClkTime(BOOL fSet, UINT uInterval);
static DWORD DrvGetSystemTime(void);
static long DoSuspendResumeEvent(Boolean resume, EventRecord *event);
static void DoDiskEvents(long dinfo);
static long TimerEvent(void);

/* Apple event prototypes */
void OpenDoc ( FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle );
void FailErr(OSErr err); 
OSErr GotRequiredAEParams (AppleEvent *theAppleEvent);
pascal OSErr HandleOAPP(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ); 
pascal OSErr HandleODOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon );
pascal OSErr HandlePDOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon );
OSErr HandleDocs ( AppleEvent *theAppleEvent, AppleEvent *reply, Boolean opening );
Boolean PreFlightDocs (Boolean opening, short itemCount, Handle *userDataHandle);
void PostFlightDocs ( Boolean opening, short itemCount, Handle userDataHandle );


#define HERTZ 60
#define TIMETOCALLWNE 2			/* Number of ticks to have passes prior to calling WaitNextEvent */

/* Apple Event handler universal procedure pointers */
AEEventHandlerUPP	OAPPHandlerUPP, ODOCHandlerUPP, PDOCHandlerUPP, QUITHandlerUPP;
char gOpenDocument[256];

/* Our globals */
int gInBackground = false;
RgnHandle gMouseRgn = NULL;
static int gTimer_ms = 0;

/********************************************************************
*  PrivateEventHook
*
********************************************************************/
DWORD PrivateEventHook(WORD wFunc, LPARAM dwParam1, LPARAM dwParam2, LPVOID lpStruct)
{
#pragma unused(lpStruct)

    switch (wFunc) {
	case DSUBSYSTEM_INIT:
		if (dwParam2)								/* Initializing */
			InitAppleEvents();
	    return TRUE;

	case DSUBSYSTEM_EVENTS:
	case DSUBSYSTEM_GETCAPS:
	    return FALSE;

	/* dwParam1 - swap flag */
	case PEH_MOUSEBUTTON:
	    return DrvSwapMouseButton((BOOL)dwParam1);

	/* dwParam1 - set/get flag */
	/* dwParam2 - value to set */
	case PEH_DBLCLKTIME:
	    return DrvDblClkTime((BOOL)dwParam1,(UINT)dwParam2);

	/* dwParam1 - duration */
	case PEH_BEEP:
	    return DrvBeep((DWORD)dwParam1);

	case PEH_SYSTIME:
	    return DrvGetSystemTime();

	default:
	    return 0L;
    }
}

/********************************************************************
*   The HEARTBEAT OF AMERICA!
*	This is it!, the main event loop.
********************************************************************/
DWORD DriverWaitEvent(BOOL noWait)
{
EventRecord	event;
long sleep = 2;					/* Default sleep time */
long hadEvent = false;
long timeToCallWNE = 0;
short SIOUXDidEvent;
	
	/* Determine if we are to wait for an event */
	if (noWait)
		sleep = 1;			/* No waiting, so return quickly */
		
	while(1) {
		if (CursorMoved(&event))	
		    hadEvent |= PrivateWindowsHook(DSUBSYSTEM_EVENTS, 0L, 0L, &event);

		hadEvent |= TimerEvent();					/* Check the timer */
		
		/* Since WNE is not a native call, limit the number of times it is used */	
		if (LMGetTicks() > timeToCallWNE) {
//		eventMask = LMGetSysEvtMask();
			/* Reset the event mask.  For some reason WNE is setting back to all - keyUp events!! */
			SetEventMask(everyEvent);			/* We should undo this later */
//		eventMask = LMGetSysEvtMask();

			if(WaitNextEvent(everyEvent, &event, sleep, gMouseRgn)) {
#ifdef SHOWCONSOLE
				SIOUXDidEvent = SIOUXHandleOneEvent(&event);
#else
				SIOUXDidEvent = FALSE;
#endif
				if (!SIOUXDidEvent)
					switch(event.what) {
					case mouseUp:	
					case mouseDown:	
					case updateEvt:
					case activateEvt:
						/* Send all window related events to the Window Sub System for handling */
				    	/* If the event was handled and posted to the lib, it will return true */
					    hadEvent |= PrivateWindowsHook(DSUBSYSTEM_EVENTS, 0L, 0L, &event);
						break;
					
					case autoKey:
					case keyDown:
					case keyUp:
					    hadEvent |= PrivateKeyboardHook(DSUBSYSTEM_EVENTS, 0L, 0L, &event);
						break;
						
					case osEvt:
						if ((event.message >> 24) == suspendResumeMessage)
						hadEvent |= DoSuspendResumeEvent((event.message & resumeFlag), &event);
						break;

					case APP_EVT:
						/* Send all app3Evt's to the window subsystem for now */
					    hadEvent |= PrivateWindowsHook(DSUBSYSTEM_EVENTS, 0L, 0L, &event);
						break;

					case diskEvt:
						DoDiskEvents(event.message);	break;
					case kHighLevelEvent:
					    AEProcessAppleEvent(&event);
					    break;
					default:
						break;
					}					/* switch(event.what) */
			}							/* WNE */
				timeToCallWNE = LMGetTicks() + TIMETOCALLWNE;		/* Reset timer */
		}								/* if timeToCallWNE */
		
		if (hadEvent || noWait)
			break;								/* Return control to library */
	}
	
	return(hadEvent);
}


/********************************************************************
*   TimerEvent
*
********************************************************************/
static long TimerEvent(void)
{
int curTime_ms;
static long timerStart_ms;

	/* if we have a timer, when should it go off? */
	/* timer is number of milliseconds to wait... */
	if (!gTimer_ms) {
		gTimer_ms = LibCallback(TWINLIBCALLBACK_TIMEOUT, 0, 0, (LPVOID)0);
		timerStart_ms = LMGetTicks() * 16;
	}

	if (gTimer_ms) {
		curTime_ms = (LMGetTicks() * 16) - timerStart_ms;
		if (curTime_ms >= gTimer_ms) {
			LibCallback(TWINLIBCALLBACK_TIMEOUT, gTimer_ms, 0, (LPVOID)0);
			gTimer_ms = 0;
			return(TRUE);
		}
	}

	return(FALSE);
}


/********************************************************************
*  DrvSwapMouseButton
*
********************************************************************/
static DWORD DrvSwapMouseButton(BOOL fSwap)
{
	gDspl->swappedButtons = fSwap;
	return 0L;
}


/********************************************************************
*  DrvDblClkTime
*
********************************************************************/
static DWORD DrvDblClkTime(BOOL fSet, UINT uInterval)
{
long oldTime;

	/* Get suggested interval in milliseconds */    
    oldTime = (TickCount() * 1000) / HERTZ;		

	/* Internally we use Ticks (1/60th sec), so convert accordingly */
    if (fSet)
		gDspl->doubleClickTime = ((long)uInterval * HERTZ) / 1000;		

    return oldTime;
}


/********************************************************************
*  DrvBeep
*
********************************************************************/
static DWORD DrvBeep(DWORD dwBeep)
{
#pragma unused(dwBeep)
//int duration = (dwBeep ==0) ? 100:LOWORD(dwBeep);
    
    /* The duration is no longer used by the SysBeep call. */
    SysBeep(1);		
    
    return 0L;
}


/********************************************************************
*  DrvGetSystemTime
*
********************************************************************/
static DWORD DrvGetSystemTime(void)
{
DWORD dwTime;

	/* Convert from ticks (1/60th sec) to milliseconds */
	dwTime = TickCount();
	dwTime *=  1000;
	dwTime /=  HERTZ;

    return dwTime;
}


/********************************************************************
*  OpenDoc
*
*	Open a document.
********************************************************************/
void OpenDoc ( FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle )
{
#pragma unused(userDataHandle)
Str255 fullPathName;
char fname[256];
int err;

	if (!opening)
		return;					/* Do not handle printing now */
		
	/* Since we are not doing anything special here, just place it into */
	/*   a location for the command line processor to handle.				*/

	PathNameFromDirID(myFSSPtr->parID, myFSSPtr->vRefNum, fullPathName);
	/* Set the directory now, the library will do a getcwd() for the C: directory.*/
	err = chdir(p2cstr(fullPathName));

	/* Get a c string copy of the file name (so as not to modify the source's FSSpec) */
	p2cstrcpy(fname, myFSSPtr->name);

	/* Save the document to open when the library asks for the command line args */
	strcpy(gOpenDocument, "./");		/* Relative path, prepend with delimeter */
	strcat(gOpenDocument, fname);		/* Now add the actual file name */

#ifdef DEBUG
	printf("AEvt: Setting Dir to %s\n", fullPathName);
	printf("AEvt: Open Document %s\n", gOpenDocument);
	getcwd(fname, 256);
#endif	
}


/********************************************************************
*  GetOneHighLevelEvent
*
*	Process events until one apple event is found.  This is to intercept the ODOC Apple Events
*   so that they can be placed into the command line arguments.
********************************************************************/
int GetOneHighLevelEvent(int timeout)
{
EventRecord event;
Boolean gotHighLevelEvent = FALSE, done = FALSE;
long startTick;

	/* The system needs a little time to asyncronously send the apple event. */
	/* Since we need it for the "Command Line Arguments", wait here until */
	/*   an event occurs (or we timeout).  
	/* We should get an ODOC, PDOC, or OAPP event.*/

	startTick = TickCount();
	/* Process all the high level events */
	while(!done) {
		/* When we get an event, process through all of them real quick. */
		while(WaitNextEvent(highLevelEventMask, &event, 30, NULL)) {
    		switch (event.what) {
			case kHighLevelEvent:
		    	AEProcessAppleEvent(&event);
		    	done = TRUE;
				gotHighLevelEvent = TRUE;			/* Got one, done after processing all of them */
	    		break;
			default:
				break;
			}
		}

		/* Don't wait too long for the system! */
		if ((TickCount() - startTick) > timeout)
			done = TRUE;						/* Timeout, bail! */
	}
	
	return (gotHighLevelEvent);
}



/********************************************************************
*   DoSuspendResumeEvent
*
********************************************************************/
static long DoSuspendResumeEvent(Boolean resume, EventRecord *event)
{
WindowPtr curFrontWin = FrontWindow();

	if (resume) {
		gInBackground = false;
		DoActivate(curFrontWin, !gInBackground, event);
		event->message = EVT_RESUMEAPP;			/* Application is being resumed */
	}
	else {
		gInBackground = true;
		DoActivate(curFrontWin, !gInBackground, event);	
		event->message = EVT_SUSPENDAPP;		/* Application is being suspended */
	}

	/* Now send any internal events to react to the event */
	event->what = APP_EVT;								/* Use this for internal events */
	PrivateCursorHook(DSUBSYSTEM_EVENTS, 0L, 0L, event);
    return PrivateKeyboardHook(DSUBSYSTEM_EVENTS, 0L, 0L, event);

}



/********************************************************************
*   DoDiskEvents
*	Just checks the error code from the disk mount,
*	and puts up the 'Format' dialog (through DIBadMount) if need be.
*	You can do much more here if you care about what disks are in the drive.
********************************************************************/
static void DoDiskEvents(long dinfo)            /* hi word is error code, lo word is drive number */
{
    short hival, loval, tommy;
    Point fredpoint =  {
        40, 40
    };
    hival = HiWord(dinfo);
    loval = LoWord(dinfo);
    if (hival != noErr)                                     /* something happened */ {
        tommy = DIBadMount(fredpoint, dinfo);
    }
}


/********************************************************************
*  InitAppleEvents
*
*	Install the core apple event handlers.
********************************************************************/
void InitAppleEvents(void) 
{
OSErr aevtErr = noErr;
static Boolean initialized = FALSE;

	if (initialized)
		return;

	strcpy(gOpenDocument, "");			/* Clear the open document buffer */
		
	OAPPHandlerUPP = NewAEEventHandlerProc(HandleOAPP);
	aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, OAPPHandlerUPP, 0, false);

	if ( aevtErr == noErr )	{
		ODOCHandlerUPP = NewAEEventHandlerProc(HandleODOC);
		aevtErr = AEInstallEventHandler ( kCoreEventClass, kAEOpenDocuments, ODOCHandlerUPP, 0, false );
	}

	initialized = TRUE;
}


/********************************************************************
*  FailErr
*
*	Called when error is encountered.
********************************************************************/
void FailErr(OSErr err) 
{
	if (err != noErr)
		ErrMsgCode("\pAn Apple Event error occured.", err);
}

/********************************************************************
*  GotRequiredParams
*
*	Called when error is encountered.
********************************************************************/
OSErr GotRequiredAEParams (AppleEvent *theAppleEvent ) 
{
DescType	typeCode;
Size		actualSize;
OSErr		retErr, err;

	err = AEGetAttributePtr ( theAppleEvent, keyMissedKeywordAttr,
					typeWildCard, &typeCode, NULL, 0, &actualSize );
	
	if ( err == errAEDescNotFound )	// we got all the required params: all is ok
		retErr = noErr;
	else if ( err == noErr )
		retErr = errAEEventNotHandled;
	else 
		retErr = err;
	
	return retErr;
}


/********************************************************************
*  HandleOAPP
*
*	This routine is the handler for the oapp (Open Application) event.
********************************************************************/
pascal OSErr HandleOAPP(AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) 
{
#pragma unused(handlerRefcon)
OSErr err;

	FailErr(err = GotRequiredAEParams(theAppleEvent));

#ifdef DEBUG
	printf("AEvt: Opening application...\n");
#endif
	
	if ( reply->dataHandle != NULL )	/*	a reply is sought */
		FailErr(err = AEPutParamPtr ( reply, 'errs', 'TEXT', "Opening", 7 ));
	
	return err;
}


/********************************************************************
*  HandleODOC
*
*	This routine is the handler for the odoc (Open Document) event.
********************************************************************/
pascal OSErr HandleODOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) 
{
#pragma unused(handlerRefcon)

	return (HandleDocs(theAppleEvent, reply, true));	// call the low level routine
}

/********************************************************************
*  HandlePDOC
*
*	This routine is the handler for the pdoc (Print Document) event.
********************************************************************/
pascal OSErr HandlePDOC ( AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) 
{
#pragma unused(handlerRefcon)

	return (HandleDocs(theAppleEvent, reply, false));	// call the low level routine
}


/********************************************************************
*  HandleDocs
*
*	This routine is the handler for the odoc and pdoc (Open and Print Document) events.
********************************************************************/
OSErr HandleDocs ( AppleEvent *theAppleEvent, AppleEvent *reply, Boolean opening ) 
{
#pragma unused(reply)
OSErr err;
FSSpec		myFSS;
AEDescList docList;
long index, itemsInList;
Size		actualSize;
AEKeyword	keywd;
DescType	typeCode;
Handle		userDataHandle;

	FailErr(err = AEGetParamDesc ( theAppleEvent, keyDirectObject, typeAEList, &docList ));
	FailErr(err = GotRequiredAEParams ( theAppleEvent ));

	/*	How many items do we have?. */
	FailErr(err = AECountItems ( &docList, &itemsInList ));

	if (PreFlightDocs (opening, itemsInList, &userDataHandle))	{	// let the app do any preflighting it might need
		for ( index = 1; index <= itemsInList; index++ ) {
			FailErr(err = AEGetNthPtr ( &docList, index, typeFSS, &keywd, &typeCode,
					(Ptr) &myFSS, sizeof ( myFSS ), &actualSize ));
	
			OpenDoc( &myFSS, opening, userDataHandle );	// call the userProc
		}
	
		PostFlightDocs (opening, itemsInList, userDataHandle);	// cleanup time
	}
	else
		err = errAEEventNotHandled;	// tells AEM that we didn't handle it!


	FailErr(AEDisposeDesc ( &docList ));

	return (err);
}



/********************************************************************
*  PreFlightDocs
*
********************************************************************/
Boolean PreFlightDocs (Boolean opening, short itemCount, Handle *userDataHandle)
{
#pragma unused(itemCount, userDataHandle)

	return opening;		// we support opening, but not printing
}


/********************************************************************
*  PostFlightDocs
*
********************************************************************/
void PostFlightDocs ( Boolean opening, short itemCount, Handle userDataHandle )
{
#pragma unused(opening, itemCount, userDataHandle)

}


