/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 *  Simon Fraser <sfraser@netscape.com>
 */


#include <Sound.h>
#include <Scrap.h>

#include "nsAEUtils.h"
#include "nsAETokens.h"
#include "nsAECoreClass.h"
#include "nsAEDocumentClass.h"
#include "nsAEWindowClass.h"

#include "nsAEApplicationClass.h"

#include "nsCommandLineServiceMac.h"

/*----------------------------------------------------------------------------
	AEApplicationClass 
	
----------------------------------------------------------------------------*/
AEApplicationClass::AEApplicationClass()
:	AEGenericClass(cApplication, typeNull)
{
}

/*----------------------------------------------------------------------------
	~AEApplicationClass 
	
----------------------------------------------------------------------------*/
AEApplicationClass::~AEApplicationClass()
{
}

#pragma mark -



/*----------------------------------------------------------------------------
	GetPropertyFromApp 
	
	Override default to customize behaviour.
----------------------------------------------------------------------------*/
void	AEApplicationClass::GetProperty(			DescType			desiredClass,
										const AEDesc*		containerToken,
										DescType			containerClass,
										DescType			keyForm,
										const AEDesc*		keyData,
										AEDesc*			resultToken)
{
	OSErr			err;
	CoreTokenRecord 	token;
	DescType			requestedProperty = **(DescType**)keyData->dataHandle;
	
	token.dispatchClass	= GetClass();
	token.objectClass    	= GetClass();
	token.propertyCode 	= requestedProperty;

	if (CanGetProperty(requestedProperty) || CanSetProperty(requestedProperty))
	{
		err = AECreateDesc(cProperty, (Ptr)&token, sizeof(CoreTokenRecord), resultToken);
		ThrowIfOSErr(err);
	}
	else
	{
		ThrowIfOSErr(errAEEventNotHandled);
	}
}



/*----------------------------------------------------------------------------
	GetItemFromContainer 
	
	Not appropriate for the application
----------------------------------------------------------------------------*/
void AEApplicationClass::GetItemFromContainer(	DescType			desiredClass,
										const AEDesc*		containerToken,
										DescType			containerClass, 
										DescType			keyForm,
										const AEDesc*		keyData,
										AEDesc*			resultToken)
{
	ThrowIfOSErr(errAEEventNotHandled);
}


#pragma mark -

/*----------------------------------------------------------------------------
	HandleClose 
	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleClose(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr 	err 	= noErr;

	StAEDesc	saving;
	StAEDesc	savingIn;
		
	// Extract the [saving yes/no/ask] optional parameter, if present
	err = AEGetParamDesc(appleEvent, 
								  keyAESaveOptions, 
								  typeWildCard, 
								  &saving);
								  
	if (err != errAEDescNotFound)
		ThrowIfOSErr(err);
		
	// Extract the [saving in <alias>] optional parameter, if present
	err = AEGetParamDesc(appleEvent, 
								  keyAEFile, 
								  typeWildCard, 
								  &savingIn);
								  
	if (err != errAEDescNotFound)
		ThrowIfOSErr(err);
			
	// Check for any required parameters we may have missed
	err = CheckForUnusedParameters(appleEvent);
	ThrowIfOSErr(err);
		
	// Now, do the application-related work
	SysBeep(2);
		

}

/*----------------------------------------------------------------------------
	HandleCount 
	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleCount(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr	err 				= noErr;
	long 		numberOfObjects 	= 0L;

	DescType	objectClass;

	if (!reply->dataHandle)
		return;
	
	// Get the class of object that we will count
	err = GetObjectClassFromAppleEvent(appleEvent, &objectClass);
	ThrowIfOSErr(err);

	// Make sure we got & handled all of the required paramters
	err = CheckForUnusedParameters(appleEvent);
	ThrowIfOSErr(err);
	
	// Send back the results
	numberOfObjects = CountApplicationObjects(token, objectClass);
	err = AEPutParamPtr(reply, keyAEResult, typeLongInteger, (Ptr)&numberOfObjects, sizeof(long));
	ThrowIfOSErr(err);
}


/*----------------------------------------------------------------------------
	HandleDataSize 
	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleDataSize(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr 	err = noErr;
	StAEDesc	data;
	long		size  = 0L;
	
	// First, get the data
	HandleGetData(token, appleEvent, reply);
	
	// now, extract it from the reply
	err = AEGetKeyDesc(reply, keyDirectObject, typeWildCard, &data);
	ThrowIfOSErr(err);
	
	size = data.GetDataSize();
	
	// do we leak all the data here?
	err = AEPutParamPtr(reply, 
								 keyAEResult, 
								 typeLongInteger, 
								 (Ptr)&size, 
								 sizeof(long));
	ThrowIfOSErr(err);
}


/*----------------------------------------------------------------------------
	HandleDelete 

	All attempts to delete an empty list are handled here
	Application contains documents and windows, and they can't be deleted
	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleDelete(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr	err = noErr;
		
	if (AEListUtils::TokenContainsTokenList(token))
	{
		long 			numItems;
		
		AECountItems(token, &numItems);

		if (numItems > 0)
			err = errAEEventNotHandled;
	}

	ThrowIfOSErr(err);
}

/*----------------------------------------------------------------------------
	HandleDuplicate 
	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleDuplicate(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	ThrowIfOSErr(errAEEventNotHandled);
}

/*----------------------------------------------------------------------------
	HandleExists 

	If <referenceToObject> exists...
	The AEResolve() function in AERCoreSuite.c will already have filtered
	out all cases where the object did not exist, so this function should
	always return TRUE.
	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleExists(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr	err 		= noErr;
	Boolean	foundIt	= true;

	err = AEPutParamPtr(reply, 
					 keyAEResult, 
					 typeBoolean, 
					 (Ptr)&foundIt, 
					 sizeof(Boolean));
		
	ThrowIfOSErr(err);
}


/*----------------------------------------------------------------------------
	HandleMake 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleMake(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	ThrowIfOSErr(errAEEventNotHandled);
}

/*----------------------------------------------------------------------------
	HandleMove 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleMove(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	ThrowIfOSErr(errAEEventNotHandled);
}

/*----------------------------------------------------------------------------
	HandleRun 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleRun(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr	err = noErr;

        // do stuff on startup that we want to do
        
	err = CheckForUnusedParameters(appleEvent);
	ThrowIfOSErr(err);
}

/*----------------------------------------------------------------------------
	HandleOpen 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleOpen(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr	err;
	
	err = CheckForUnusedParameters(appleEvent);
	ThrowIfOSErr(err);

	long 			numItems, i;
	Boolean		openedGroups = false;

	err = ::AECountItems(token, &numItems);
	ThrowIfOSErr(err);
	
	for (i = 1; i <= numItems; i++)
	{
		FSSpec 		fSpec;
		FInfo 		fndrInfo;
		AEKeyword 	keywd;
		DescType 		returnedType;
		Size 			actualSize;

		err = ::AEGetNthPtr(token, i, typeFSS, &keywd, &returnedType, (Ptr)&fSpec, sizeof(fSpec), &actualSize);
		ThrowIfOSErr(err);
		
		err = ::FSpGetFInfo(&fSpec, &fndrInfo);
		ThrowIfOSErr(err);

		nsMacCommandLine&  cmdLine = nsMacCommandLine::GetMacCommandLine();
		cmdLine.HandleOpenOneDoc(fSpec, fndrInfo.fdType);
	}
}


/*----------------------------------------------------------------------------
	HandlePrint 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandlePrint(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr	err;
	
	err = CheckForUnusedParameters(appleEvent);
	ThrowIfOSErr(err);

	long 			numItems, i;
	Boolean		openedGroups = false;

	err = ::AECountItems(token, &numItems);
	ThrowIfOSErr(err);
	
	for (i = 1; i <= numItems; i++)
	{
		FSSpec 		fSpec;
		FInfo 		fndrInfo;
		AEKeyword 	keywd;
		DescType 		returnedType;
		Size 			actualSize;

		err = ::AEGetNthPtr(token, i, typeFSS, &keywd, &returnedType, (Ptr)&fSpec, sizeof(fSpec), &actualSize);
		ThrowIfOSErr(err);
		
		err = ::FSpGetFInfo(&fSpec, &fndrInfo);
		ThrowIfOSErr(err);

		nsMacCommandLine&  cmdLine = nsMacCommandLine::GetMacCommandLine();
		cmdLine.HandlePrintOneDoc(fSpec, fndrInfo.fdType);
	}
}

/*----------------------------------------------------------------------------
	HandleQuit 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleQuit(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	// get optional saving param
	StAEDesc		savingParam;
	TAskSave		askSave = eSaveUnspecified;
	
	OSErr	err = ::AEGetKeyDesc(appleEvent, keyAESaveOptions, typeEnumeration, &savingParam);
	if (err != errAEDescNotFound)
	{
		DescType		enumValue = savingParam.GetEnumType();
		
		switch (enumValue)
		{
			case 'yes ':		askSave = eSaveYes;		break;
			case 'no  ':		askSave = eSaveNo;			break;
			case 'ask ':		askSave = eSaveAsk;		break;
		}
	}
	
	err = CheckForUnusedParameters(appleEvent);
	ThrowIfOSErr(err);

	nsMacCommandLine&  cmdLine = nsMacCommandLine::GetMacCommandLine();
	err = cmdLine.Quit(askSave);
	ThrowIfOSErr(err);
}

/*----------------------------------------------------------------------------
	HandleSave 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleSave(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	ThrowIfOSErr(errAEEventNotHandled);
}

#if 0
/*----------------------------------------------------------------------------
	HandleSetData 

	
----------------------------------------------------------------------------*/
void AEApplicationClass::HandleSetData(AEDesc *token, const AppleEvent *appleEvent, AppleEvent *reply)
{
	OSErr 			err 		= noErr;	
	StAEDesc			tokenData;
	AETokenDesc		tokenDesc(token);
	DescType		 	propertyCode;
			
	if (token->descriptorType == cProperty)
	{
		propertyCode = tokenDesc.GetPropertyCode();

		if (CanSetProperty(propertyCode))
		{
			// only the clipboard property is writeable
			// the clipboard data should be in a list, so we extract that list
			switch (propertyCode)
			{
				case pClipboard:
					err = AEGetKeyDesc(appleEvent, keyAEData, typeAEList, &tokenData);
					ThrowIfOSErr(err);
					
					SetDataForObject(token, &tokenData);		// may throw

					err = AEPutKeyDesc(reply, keyDirectObject, &tokenData);	// return the requested data
					ThrowIfOSErr(err);
					break;
					
				default:
					ThrowIfOSErr(errAENotModifiable);  // "Can't set xxx to nnn"
					break;
			}
		}
		else
		{
			ThrowIfOSErr(errAENotModifiable);
		}
	}
	else
	{
		ThrowIfOSErr(errAEEventNotHandled);
	}
	
}
#endif

#pragma mark -

/*----------------------------------------------------------------------------
	CountObjects 
	
----------------------------------------------------------------------------*/
void AEApplicationClass::CountObjects(				DescType 		 	desiredType,
											DescType 		 	containerClass,
											const AEDesc *		container,
							   				long *			result)
{
	long		numberOfObjects = CountApplicationObjects(container, desiredType);
	*result = numberOfObjects;
}

#pragma mark -

/*----------------------------------------------------------------------------
	GetDataFromObject 
	
----------------------------------------------------------------------------*/
void AEApplicationClass::GetDataFromObject(const AEDesc *token, AEDesc *desiredTypes, AEDesc *data)
{
	OSErr					err	= noErr;
	
	Str255					applicationName = "\p";
	Str255					versionString;
	
	AETokenDesc				tokenDesc(token);
	
	ProcessSerialNumber		applicationProcessNumber;
	ProcessInfoRec				applicationInfo;
	FSSpec					appFSSpec;

	Boolean           				isFrontProcess 		= true;	// еее !gInBackground;
	
	DescType					aDescType		= cApplication;
	
	long						documentNumber	= 0L;
	unsigned long				elementNumber		= 0L;
		
	Boolean					usePropertyCode	= tokenDesc.UsePropertyCode();
	DescType					propertyCode;
	
	long 						free;
	long 						contiguous;
	unsigned long 				ticks;

	err = GetCurrentProcess(&applicationProcessNumber);	
	
	if (err == noErr)
	{
		applicationInfo.processInfoLength 	= sizeof(ProcessInfoRec);
		applicationInfo.processName 		= applicationName;
		applicationInfo.processAppSpec 	= &appFSSpec;
		err = GetProcessInformation(&applicationProcessNumber, &applicationInfo);
	}
				
	GetShortVersionString(2, versionString);
	PurgeSpace(&free, &contiguous);
				
	ticks = TickCount();

	propertyCode = tokenDesc.GetPropertyCode();
	
	switch (propertyCode)
	{
		case pProperties:
			err = AECreateList(nil, 0, true, data);
			ThrowIfOSErr(err);

			err = AEPutKeyPtr(data, pObjectType, 		typeType, 		&aDescType, sizeof(DescType));
			err = AEPutKeyPtr(data, pName,       		typeChar, 		&applicationName[1], applicationName[0]);
			err = AEPutKeyPtr(data, pVersion,    		typeChar, 		&versionString[1], versionString[0]);
			err = AEPutKeyPtr(data, pIsFrontProcess,	typeBoolean, 		&isFrontProcess, sizeof(Boolean));
			err = AEPutKeyPtr(data, pFreeMemory,		typeLongInteger, 	&free, sizeof(long));
			err = AEPutKeyPtr(data, pLargestFreeBlock,	typeLongInteger, 	&contiguous, sizeof(long));
			err = AEPutKeyPtr(data, pTicks,			typeLongInteger, 	&ticks, sizeof(long));
			break;
			
		case pBestType:
		case pClass:
		case pDefaultType:
		case pObjectType:
			err = AECreateDesc(typeType, &aDescType, sizeof(DescType), data);
			break;
			
		case pName:	
			err = AECreateDesc(typeChar, &applicationName[1], applicationName[0], data);
			break;

		case pVersion:
			err = AECreateDesc(typeChar, &versionString[1], versionString[0], data);
			break;

		case pIsFrontProcess:
			err = AECreateDesc(typeBoolean, &isFrontProcess, sizeof(Boolean), data);
			break;

		case pFreeMemory:
			err = AECreateDesc(typeLongInteger, &free, sizeof(long), data);
			break;
			
		case pLargestFreeBlock:
			err = AECreateDesc(typeLongInteger, &contiguous, sizeof(long), data);
			break;
			
		case pTicks:
			err = AECreateDesc(typeLongInteger, &ticks, sizeof(long), data);
			break;
			
		case pClipboard:
			{
				//	Return all of the items currently on the clipboard.
				//	The returned information is an AEList, and each data type
				//	on the scrap gets its own entry in the list
				//	Since the OS doesn't supply the tools for getting all
				//	of the types in the scrap, we have to scan the scrap ourselves

				char			*scrapPtr;
				char			*scrapEnd;
				PScrapStuff	 scrapInfo;
				OSType		 itemType;
				long			 itemLength;
				long			 index;
				
				err = AECreateList(NULL, 0, false, data);
				ThrowIfOSErr(err);
				
				err = LoadScrap();										//	Make sure the scrap is in memory, not on disk.
				ThrowIfOSErr(err);
				
				scrapInfo = InfoScrap();								//	Get the base address of the scrap in RAM
				MoveHHi(scrapInfo->scrapHandle);
				HLock  (scrapInfo->scrapHandle);						// ...and lock it
				
				scrapPtr = (char *)*scrapInfo->scrapHandle;
				scrapEnd = scrapPtr + scrapInfo->scrapSize;
				
				// scan the scrap in memory and extract each scrap type
				
				index = 1;
				while (scrapPtr < scrapEnd) 
				{
					itemType = *(OSType *)scrapPtr;
					scrapPtr += sizeof(itemType);
					itemLength = *(long *)scrapPtr;
					scrapPtr += sizeof(itemLength);
					
					// Move this information into the next entry on the list
					err = AEPutPtr(data, index, itemType, scrapPtr, itemLength);
					ThrowIfOSErr(err);
						
					index++;
					
					// Adjust the pointer to the start of the next item
					
					if (itemLength & 1) 
						itemLength++; 										// If it's odd, make it even
						
					scrapPtr += itemLength;
				}
				HUnlock  (scrapInfo->scrapHandle);
			} 
			break;

		default:
			Inherited::GetDataFromObject(token, desiredTypes, data);
			break;
	}

	ThrowIfOSErr(err);
}

/*----------------------------------------------------------------------------
	SetDataForObject 
	
	Assumption: HandleSetData() has already filtered out all attempts
	to write to a read-only property.
	
----------------------------------------------------------------------------*/
void AEApplicationClass::SetDataForObject(const AEDesc *token, AEDesc *data)
{
	OSErr				err			= noErr;

	long					numItems;
	long					index;
	AEKeyword 			theAEKeyword;
	
	AETokenDesc			tokenDesc(token);
	Boolean				usePropertyCode     = tokenDesc.UsePropertyCode();
	DescType				propertyCode;
	
	if (usePropertyCode)
	{
		propertyCode = tokenDesc.GetPropertyCode();
		
		switch (propertyCode)
		{
			// the clipboard is the only writeable property for the application object
			case pClipboard:
				//	The data should be an AE list containing a series of things to be placed on the
				//	clipboard. The data type of each item is also the clipboard type for that data
				
				err = ZeroScrap();
				ThrowIfOSErr(err);
				
				AECountItems(data, &numItems);
				
				//  Copy each item onto the clipboard
				
				for (index = 1; index <= numItems; index++) 
				{
					StAEDesc		currentItemDesc;
					err = AEGetNthDesc(data, index, typeWildCard, &theAEKeyword, &currentItemDesc);
					ThrowIfOSErr(err);
						
					HLock(currentItemDesc.dataHandle);
					err = PutScrap(GetHandleSize(currentItemDesc.dataHandle), 
										  currentItemDesc.descriptorType,
								   	 	*currentItemDesc.dataHandle);
					ThrowIfOSErr(err);
				}
				break;
				
			default:
				ThrowIfOSErr(errAENotModifiable);
		}
	}
	
}


/*----------------------------------------------------------------------------
	GetKeyEventDataAs 

----------------------------------------------------------------------------*/
DescType AEApplicationClass::GetKeyEventDataAs(DescType propertyCode)
{
	DescType		returnType;

	switch (propertyCode)
	{
		case pClipboard:
			returnType = typeAEList;
			break;
			
		default:
			returnType = typeWildCard;
	
	}
	return returnType;
}


#pragma mark -

/*----------------------------------------------------------------------------
	CanSetProperty 
		
----------------------------------------------------------------------------*/
Boolean AEApplicationClass::CanSetProperty(DescType propertyCode)
{
	Boolean result = false;
	
	switch (propertyCode)
	{
		// Properties we can set:
		
		case pClipboard:
			result = true;
			break;
			
		// Properties we should be able to set, but they're not implemented yet:
		
					
		// Properties that are read-only

		case pBestType:
		case pClass:
		case pDefaultType:
		case pObjectType:
		
		case pProperties:
		
		case pFreeMemory:
		case pLargestFreeBlock:
		case pTicks:

		case pIsFrontProcess:
		case pName:
		case pVersion:
		case pInsertionLoc:
		case pSelection:
		case pUserSelection:
			result = false;
			break;
		
		default:
			result = Inherited::CanSetProperty(propertyCode);
			break;
	}
	
	return result;
}


/*----------------------------------------------------------------------------
	CanGetProperty 
	
----------------------------------------------------------------------------*/
Boolean AEApplicationClass::CanGetProperty(DescType propertyCode)
{
	Boolean result = false;
	
	switch (propertyCode)
	{
		// Properties we can get:

		case pBestType:
		case pClass:
		case pDefaultType:
		case pObjectType:
			
		case pProperties:
		
		case pFreeMemory:
		case pLargestFreeBlock:
		case pTicks:

		case pIsFrontProcess:
		case pName:
		case pVersion:
		case pInsertionLoc:
		case pSelection:
		case pUserSelection:
			result = true;
			break;
			
		// Properties we should be able to get, but they're not implemented yet:
					
		// Properties we should not be able to get:
		
		default:
			result = Inherited::CanGetProperty(propertyCode);
			break;
	}
	
	return result;
}


#pragma mark -

/*----------------------------------------------------------------------------
	CreateSelfSpecifier 

----------------------------------------------------------------------------*/
void AEApplicationClass::CreateSelfSpecifier(const AEDesc *token, AEDesc *outSpecifier)
{
	OSErr	err = ::AECreateDesc(typeNull, nil, 0, outSpecifier);
	ThrowIfOSErr(err);
}

#pragma mark -

/*----------------------------------------------------------------------------
	CountApplicationObjects 
	
----------------------------------------------------------------------------*/
long AEApplicationClass::CountApplicationObjects(const AEDesc *token, DescType desiredType)
{
	long		numberOfObjects = 0;
	OSErr	err = noErr;
	
	if (AEListUtils::TokenContainsTokenList(token))
	{
		err = AECountItems(token, &numberOfObjects);
	}
	else
	{
		AEDispatchHandler*	countHandler = AECoreClass::sAECoreHandler->GetDispatchHandler(desiredType);
		if (countHandler == nil)
			ThrowOSErr(errAEEventNotHandled);
		
		countHandler->CountObjects(desiredType, typeNull, token, &numberOfObjects);
		/*
		switch (desiredType)
		{
			case cDocument:
				numberOfObjects = AEDocumentClass::CountDocuments();
				break;

			case cWindow:
				numberOfObjects = AEWindowClass::CountWindows(kAnyWindowKind);
				break;
			
			// application specific classes
			case cGroupWindow:
				numberOfObjects = AEWindowClass::CountWindows(kUserGroupWindowKind);
				break;
			
			
			default:
				err = errAEEventNotHandled;
				break;
		}
		*/
	}

	ThrowIfOSErr(err);
	return numberOfObjects;
}

