#ifndef __GNUC__
	#include <StandardFile.h>
#endif

#include "O.h"
#include "IC_FileIO.h"
#include "GSOS_Icons.h"
#include "MemUtils.h"
#include "IC_ImageTypes.h"
#include "Open_Save.h"
#include "ADFS_Prefs.h"
#include "DSGlobals.h"
#include "ADFS_LogFile.h"
#include "ADFS_Menus.h"
#include "ProFileTypes.h"
#include "BasicTokenizer.h"

#include "DSUserProcs.h"

// Static Prototypes
static OSErr ProcessFolder(FSSpecPtr myFSSPtr);


/*
	Uncomment this line if you want each item of a dropped folder processed
	as an individual item
*/
// #define qWalkFolders


/*
	This routine is called during init time.
	
	It allows you to install more AEVT Handlers beyond the standard four
*/
#pragma segment Main
void InstallOtherEvents (void) {
}


/*	
	This routine is called when an OAPP event is received.
	
	Currently, all it does is set the gOApped flag, so you know that
	you were called initally with no docs, and therefore you shouldn't 
	quit when done processing any following odocs.
*/
#pragma segment Main
void OpenApp (void) {
	gOApped = true;
}


/*	
	This routine is called when an QUIT event is received.
	
	We simply set the global done flag so that the main event loop can
	gracefully exit.  We DO NOT call ExitToShell for two reasons:
	1) It is a pretty ugly thing to do, but more importantly
	2) The Apple event manager will get REAL upset!
*/
#pragma segment Main
OSErr QuitApp (void)
{
	OSErr		err = noErr;
	
	if (IsModalUp()) {
		SysBeep(1);
		err = 1;
	} else {
		char		bufAC[64];
		
		gDone = TRUE;
		ADFS_Log(GetCommandString(cmdQuit, bufAC));
	}
	
	return err;
}


/*	
	This routine is the first one called when an ODOC or PDOC event is received.
	
	In this routine you would place code used to setup structures, etc. 
	which would be used in a 'for all docs' situation (like "Archive all
	dropped files")

	Obviously, the opening boolean tells you whether you should be opening
	or printing these files based on the type of event recieved.
	
	NEW IN 2.0!
	The itemCount parameter is simply the number of items that were dropped on
	the application and that you will be processing.  This gives you the ability
	to do a single preflight for memory allocation needs, rather than doing it
	once for each item as in previous versions.
	
	userDataHandle is a handle that you can create & use to store your own
	data structs.  This dataHandle will be passed around to the other 
	odoc/pdoc routines so that you can get at your data without using
	globals - just like the new StandardFile.  
	
	We also return a boolean to tell the caller if you support this type
	of event.  By default, our dropboxes don't support the pdoc, so when
	opening is FALSE, we return FALSE to let the caller send back the
	proper error code to the AEManager.

	You will probably want to remove the #pragma unused (currently there to fool the compiler!)
*/
#pragma segment Main
Boolean PreFlightDocs (Boolean opening, short itemCount, Handle *userDataHandle) {
#pragma unused ( itemCount )
#pragma unused ( userDataHandle )

	return opening;		// we support opening, but not printing - see above
}


/*	
	This routine is called for each file passed in the ODOC event.
	
	In this routine you would place code for processing each file/folder/disk that
	was dropped on top of you.
	
	You will probably want to remove the #pragma unused (currently there to fool the compiler!)
*/
#pragma segment Main
void OpenDoc ( FSSpecPtr myFSSPtr, Boolean opening, Handle userDataHandle ) {
#pragma unused ( myFSSPtr )
#pragma unused ( opening )
#pragma unused ( userDataHandle )
	OSErr	err = noErr;
	
	
	#ifdef qWalkFolders
	/*
		For this case we need to determine if the FSSpec is a file or folder.
		If it's a folder, we then need to process each item in that folder,
		otherwise just process the item.
	*/
	if (FSpIsFolder2(myFSSPtr))
		err = ProcessFolder(myFSSPtr);
	else
		err = ProcessItem(myFSSPtr);
	#else
	/*
		For this case we just call ProcessItem on the FSSpec above.
	*/
	err = ProcessItem(myFSSPtr);
	#endif
	
	// you should probably do something if you get back an error ;)
}


/*	
	This routine is the last routine called as part of an ODOC event.
	
	In this routine you would place code to process any structures, etc. 
	that you setup in the PreflightDocs routine.

	NEW IN 2.0!
	The itemCount parameter was the number of items that you processed.
	It is passed here just in case you need it ;)  
	
	If you created a userDataHandle in the PreFlightDocs routines, this is
	the place to dispose of it since the Shell will NOT do it for you!
	
	You will probably want to remove the #pragma unusued (currently there to fool the compiler!)
*/
#pragma segment Main
void PostFlightDocs ( Boolean opening, short itemCount, Handle userDataHandle ) {
#pragma unused ( opening )
#pragma unused ( itemCount )
#pragma unused ( userDataHandle )

	if ( (opening) && (!gOApped) ) {
		//	gDone = true;	//	close everything up!
	}
	/*
		The reason we do not auto quit is based on a recommendation in the
		Apple event Registry which specifically states that you should NOT
		quit on a 'pdoc' as the Finder will send you a 'quit' when it is 
		ready for you to do so.
	*/
}


#ifdef nodef
/*
	This routine gets called for each item (which could be either a file or a folder)
	that the caller wants dropped.  The determining factor is the definition of the 
	qWalkFolder compiler directive.   Either way, the item in question should be
	processed as a single item and not "dissected" into component units (like subfiles
	of a folder!)
*/
OSErr ProcessItem(FSSpecPtr myFSSPtr)
{
	OSErr	err = noErr;
	
	// do something here
	
	return(err);
}
#endif

/*
	This routine gets called for any folder (or disk) that the caller wants 
	processed as a set of component items, instead of as a single entity.
	The determining factor is the definition of the qWalkFolder compiler directive.
*/
static OSErr ProcessFolder(FSSpecPtr myFSSPtr)
{
	OSErr		err = noErr;
	short		index, oldIndex, localIndex;
	FSSpec		localFSSpec, curFSSpec;
	CInfoPBRec	cipb;
	Str255		fName, vFName;
	long		dirID, origDirID;
	Boolean		foundPosition;
 
 	// copy the source locally to avoid recursion problems
 	BlockMoveData(myFSSPtr, &localFSSpec, sizeof(FSSpec));
 	
	//	get the dirID for THIS folder, not it's parent!
	BlockMoveData(localFSSpec.name, fName, 32);
	
	cipb.hFileInfo.ioCompletion	= 0L;
	cipb.hFileInfo.ioNamePtr	= fName;
	cipb.hFileInfo.ioVRefNum	= localFSSpec.vRefNum;
	cipb.hFileInfo.ioFDirIndex	= 0;	// use the dir & vRefNum;
	cipb.hFileInfo.ioDirID		= localFSSpec.parID;
	err = PBGetCatInfoSync(&cipb);
	
	if (!err) {		
		origDirID = cipb.dirInfo.ioDrDirID; // copy the sucker
		index = 1;
				
		// index through all contents of this folder
		while (err == noErr) {
			dirID = origDirID;
			localIndex = index;
			fName [0] = 0;
			cipb.hFileInfo.ioCompletion	= 0L;
			cipb.hFileInfo.ioNamePtr	= fName;
			cipb.hFileInfo.ioVRefNum	= localFSSpec.vRefNum;
			cipb.hFileInfo.ioFDirIndex	= localIndex;	// use a real index
			cipb.hFileInfo.ioDirID		= dirID;
			err = PBGetCatInfoSync(&cipb);

			if (!err) {
				BlockMoveData(fName, curFSSpec.name, 32);
				curFSSpec.vRefNum	= cipb.hFileInfo.ioVRefNum;
				curFSSpec.parID		= dirID;
			
				/*	
					Check to see if this entry is a folder.
				*/
				if (cipb.hFileInfo.ioFlAttrib & ioDirMask) {
					err = ProcessFolder(&curFSSpec);
				 } else
					err = ProcessItem(&curFSSpec);
			
				/*	If we've had an error, get out! */
				if (err)	break;

				// dirID = origDirID;	
				localIndex = index;	

				/*	
					Now take into account new files being created
					in the current directory & messing up our index.
					See Dev.CD Vol. XI:Tools & Apps (Moof!):Misc Utilities:
					Disinfectant & Source 2.5.1:Sample:Notes:Scan Alg	
				*/
				vFName [0] = 0;
				cipb.hFileInfo.ioCompletion	= 0L;
				cipb.hFileInfo.ioNamePtr	= vFName;
				cipb.hFileInfo.ioVRefNum	= localFSSpec.vRefNum;
				cipb.hFileInfo.ioFDirIndex	= localIndex;	// use a real index
				cipb.hFileInfo.ioDirID		= dirID;
				err = PBGetCatInfoSync(&cipb);
				oldIndex = index;
				if (!err) {
					/*	If they're equal - same place, go to next */
					if (EqualString (vFName, fName, false, false))
						index++;
				}
				
				/*	If we didn't advance, then perhaps a file was created or deleted */
				if (oldIndex == index) {
					oldIndex		= index;	/* save off the old */
					index			= 0;		/* and start at the beginning */
					err				= noErr;
					vFName [0]		= 0;
					foundPosition	= false;
					
					while (!foundPosition) {
						index++;
						vFName [0] = 0;
						cipb.hFileInfo.ioCompletion	= 0L;
						cipb.hFileInfo.ioNamePtr	= vFName;
						cipb.hFileInfo.ioVRefNum	= localFSSpec.vRefNum;
						cipb.hFileInfo.ioFDirIndex	= index;	/* now use a real index */
						cipb.hFileInfo.ioDirID		= dirID;
						err = PBGetCatInfoSync(&cipb);
						
						if (err == fnfErr) {  // we've just been deleted
							index = oldIndex;
							foundPosition = true;
							err = noErr;	// have to remember to reset this!
						}
						
					/*	found same file & same index position */
					/*	so try the next item */
						if ((!foundPosition) && EqualString(fName, vFName, false, false)) {
							index++;
							foundPosition = true;
						}
					}
				}
			}
		}
	}
	
	return(err);
}

/*
	This routine is called when the user chooses "Select FileÉ" from the
	File Menu.
	
	Currently it simply calls the new StandardGetFile routine to have the
	user select a single file (any type, numTypes = -1) and then calls the
	SendODOCToSelf routine in order to process it.  
			
	The reason we send an odoc to ourselves is two fold: 1) it keeps the code
	cleaner as all file openings go through the same process, and 2) if events
	are ever recordable, the right things happen (this is called Factoring!)

	Modification of this routine to only select certain types of files, selection
	of multiple files, and/or handling of folder & disk selection is left 
	as an exercise to the reader.
*/

void	FillInTypeList(OSType *theTypeList, short *numTypes);

static	Boolean		IsDiskImageFile(
	StringPtr			fileName, 
	unsigned long		fileSize, 
	OSType				fileType)
{
	Boolean		goodTypeB = FALSE;
	OSType		typeList[IC_FileDescIndex_NUMTYPES];
	short		curType, numTypes;
	
	FillInTypeList(typeList, &numTypes);
	
	for (curType = 0; curType < numTypes; curType++) {
		if (typeList[curType] == fileType) {
			goodTypeB = TRUE;
		}
	}
	
	return goodTypeB;
	
/*	(
		fileType == a2pixType
		|| fileType == a2pixType2
		|| fileType == a2pixType3
	) && (
		!HAS_AUX(fileName)
	) && (
		IS_SINGLE_SIZE(fileSize)
		|| IS_DOUBLE_SIZE(fileSize)
	);
*/
}

void SelectFile (void)
{
	FSSpec				docFSSpec;
	
	if (GetFileOrFolder(
		Nav_FileSelect_SPECIFIC_FILE, 
		c2p("Please select a disk image:"), 
		&docFSSpec, IsDiskImageFile)
	) {
		SendODOCToSelf(&docFSSpec);	// so send me an event!
	}
}

#if 0
#define				kResultSize		32
static	void		PrintPlistStuff(ulong value)
{
	Byte			resultAC[kResultSize];
	Byte			bytesAC[4];
	CFStringRef 	cfStringRef;
	CFIndex			resultSize, byteIndex;
	char  		 	finalBuf[256];
	char    		tempBuf[256];
	
	*(long *)&bytesAC = value;

	cfStringRef = CFStringCreateWithBytes(
		NULL, bytesAC, 4, kCFStringEncodingMacRoman, FALSE);

	(void)CFStringGetBytes(
		cfStringRef, CFRangeMake(0, CFStringGetLength(cfStringRef)),
		kCFStringEncodingUTF8, 0, FALSE, resultAC, kResultSize,
		&resultSize);

	CFRelease(cfStringRef);
	
	sprintf(finalBuf, "0x%lX:\n", value);
	ADFS_Log(finalBuf);

	finalBuf[0] = 0;
	strcat(finalBuf, "<string>");

	for (byteIndex = 0; byteIndex < resultSize; byteIndex++) {
	    sprintf(tempBuf, "&#x%.2hX;", resultAC[byteIndex]);
	    strcat(finalBuf, tempBuf);
	}

	strcat(finalBuf, "</string>\n\n");
	ADFS_Log(finalBuf);
}

#endif

static	Boolean		IsMinimumSystem(void)
{
	OSErr		err;
	long		result;
	Boolean		isMinSysB = FALSE;

	err = Gestalt(gestaltDragMgrAttr, &result);
	
	switch (err) {
		
		case noErr: {
			isMinSysB = (result & (1 << gestaltDragMgrPresent)) != 0;
			
			ADFS_Log("Gestalt worked\n");
			if (!isMinSysB) {
				ReportErrorStr(-1, "Buddy, you ain't got no drag manager.");
			}
			break;
		}
		
		case gestaltUnknownErr: {
			ReportErrorStr(-1, "No response from Gestalt.");
			break;
		}
		
		case gestaltUndefSelectorErr: {
			ReportErrorStr(-1, "Buddy, you ain't got no drag manager.");
			break;
		}
		
		default: {
			ReportErrorStr(err, "Gestalt returned an error");
			break;
		}
	}
	
	return isMinSysB;
}

/*
	This routine is called during the program's initialization and gives you
	a chance to allocate or initialize any of your own globals that your
	dropbox needs.
	
	You return a boolean value which determines if you were successful.
	Returning false will cause DropShell to exit immediately.
*/
Boolean		CDesktop_IDesktop(void);

Boolean		InitDSUserGlobals(void)
{
	Boolean		okay;
	
	ADFS_Log("Birthing\n");

	okay = O_PreAllocate() == noErr;
	
	if (okay) {
		ADFS_Log("menus\n");
		SetUpMenus();
		
		ADFS_Log("fonts\n");
		(void)GetAppFonts(40);
	}

	if (okay) {
		ADFS_Log("checking system requirements\n");
		okay = IsMinimumSystem();
	}
	
	if (okay) {
		ADFS_Log("tokenizer\n");
		okay = SetUpTokenizer();
	}

	if (okay) {
		ADFS_Log("prefs\n");
		okay = LoadPrefs() == noErr;
	}
	
	if (okay) {
		ADFS_Log("filetypes\n");
		okay = Pro_InitFileTypes();
	}

/*
	if (okay) {
		ADFS_OpenLogFile();
		PrintPlistStuff(0x70062000);
		PrintPlistStuff(0x70064000);
		PrintPlistStuff(0x70F70118);
		PrintPlistStuff(0x70F70000);
	}
*/
	if (okay) {
		extern	FILE	*gLogfile;
		
		if (gLogfile) {
			(**gPrefsH).loggingB = TRUE;
			SavePrefs();
		} else {
			if ((**gPrefsH).loggingB) {
				ADFS_OpenLogFile();
			}
		}
	}
	
	if (okay) {
		StartTracking();
		okay = CDesktop_IDesktop();
	}

	#ifdef __ROSE__
	if (okay) {
		okay = MakeNewRoseServers();
	}
	#endif

	return okay;
}

/*
	This routine is called during the program's cleanup and gives you
	a chance to deallocate any of your own globals that you allocated 
	in the above routine.
*/
void DisposeUserGlobals(void)
{
	void		CDesktop_Dispose();
	
	CDesktop_Dispose();

	#ifdef __ROSE__
	gRoseServers->Dispose();
	#endif
	
	O_PostDispose();

	GSOS_PostDispose();

	ReportTracking();

	Idle_DisposeSpeechChannel(TRUE);
}
