/**
 * @param	pFS				Initialized FAT State struct
 * @param	hParent			Handle to directory in which to search
 * @param	pMask			Pointer to Find Mask structure
 * @param	resultDirEntry	Optional pointer to buffer in which to store a copy of the directory entry
 * 							(if meta-information on the file needs to be accessed)
 * @param	hResultFile		Optional pointer to buffer in which to store a new handle
 * 							to the file/directory found
 * 
 * @return	boolean			True if a match was found
 */
bool Fat_FindFile(
		fat_state_t*	pFS,
		file_handle_t*	hParent,
		fat_find_mask_t* pMask,
		fat_direntry_t*	resultDirEntry,
		file_handle_t*  hResultFile)
{
	// addressing
	unsigned long	address;
	unsigned int	bytes_to_border;
	unsigned int	readCount;
	//unsigned int	bytsPerClus = (1 << pFS->BytsPerSec_bit) << pFS->SecPerClus_bit;
	// directory structure
	fat_direntry_t	dirEntry;

	// callback
	sd_read_cb_t fpCompare = _Fat_FindFileCompare;
	
	//
	// setup
	//
	if( (hParent->Flags & FILE_HANDLE_FLAG_DIRECTORY) == 0 )
		return FALSE;
	if( (hParent->Flags & FILE_HANDLE_FLAG_EOF) )
		return FALSE;
	if( (hParent->Flags & FILE_HANDLE_FLAG_DIRTY) == 0 ) {
		// open handle
		hParent->CurrentCA = hParent->StartCA;
		hParent->CurrentPos = FAT_DIRENTRY_SIZE;	// always ignore first entry (. or VOLID)
		hParent->Flags |= FILE_HANDLE_FLAG_DIRTY;
	}

	Sd_set_blocklength(9); // 512
	while(1) {
		//
		// read one cluster
		//
		
		address = _Fat_GetFilePos(pFS, hParent);

		if( (pFS->FATType == FAT16) && (hParent->Flags & FILE_HANDLE_FLAG_ROOT) ) {
			// FAT16 root dir is not clustered, process in one shot
			// side effect: CurrentCA is not increased (but CurrentPos is)
			bytes_to_border = pFS->RootDirSz;
			bytes_to_border -= hParent->CurrentCA << pFS->SecPerClus_bit;
			bytes_to_border <<= pFS->BytsPerSec_bit;
		} else
			bytes_to_border = pFS->BytsPerClus;
		bytes_to_border -= hParent->CurrentPos;		// bytes to end of cluster
		
		// traverse all entries in this cluster
		readCount = Sd_read_chunked(
			&dirEntry,
			address, bytes_to_border, FAT_DIRENTRY_SIZE,
			&fpCompare, pMask );
		RETF(readCount);

		//
		// update handle
		//
		if( readCount == bytes_to_border ) {
			// traverse to next cluster
			readCount = _fat_readChain(pFS, hParent->CurrentCA, &hParent->CurrentCA, 1);
			if( readCount == 0 ) {
				hParent->Flags |= FILE_HANDLE_FLAG_EOF;
				return FALSE;
			}
			hParent->CurrentPos = 0;
		} else {
			hParent->CurrentPos += readCount;
		}
		
		//
		// process result
		//
		if( dirEntry.Name[0] == FAT_NAME_FLAG_END )
			return FALSE;
		else if( dirEntry.Name[0] != FAT_NAME_FLAG_DELETED ) {
			// match
			
			if( resultDirEntry )
				memcpy( resultDirEntry, &dirEntry, sizeof(dirEntry) );
			if( hResultFile ) {
				_Fat_DirEntryToHandle(hResultFile, &dirEntry);
			}
			return TRUE;
		}
	} // while
}

bool _Fat_FindFileCompare(unsigned char** p, void* pParam) {
	fat_direntry_t*		dirEntry; 
	
	if( **p == FAT_NAME_FLAG_DELETED ) {
		// not found, continue
		return TRUE;
	}

	if( **p == FAT_NAME_FLAG_END ) {
		// not found, stop
		return FALSE;
	}
	
	dirEntry = (fat_direntry_t*)*p;
	if( dirEntry->Attributes != 0 ) {
		if( (dirEntry->Attributes == FAT_ATTR_LONG_NAME)
			|| !(dirEntry->Attributes & ((fat_find_mask_t*)pParam)->AttribMask) ) {
			// not found, continue
			goto _fat_find_file_compare_nfc;
		}
	}

	if( Fat_MatchName(dirEntry->Name, ((fat_find_mask_t*)pParam)->Name) ) {
		// found, stop
		return FALSE;
	}
	// not found, stop
	
_fat_find_file_compare_nfc:
	**p = FAT_NAME_FLAG_DELETED;	
	return TRUE;
	// buffer pointer p remains unchanged at start of buffer
}
