/* Copyright 1994, 1995, 1996 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_AE[] =
  "$Id: AE.c 63 2004-12-24 18:19:43Z ctm $";
#endif

/* AppleEvents implementation */

#include "rsys/common.h"

#include "EventMgr.h"
#include "NotifyMgr.h"
#include "AppleEvents.h"
#include "MemoryMgr.h"
#include "ProcessMgr.h"
#include "OSEvent.h"
#include "OSUtil.h"
#include "ProcessMgr.h"
#include "AliasMgr.h"

#include "rsys/mman.h"
#include "rsys/flags.h"

boolean_t send_application_open_aevt_p;

/* dispatching apple events */

P3 (PUBLIC pascal, OSErr, EventHandlerTemplate,
    AppleEvent *, evt, AppleEvent *, reply,
    int32, refcon)
{
  /* template for CTOP_... and PTOC_... flags generation */
  abort ();
#if !defined (LETGCCWAIL)
  return paramErr;
#endif
}

P1 (PUBLIC pascal trap, OSErr, AEProcessAppleEvent,
    EventRecord *, evtrec)
{
  AEEventID event_id;
  AEEventClass event_class;
  DescType dummy_type;
  Size dummy_size;
  TargetID dummy_target_id;
  
  AppleEvent *evt = alloca (sizeof *evt);
  
  EventHandlerProcPtr hdlr;
  Handle evt_data;
  Size evt_data_size;
  int32 refcon, dummy_refcon;
  
  OSErr err;
  OSErr retval;

  /* our current buffer is empty */
  evt_data_size = 0;
  err = AcceptHighLevelEvent (&dummy_target_id,
			      &dummy_refcon,
			      NULL, &evt_data_size);
  if (err != bufferIsSmall)
    AE_RETURN_ERROR (errAEEventNotHandled);
  
  evt_data = NewHandle (CL (evt_data_size));
  if (MemError () != noErr)
    AE_RETURN_ERROR (MemError ());
  
  LOCK_HANDLE_EXCURSION_1
    (evt_data,
     {
       err = AcceptHighLevelEvent (&dummy_target_id,
				   &dummy_refcon,
				   STARH (evt_data), &evt_data_size);
     });
  
  if (err != noErr)
    {
      DisposHandle (evt_data);
      AE_RETURN_ERROR (errAEEventNotHandled);
    }
  
  DESC_TYPE_X (evt) = CLC (typeAppleEvent);
  DESC_DATA_X (evt) = RM (evt_data);
  
  err = AEGetAttributePtr (evt, keyEventClassAttr,
			   typeType, &dummy_type,
			   (Ptr) &event_class, sizeof event_class,
			   &dummy_size);
  if (err != noErr)
    {
      AEDisposeDesc (evt);
      AE_RETURN_ERROR (err);
    }
  event_class = CL (event_class);
  
  err = AEGetAttributePtr (evt, keyEventIDAttr,
			   typeType, &dummy_type,
			   (Ptr) &event_id, sizeof event_id, &dummy_size);
  if (err != noErr)
    {
      AEDisposeDesc (evt);
      AE_RETURN_ERROR (err);
    }
  event_id = CL (event_id);
  
  err = AEGetEventHandler (event_class, event_id, &hdlr, &refcon, FALSE);
  if (err != noErr)
    {
      err = AEGetEventHandler (event_class, event_id, &hdlr, &refcon, TRUE);
      if (err != noErr)
	{
	  AEDisposeDesc (evt);
	  AE_RETURN_ERROR (err);
	}
    }
  hdlr = MR (hdlr);
  refcon = CL (refcon);
  
  {
    AppleEvent *reply = alloca (sizeof *reply);
    AEDesc *target = alloca (sizeof *target);
    ProcessSerialNumber psn;
    
    GetCurrentProcess (&psn);
    
    err = AECreateDesc (typeProcessSerialNumber,
			(Ptr) &psn, sizeof psn, target);
    
    /* create a reply apple event.  currently unused */
    err = AECreateAppleEvent (kCoreEventClass, kAEAnswer,
			      target,
			      /* dummy */ -1, /* dummy */ -1,
			      reply);
    
    retval = CToPascalCall (hdlr,
			    CTOP_EventHandlerTemplate,
			    evt, reply, refcon);
    
    AEDisposeDesc (reply);
  }
  
  AEDisposeDesc (evt);
  AE_RETURN_ERROR (retval);
}

P7 (PUBLIC pascal trap, OSErr, AESend,
    AppleEvent *, evt, AppleEvent *, reply,
    AESendMode, send_mode, AESendPriority, send_priority,
    int32, timeout, IdleProcPtr, idle_proc, EventFilterProcPtr, filter_proc)
{
  AEDesc *target = alloca (sizeof *target);
  DescType target_type, dummy_type;
  Size target_size, dummy_size;
  
  ProcessSerialNumber *target_psn  = alloca (sizeof *target_psn);
  ProcessSerialNumber *current_psn = alloca (sizeof *current_psn);
  
  AEEventID event_id;
  AEEventClass event_class;
  
  OSErr err;
  
  GetCurrentProcess (current_psn);
  err = AEGetAttributePtr (evt, keyAddressAttr,
			   typeWildCard, &target_type,
			   (Ptr) target_psn, sizeof *target_psn, &target_size);
  if (err != noErr)
    /* ### not sure what error we should return here */
    AE_RETURN_ERROR (errAEEventNotHandled);
  
  target_type = CL (target_type);
  target_size = CL (target_size);
  
  if (err != noErr)
    AE_RETURN_ERROR (err);
  
  err = AEGetAttributePtr (evt, keyEventClassAttr,
			   typeType, &dummy_type,
			   (Ptr) &event_class, sizeof event_class,
			   &dummy_size);
  if (err != noErr)
    AE_RETURN_ERROR (err);
  
  err = AEGetAttributePtr (evt, keyEventIDAttr,
			   typeType, &dummy_type,
			   (Ptr) &event_id, sizeof event_id, &dummy_size);
  if (err != noErr)
    AE_RETURN_ERROR (err);
  
  if (target_type == keyProcessSerialNumber
      && target_size == sizeof *target_psn)
    {
      Boolean same_process_p;
      
      err = SameProcess (target_psn, current_psn, &same_process_p);
      if (err != noErr)
	AE_RETURN_ERROR (errAEEventNotHandled);
      
      if (same_process_p)
	{
	  /* we are sending the AppleEvent to ourselves */
	  
	  if ((send_mode & _kAEReplyMask) == kAEWaitReply)
	    {
	      /* ### ignoring timeout/receipt requests */
	      warning_unimplemented ("target kCurrentProcess && kAEWaitReply");
	      AE_RETURN_ERROR (errAEEventNotHandled);
	    }
	  
	  if ((send_mode & _kAEReplyMask) == kAEQueueReply)
	    {
	      warning_unimplemented ("can't handle reply requests yet");
	      AE_RETURN_ERROR (errAEEventNotHandled);
	    }
	  
	  {
	    EventRecord evt_rec;
	    Handle desc_data;
	    int desc_data_size;
	    Point bogo_event_id;
	    
	    desc_data = DESC_DATA (evt);
	    desc_data_size = GetHandleSize (desc_data);
	    
	    evt_rec.what      = CWC (kHighLevelEvent);
	    evt_rec.message   = event_class;
	    
	    *(uint32 *) &bogo_event_id = event_id;
	    evt_rec.where     = bogo_event_id;
	    
	    LOCK_HANDLE_EXCURSION_1
	      (desc_data,
	       {
		 err = PostHighLevelEvent (&evt_rec,
					   /* #### i dunno */
					   NULL, -1,
					   STARH (desc_data), desc_data_size,
					   -1);
	       });
	    
	    AE_RETURN_ERROR (err);
	  }
	}
      else
	{
	  /* since there isn't any other processs this could be
             intended for, panic */
	  warning_unimplemented ("AE target not ProcessSerialNumber");
	  AE_RETURN_ERROR ((send_mode & _kAEReplyMask) == kAEWaitReply ?
			   errAEEventNotHandled : noErr);
	}
    }
  else
    {
      /* target is not a psn, we can't deal with anything else at this
	 time */
      if (target_type == typeApplSignature)
	{
	  warning_unimplemented ("returning invalidConnection");
	  AE_RETURN_ERROR (invalidConnection);
	}

      warning_unimplemented ("bad AE target, not ProcessSerialNumber");
      AE_RETURN_ERROR ((send_mode & _kAEReplyMask) == kAEWaitReply
		       ? errAEUnknownAddressType : noErr);
    }
}

P1 (PUBLIC pascal trap, OSErr, AESuspendTheCurrentEvent,
    AppleEvent *, evt)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}

P4 (PUBLIC pascal trap, OSErr, AEResumeTheCurrentEvent,
    AppleEvent *, evt, AppleEvent *, reply,
    EventHandlerProcPtr, dispatcher,
    int32, refcon)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}

P1 (PUBLIC pascal trap, OSErr, AEGetTheCurrentEvent,
    AppleEvent *, return_evt)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}

P1 (PUBLIC pascal trap, OSErr, AESetTheCurrentEvent,
    AppleEvent *, evt)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}

/* array spew */

P7 (PUBLIC pascal trap, OSErr, AEGetArray,
    AEDescList *, list,
    AEArrayType, array_type,
    AEArrayDataPointer, array_ptr, Size, max_size,
    DescType *, return_item_type,
    Size *, return_item_size, int32 *, return_item_count)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}

P6 (PUBLIC pascal trap, OSErr, AEPutArray,
    AEDescList *, list, AEArrayType, type,
    AEArrayDataPointer, array_data, DescType, item_type,
    Size, item_size, int32, item_count)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}

/* user interaction settings */

AEInteractionAllowed interaction_level = kAEInteractWithSelf;

P1 (PUBLIC pascal trap, OSErr, AESetInteractionAllowed,
    AEInteractionAllowed, level)
{
  /* do nothing; since all levels are a superset of
     kAEInteractWithSelf, which is the only interaction currently
     available */
  
  interaction_level = level;
  AE_RETURN_ERROR (noErr);
}

P1 (PUBLIC pascal trap, OSErr, AEGetInteractionAllowed,
    AEInteractionAllowed *, level_out)
{
  *level_out = interaction_level;
  AE_RETURN_ERROR (noErr);
}

P3 (PUBLIC pascal trap, OSErr, AEInteractWithUser,
    int32, timeout, NMRecPtr, nm_req,
    IdleProcPtr, idle_proc)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}


P1 (PUBLIC pascal trap, OSErr, AEResetTimer,
    AppleEvent *, evt)
{
  warning_unimplemented (NULL_STRING);
  AE_RETURN_ERROR (noErr);
}

/* suspending and resuming AE handling */

/* type coersion */

P6 (PUBLIC pascal, OSErr, CoercePtrTemplate,
    DescType, data_type, Ptr, data, Size, data_size,
    DescType, to_type, int32, refcon, AEDesc *, desc_out)
{
  /* template for CTOP_... and PTOC_... flags generation */
  abort ();
#if !defined (LETGCCWAIL)
  return paramErr;
#endif
}

P4 (PUBLIC pascal, OSErr, CoerceDescTemplate,
    AEDesc *, desc, DescType, to_type, int32, refcon,
    AEDesc *, desc_out)
{
  /* template for CTOP_... and PTOC_... flags generation */
  abort ();
#if !defined (LETGCCWAIL)
  return paramErr;
#endif
}

P5 (PUBLIC pascal trap, OSErr, AECoercePtr,
    DescType, data_type,
    Ptr, data, Size, data_size,
    DescType, result_type, AEDesc *, desc_out)
{
  ProcPtr coercion_hdlr;
  int32 refcon;
  Boolean is_desc_hdlr_p;
  OSErr err;
  
  if (result_type == typeWildCard
      || result_type == data_type)
    {
      AECreateDesc (data_type, data, data_size, desc_out);
      AE_RETURN_ERROR (noErr);
    }
  
  err = AEGetCoercionHandler (data_type, result_type,
			      &coercion_hdlr, &refcon,
			      &is_desc_hdlr_p,
			      FALSE);
  if (err != noErr)
    {
      err = AEGetCoercionHandler (data_type, result_type,
				  &coercion_hdlr, &refcon,
				  &is_desc_hdlr_p,
				  TRUE);
      if (err != noErr)
	AE_RETURN_ERROR (errAECoercionFail);
    }
  
  /* swap things to a normal state */
  coercion_hdlr = MR (coercion_hdlr);
  refcon = CL (refcon);
  
  if (is_desc_hdlr_p)
    {
      descriptor_t *desc = alloca (sizeof *desc);
      
      err = AECreateDesc (data_type, data, data_size, desc);
      if (err != noErr)
	return memFullErr;

      err = CToPascalCall (coercion_hdlr, PTOC_CoerceDescTemplate,
			   desc, result_type, refcon, desc_out);
      
      AEDisposeDesc (desc);
    }
  else
    {
      err = CToPascalCall (coercion_hdlr, PTOC_CoercePtrTemplate,
			   data_type, data, data_size, result_type,
			   refcon, desc_out);
    }
  
  if (err != noErr)
    AE_RETURN_ERROR (errAECoercionFail);
  
  AE_RETURN_ERROR (noErr);
}

#define AEGetParamDesc AEGetKeyDesc

PRIVATE OSErr
parse_evt (const AppleEvent *evtp, AEDesc *desc_out)
{
  OSErr retval;
  AEDesc d;

  /*
   * The cast to AppleEvent * of evtp is so the compiler won't complain
   * about our discarding const.  The problem is AEGetParamDesc doesn't the
   * thing pointed to by its first argument, but the prototype doesn't show
   * this.
   */

  retval = AEGetParamDesc ((AppleEvent *) evtp,
			   keyDirectObject, typeAEList, &d);
  if (retval == noErr)
    {
      LONGINT n;

      retval = AECountItems (&d, &n);
      n = CL (n);
      if (retval == noErr)
	{
	  Handle h;
	  AppParametersPtr p;

	  h = NewHandle (sizeof *p + n * sizeof (FSSpec));
	  if (!h || !(p = (typeof (p)) STARH (h)))
	    retval = MemError();
	  else
	    {
	      LONGINT l;

	      p->magic = CLC (APP_PARAMS_MAGIC);
	      p->n_fsspec = CW (n);
	      for (l = 1; retval == noErr && l <= n; ++l)
		{
		  AEDesc d2;
		  AEKeyword keyword;

		  retval = AEGetNthDesc (&d, 1L, typeAlias, &keyword, &d2);
		  if (retval == noErr)
		    {
		      AliasHandle ah;
		      Boolean wasChanged;

		      ah = (AliasHandle) MR (d2.dataHandle);
		      retval = ResolveAlias (NULL, ah, &p->fsspec[l-1],
					     &wasChanged);
		    }
		}
	      if (retval != noErr)
		DisposHandle (h);
	      else
		{
		  desc_out->descriptorType = CLC(TICK("appa"));
		  desc_out->dataHandle = RM (h);
		}
	    }
	}
    }
  return retval;
}

P3 (PUBLIC pascal trap, OSErr, AECoerceDesc,
    AEDesc *, desc, DescType, result_type, AEDesc *, desc_out)
{
  ProcPtr coercion_hdlr;
  int32 refcon;
  Boolean is_desc_hdlr_p;
  DescType desc_type;
  OSErr err;
  
  desc_type = DESC_TYPE (desc);

  if (desc_type == TICK("aevt") && result_type == TICK("appa"))
    {
      err = parse_evt (desc, desc_out);
      AE_RETURN_ERROR (err);
    }
  
  if (result_type == typeWildCard
      || result_type == desc_type)
    {
      AEDuplicateDesc (desc, desc_out);
      AE_RETURN_ERROR (noErr);
    }
  
  err = AEGetCoercionHandler (desc_type, result_type,
			      &coercion_hdlr, &refcon,
			      &is_desc_hdlr_p,
			      FALSE);
  if (err != noErr)
    {
      err = AEGetCoercionHandler (desc_type, result_type,
				  &coercion_hdlr, &refcon,
				  &is_desc_hdlr_p,
				  TRUE);
      if (err != noErr)
	goto fail;
    }
  
  /* swap things to a normal state */
  coercion_hdlr = MR (coercion_hdlr);
  refcon = CL (refcon);

  if (is_desc_hdlr_p)
    {
      err = CToPascalCall (coercion_hdlr, PTOC_CoerceDescTemplate,
			   desc, result_type, refcon, desc_out);
    }
  else
    {
      Handle desc_data;
      
      desc_data = DESC_DATA (desc);
      
      LOCK_HANDLE_EXCURSION_1
	(desc_data,
	 {
	   err = CToPascalCall (coercion_hdlr, PTOC_CoercePtrTemplate,
				desc_type, STARH (desc_data),
				GetHandleSize (desc_data),
				result_type, refcon, desc_out);
	 });
    }
  
  if (err != noErr)
    goto fail;
  
  AE_RETURN_ERROR (noErr);

 fail:
  desc_out->descriptorType = CLC (typeNull);
  desc_out->dataHandle = CLC (0);
  AE_RETURN_ERROR (errAECoercionFail);
}

P1 (PUBLIC pascal trap, OSErr, AEManagerInfo, LONGINT *, resultp)
{
  OSErr retval;

  *resultp = 0;
  retval = noErr;

  return retval;
}

/* stubs added by Cliff after reading documentation on Apple's web site */

P1 (PUBLIC pascal trap, OSErr, AEDisposeToken,
    AEDesc *, theToken)
{
  warning_unimplemented (NULL_STRING);
  return noErr;
}

P3 (PUBLIC pascal trap, OSErr, AEREesolve,
    AEDesc *, objectSpecifier,
    INTEGER, callbackFlags,
    AEDesc *, theToken)
{
  warning_unimplemented (NULL_STRING);
  return noErr;
}

P4 (PUBLIC pascal trap, OSErr, AERemoveObjectAccessor,
    DescType, desiredClass,
    DescType, containerType,
    ProcPtr, theAccessor,
    BOOLEAN, isSysHandler)
{
  warning_unimplemented (NULL_STRING);
  return noErr;
}

P5 (PUBLIC pascal trap, OSErr, AEInstallObjectAccessor,
    DescType, desiredClass,
    DescType, containerType,
    ProcPtr, theAccessor,
    LONGINT, refcon,
    BOOLEAN, isSysHandler)
{
  warning_unimplemented (NULL_STRING);
  return noErr;
}

P5 (PUBLIC pascal trap, OSErr, AEGetObjectAccessor,
    DescType, desiredClass,
    DescType, containerType,
    ProcPtr *, theAccessor,
    LONGINT *, accessorRefcon,
    BOOLEAN, isSysHandler)
{
  warning_unimplemented (NULL_STRING);
  return noErr;
}

P6 (PUBLIC pascal trap, OSErr, AECallObjectAccessor,
    DescType, desiredClass,
    AEDesc *, containerToken,
    DescType, containerClass,
    DescType, keyForm,
    AEDesc *, keyData,
    AEDesc *, theToken)
{
  warning_unimplemented (NULL_STRING);
  return noErr;
}

P7 (PUBLIC pascal trap, OSErr, AESetObjectCallbacks,
    ProcPtr, myCompareProc,
    ProcPtr, myCountProc,
    ProcPtr, myDisposeTokenProc,
    ProcPtr, myGetMarkTokenProc,
    ProcPtr, myMarkProc,
    ProcPtr, myAdjustMarksProc,
    ProcPtr, myGetErrDescProc)
{
  warning_unimplemented (NULL_STRING);
  return noErr;
}

