/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla 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/MPL/
 *
 * 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 the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Simon Fraser <sfraser@netscape.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsAEUtils.h"
#include "nsAETokens.h"
#include "nsAECoreClass.h"
#include "nsAEApplicationClass.h"

#include "nsAEDocumentClass.h"


/*----------------------------------------------------------------------------
	AEDocumentClass 
	
----------------------------------------------------------------------------*/
AEDocumentClass::AEDocumentClass()
:	AEGenericClass(cDocument, typeNull)
{
}


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

#pragma mark -

/*----------------------------------------------------------------------------
	PropertyFromApplicationAccessor 
	
----------------------------------------------------------------------------*/
pascal OSErr AEDocumentClass::PropertyAccessor(		DescType			desiredClass,
											const AEDesc*		containerToken,
											DescType			containerClass,
											DescType			keyForm,
											const AEDesc*		keyData,
											AEDesc*			resultToken,
											long 				refCon)
{
	AEDocumentClass*	docClass = reinterpret_cast<AEDocumentClass *>(refCon);
	if (!docClass) return paramErr;
	
	OSErr		err = noErr;
	
	try
	{
		docClass->GetProperty(desiredClass, containerToken, containerClass, keyForm, keyData, resultToken);
	}
	catch(OSErr catchErr)
	{
		err = catchErr;
	}
	catch(...)
	{
		err = paramErr;
	}
	
	return err;
}

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

/*----------------------------------------------------------------------------
	GetDocumentFromApp 
	
----------------------------------------------------------------------------*/
void AEDocumentClass::GetDocumentFromApp(		DescType			desiredClass,		// cDocument
										const AEDesc*		containerToken,	// null container
										DescType			containerClass,  	 // cApplication
										DescType			keyForm,
										const AEDesc*		keyData,
										AEDesc*			resultToken)		// specified Document is returned in result
{
	OSErr				err			= noErr;
	DescType				keyDataType 	= keyData->descriptorType;
	long					index;
	long					numItems;
	Boolean				wantsAllItems	= false;
	StAEDesc 				startObject;	// These are used to resolve formRange
	StAEDesc 				stopObject;
	DocumentReference		document;
	Str63				documentName;
	CoreTokenRecord 		token;
	
	numItems = CountDocuments();

	switch (keyForm) 
	{
		case formName:											// Document by name
			{
				if (DescToPString(keyData, documentName, 63) != noErr)
				{
					err = errAECoercionFail;
				}
				else
				{
					document = GetDocumentByName(documentName);
					if (document == nil)
						err = errAENoSuchObject;
				}
			}
			break;
		
		case formAbsolutePosition:									// Document by number
			err = NormalizeAbsoluteIndex(keyData, &index, numItems, &wantsAllItems);
			if ((err == noErr) && (wantsAllItems == false))
			{
				document = GetDocumentByIndex(index);
				if (document == nil)
					err = errAEIllegalIndex;
			}
			break;	
		
		case formRelativePosition:
			ProcessFormRelativePostition(containerToken, keyData, &document);
			break;	
		
		case formRange:
			switch (keyDataType)
			{		
				case typeRangeDescriptor:
					err = ProcessFormRange((AEDesc *)keyData, &startObject, &stopObject);
					if (err == noErr)
					{
						ConstAETokenDesc	startTokenDesc(&startObject);
						ConstAETokenDesc	stopTokenDesc(&startObject);
						
						DescType startType = startTokenDesc.GetDispatchClass();
						DescType stopType  = stopTokenDesc.GetDispatchClass();
	 
						if (startType != cDocument || stopType != cDocument)
							err = errAEWrongDataType;
					}
					break;

				default:
					err = errAEWrongDataType;
					break;	
			}
			break;	

		default:
			err = errAEEventNotHandled;
			break;
	}
	
	// if user asked for all items, and there aren't any,
	// we'll be kind and return an empty list.

	if (wantsAllItems && (err == errAENoSuchObject || err == errAEIllegalIndex))
	{
		err = AECreateList(NULL, 0, false, (AEDescList*)resultToken);
		ThrowIfOSErr(err);
		return;
	}

	ThrowIfOSErr(err);
	
	// fill in the result token

	token.dispatchClass  	= GetClass();
	token.objectClass	= GetClass();
	token.propertyCode 	= typeNull;

	if (wantsAllItems)
	{						
		err = AECreateList(NULL, 0, false, (AEDescList*)resultToken);
		
		if (err == noErr)
		{
			for (index = 1; index <= numItems; index++)
			{
				document = GetDocumentByIndex(index);
				ThrowIfOSErr(errAEEventNotHandled);
				
				token.documentID = GetDocumentID(document);
				
				err = AEPutPtr(resultToken, 0, desiredClass, &token, sizeof(token));
				ThrowIfOSErr(err);
			}
		}
	}
	else if (keyForm == formRange)
	{			
		DocumentReference	beginDocument;
		DocumentReference	endDocument;
		long				beginIndex;
		long				endIndex;
		
		beginDocument = GetDocumentReferenceFromToken(&startObject);
		beginIndex = GetDocumentIndex(beginDocument);

		endDocument = GetDocumentReferenceFromToken(&stopObject);
		endIndex = GetDocumentIndex(endDocument);
								
		err = AECreateList(NULL, 0, false, (AEDescList*)resultToken);
		ThrowIfOSErr(err);
			
		if (beginIndex > endIndex) // swap elements
		{
			DocumentReference temp;
			temp			= beginDocument;
			beginDocument	= endDocument;
			endDocument	= temp;
		}
		
		document = beginDocument;
		while (document != nil)
		{
			token.documentID = GetDocumentID(document);
			
			err = AEPutPtr(resultToken, 0, desiredClass, &token, sizeof(token));
			ThrowIfOSErr(err);

			if (document == endDocument)
				break;
			document = GetNextDocument(document);
		}
	}
	else
	{
		token.documentID = GetDocumentID(document);
		err = AECreateDesc(desiredClass, &token, sizeof(token), resultToken);
	}

	ThrowIfOSErr(err);		
}


/*----------------------------------------------------------------------------
	DocumentAccessor 
	
	Document from null accessor
----------------------------------------------------------------------------*/
pascal OSErr AEDocumentClass::DocumentAccessor(		DescType			desiredClass,		// cDocument
											const AEDesc*		containerToken,	// null container
											DescType			containerClass,  	 // cApplication
											DescType			keyForm,
											const AEDesc*		keyData,
											AEDesc*			resultToken,		// specified Document is returned in result
											long 				refCon)
{
	AEDocumentClass*		docClass = reinterpret_cast<AEDocumentClass *>(refCon);
	if (!docClass) return paramErr;
	
	OSErr		err = noErr;
	
	try
	{
		docClass->GetDocumentFromApp(desiredClass, containerToken, containerClass, keyForm, keyData, resultToken);
	}
	catch(OSErr catchErr)
	{
		err = catchErr;
	}
	catch(...)
	{
		err = paramErr;
	}
	
	return err;
}

											


#pragma mark -

/*----------------------------------------------------------------------------
	ProcessFormRelativePostition 
	
----------------------------------------------------------------------------*/
void AEDocumentClass::ProcessFormRelativePostition(const AEDesc* anchorToken, const AEDesc *keyData, DocumentReference *document)
{
	ConstAETokenDesc		tokenDesc(anchorToken);
	OSErr			err = noErr;
	DescType 			positionEnum;
	DocumentReference	anchorDocument;
	DocumentReference	relativeDocument = nil;
	
	*document = nil;
	
	anchorDocument = GetDocumentReferenceFromToken(anchorToken);

	if (err == noErr)
	{
	
		switch (keyData->descriptorType)
		{
		   case typeEnumerated:
				if (DescToDescType((AEDesc*)keyData, &positionEnum) != noErr)
				{
					err = errAECoercionFail;
				}
				else
				{
					switch (positionEnum)
					{
						case kAENext:						// get the document behind the anchor
							*document = GetPreviousDocument(anchorDocument);
							if (*document == nil)
								err = errAENoSuchObject;
							break;
							
						case kAEPrevious:					// get the document in front of the anchor
							*document = GetNextDocument(anchorDocument);
							if (*document == nil)
								err = errAENoSuchObject;
							break;
							
						default:
							err = errAEEventNotHandled;
							break;
					}
				}
				break;
				
			default:
				err = errAECoercionFail;
				break;
		}
	}
	
	ThrowIfOSErr(err);
}

#pragma mark -

/*----------------------------------------------------------------------------
	CanGetProperty 
	
----------------------------------------------------------------------------*/
Boolean AEDocumentClass::CanGetProperty(DescType property)
{
	Boolean	result = false;
	
	switch (property)
	{
		// Properties we can get:
		
		case pBestType:
		case pClass:
		case pDefaultType:
		case pObjectType:
		
		case pName:
		case pProperties:
		case pIsModified:
			result = true;
			break;
			
		// Properties we can't get:
		default:
			result = Inherited::CanGetProperty(property);
			break;
	}

	return result;
}


/*----------------------------------------------------------------------------
	CanSetProperty 
	
----------------------------------------------------------------------------*/
Boolean AEDocumentClass::CanSetProperty(DescType property)
{
	Boolean	result = false;
	
	switch (property)
	{
		// Properties we can set:
		
		case pName:
			result = true;
			break;
			
		// Properties we can't set:

		case pBestType:
		case pClass:
		case pDefaultType:
		case pObjectType:
		
		case pProperties:
		case pIsModified:
			result = false;
			break;
			
		default:
			result = Inherited::CanSetProperty(property);
			break;
	}

	return result;
}


#pragma mark -


/*----------------------------------------------------------------------------
	GetDataFromObject 
	
----------------------------------------------------------------------------*/
void AEDocumentClass::GetDataFromObject(const AEDesc *token, AEDesc *desiredTypes, AEDesc *data)
{
	OSErr			err				= noErr;
	Boolean			usePropertyCode	= false;	
	DocumentReference	document 			= nil;
	ConstAETokenDesc		tokenDesc(token);
	DescType			aType			= cDocument;
	Str63 			documentName;
	Boolean 			isModified;
	
	// ugh
	document = GetDocumentReferenceFromToken(token);
	ThrowIfOSErr(err);
	if (document == nil)
		ThrowIfOSErr(paramErr);

	GetDocumentName(document, documentName);						
	isModified = DocumentIsModified(document);						

	usePropertyCode = tokenDesc.UsePropertyCode();
	
	DescType propertyCode = tokenDesc.GetPropertyCode();
	
	switch (propertyCode)
	{
		case pProperties:
			err = AECreateList(NULL, 0L, true, data);
			ThrowIfOSErr(err);

			err = AEPutKeyPtr(data, pObjectType,	typeType, 	&aType, 		 	sizeof(DescType));
			err = AEPutKeyPtr(data, pName, 		typeChar, 	&documentName[1], 	documentName[0]);
			err = AEPutKeyPtr(data, pIsModified, 	typeBoolean, 	&isModified, 		sizeof(Boolean));
			break;
			
		case pBestType:
		case pClass:
		case pDefaultType:
		case pObjectType:
			err = AECreateDesc(typeType, &aType, sizeof(DescType), data);
			break;
			
		case pName:
			err = AECreateDesc(typeChar, &documentName[1], documentName[0], data);
			break;
			
		case pIsModified:
			err = AECreateDesc(typeBoolean, &isModified, sizeof(Boolean), data);
			break;
			
		default:
			Inherited::GetDataFromObject(token, desiredTypes, data);
			break;
	}
	
	ThrowIfOSErr(err);
}

/*----------------------------------------------------------------------------
	SetDataForObject 
	
----------------------------------------------------------------------------*/
void AEDocumentClass::SetDataForObject(const AEDesc *token, AEDesc *data)
{
	OSErr				err = noErr;		
	Boolean				usePropertyCode;
	DescType				propertyCode;
	DocumentReference 		document = nil;
	ConstAETokenDesc			tokenDesc(token);
	StAEDesc 				propertyRecord;

	usePropertyCode = tokenDesc.UsePropertyCode();
	document = GetDocumentReferenceFromToken(token);
		
	if (usePropertyCode == false)
	{
		err = errAEWriteDenied;
	}
	else
	{
		propertyCode = tokenDesc.GetPropertyCode();
		
		if (data->descriptorType == typeAERecord)
		{		
			SetDocumentProperties(document, data);
		}
		else	// Build a record with one property
		{
			err = AECreateList(NULL, 0L, true, &propertyRecord);
			ThrowIfOSErr(err);
			
			err = AEPutKeyDesc(&propertyRecord, propertyCode, data);
			ThrowIfOSErr(err);
		
			SetDocumentProperties(document, &propertyRecord);
		}
	}

	ThrowIfOSErr(err);
}


/*----------------------------------------------------------------------------
	SetDocumentProperties 
	
----------------------------------------------------------------------------*/
void	AEDocumentClass::SetDocumentProperties(DocumentReference document, AEDesc *propertyRecord)
{

}


/*----------------------------------------------------------------------------
	CountDocuments 
	
----------------------------------------------------------------------------*/
long AEDocumentClass::CountDocuments()
{
	return 0;
}


/*----------------------------------------------------------------------------
	GetDocumentByName 
	
----------------------------------------------------------------------------*/
DocumentReference  AEDocumentClass::GetDocumentByName(ConstStr255Param docName)
{
	return nil;
}

/*----------------------------------------------------------------------------
	GetDocumentByIndex 
	
----------------------------------------------------------------------------*/
DocumentReference  AEDocumentClass::GetDocumentByIndex(long index)
{
	return nil;
}

/*----------------------------------------------------------------------------
	GetDocumentByID 
	
----------------------------------------------------------------------------*/
DocumentReference AEDocumentClass::GetDocumentByID(long docID)
{
	return nil;
}

/*----------------------------------------------------------------------------
	GetNextDocument 
	
----------------------------------------------------------------------------*/
DocumentReference AEDocumentClass::GetNextDocument(DocumentReference docRef)
{
	return nil;
}


/*----------------------------------------------------------------------------
	GetPreviousDocument 
	
----------------------------------------------------------------------------*/
DocumentReference AEDocumentClass::GetPreviousDocument(DocumentReference docRef)
{
	return nil;
}


/*----------------------------------------------------------------------------
	GetDocumentReferenceFromToken 
	
----------------------------------------------------------------------------*/
DocumentReference AEDocumentClass::GetDocumentReferenceFromToken(const AEDesc *token)
{
	ConstAETokenDesc	tokenDesc(token);
	long			docID = tokenDesc.GetDocumentID();
	
	return GetDocumentByID(docID);
}

/*----------------------------------------------------------------------------
	CloseWindowSaving 
	
----------------------------------------------------------------------------*/
void AEDocumentClass::CloseWindowSaving(AEDesc *token, const AEDesc *saving, AEDesc *savingIn)
{
	OSErr			err			= noErr;
	DocumentReference	document;

	document = GetDocumentReferenceFromToken(token);
	
	if (document != nil)
	{
		// DestroyDocument(document);
	}
	else
		err = errAEEventNotHandled;
	
	ThrowIfOSErr(err);
}

#pragma mark -

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

----------------------------------------------------------------------------*/
void AEDocumentClass::CreateSelfSpecifier(const AEDesc *token, AEDesc *outSpecifier)
{
	ThrowIfOSErr(errAENoSuchObject);
}

#pragma mark -

/*----------------------------------------------------------------------------
	GetDocumentID 
	
----------------------------------------------------------------------------*/
long GetDocumentID(DocumentReference docRef)
{
	return 0;
}

	
/*----------------------------------------------------------------------------
	GetDocumentIndex 
	
----------------------------------------------------------------------------*/
long GetDocumentIndex(DocumentReference docRef)
{
	return 0;
}

/*----------------------------------------------------------------------------
	GetDocumentIndex 
	
----------------------------------------------------------------------------*/
void	GetDocumentName(DocumentReference docRef, Str63 docName)
{
	docName[0] = 0;
}


/*----------------------------------------------------------------------------
	GetDocumentIndex 
	
----------------------------------------------------------------------------*/
Boolean DocumentIsModified(DocumentReference docRef)
{
	return false;
}

