void FileChangeNotifier::OnFileChange( const FileSystemUtils::Path& filename )
{
    if (m_bActive)
    {
        // Check for multiple hits on the same file in close succession 
        // (Can be caused by NTFS system making multiple changes even though only
        //  one actual change occurred)
        bool bIgnoreFileChange = (filename == m_LastFileChanged) && 
            m_fFileChangeSpamTimeRemaining > 0.0f;
        m_LastFileChanged = filename;

        if (!bIgnoreFileChange)
        {
            FileSystemUtils::Path filePath = filename;
            filePath.ToOSCanonicalCase();

            m_changedFileList.push_back(filePath.m_string);

            if (!m_bRecompilePending)
            {
                m_bRecompilePending = true;
                m_fTimeUntilNextAllowedRecompile = max(m_fTimeUntilNextAllowedRecompile, m_fChangeNotifyDelay);
            }

            m_fFileChangeSpamTimeRemaining = FILE_CHANGE_SPAM_TIME;
            TriggerNotificationIfPossible();    
        }    
    }
}
void FileChangeNotifier::Watch( const FileSystemUtils::Path& filename, IFileChangeListener *pListener )
{
    FileSystemUtils::Path fixedFilename = filename.DelimitersToOSDefault(); // Note this doesn't handle ../
    
    fixedFilename = fixedFilename.GetCleanPath();
    fixedFilename.ToOSCanonicalCase();

    m_pFileMonitor->Watch(fixedFilename, this);
    m_fileListenerMap[fixedFilename].insert(pListener);
    pListener->OnRegisteredWithNotifier(this);
}
FileSystemUtils::Path RuntimeObjectSystem::FindFile( const FileSystemUtils::Path& input )
{
    FileSystemUtils::Path requestedDirectory = input;
    FileSystemUtils::Path filename;
    FileSystemUtils::Path foundFile = input;
    bool bIsFile = input.HasExtension();
    if( bIsFile )
    {
        requestedDirectory = requestedDirectory.ParentPath();
        filename = input.Filename();
    }
    requestedDirectory.ToOSCanonicalCase();
    filename.ToOSCanonicalCase();
    foundFile.ToOSCanonicalCase();

    // Step 1: Try input directory
    if( requestedDirectory.Exists() )
    {
        m_FoundSourceDirectoryMappings[ requestedDirectory ] = requestedDirectory;
    }
    else
    {
        // Step 2: Attempt to find a pre-existing mapping
        bool bFoundMapping = false;
        if( m_FoundSourceDirectoryMappings.size() )
        {
            FileSystemUtils::Path testDir = requestedDirectory;
            FileSystemUtils::Path foundDir;
            unsigned int depth = 0;
            bool bFound = false;
            while( testDir.HasParentPath() )
            {
                TFileMapIterator itrFind = m_FoundSourceDirectoryMappings.find( testDir );
                if( itrFind != m_FoundSourceDirectoryMappings.end() )
                {
                    foundDir = itrFind->second;
                    bFound = true;
                    break;
                }

                testDir = testDir.ParentPath();
                ++depth;
            }

            if( bFound )
            {
                if( depth )
                {
                    // not an exact match
                    FileSystemUtils::Path directory = requestedDirectory;
                    directory.m_string.replace( 0, testDir.m_string.length(), foundDir.m_string );
                    if( directory.Exists() )
                    {
                        foundFile = directory / filename;
                        if( foundFile.Exists() )
                        {
                            m_FoundSourceDirectoryMappings[ requestedDirectory ] = directory;
                            if( m_pCompilerLogger ) {  m_pCompilerLogger->LogInfo( "Found Directory Mapping: %s to %s\n", requestedDirectory.c_str(), directory.c_str() ); }
                            bFoundMapping = true;
                        }
                    }

                }
                else
                {
                    // exact match
                    foundFile = foundDir / filename;
                    bFoundMapping = true;
                }
            }
            
            if( !bFoundMapping )
            {
                // Step 3: Attempt to find a mapping from a known path
                TFileList requestedSubPaths;
                FileSystemUtils::Path requestedSubPath = requestedDirectory;
                while( requestedSubPath.HasParentPath() )
                {
                    requestedSubPaths.push_back( requestedSubPath );
                    requestedSubPath = requestedSubPath.ParentPath();
                }

                TFileMapIterator itr = m_FoundSourceDirectoryMappings.begin();
                while( ( itr != m_FoundSourceDirectoryMappings.end() ) && !bFoundMapping )
                {
                    FileSystemUtils::Path existingPath = itr->second;
                    while( ( existingPath.HasParentPath() ) && !bFoundMapping )
                    {
                        // check all potentials
                        for( size_t i=0; i<requestedSubPaths.size(); ++i )
                        {
                            FileSystemUtils::Path toCheck = existingPath / requestedSubPaths[i].Filename();
                            if( toCheck.Exists() )
                            {
                                // potential mapping
                                FileSystemUtils::Path directory = requestedDirectory;
                                directory.m_string.replace( 0, requestedSubPaths[i].m_string.length(), toCheck.m_string );
                                if( directory.Exists() )
                                {
                                    foundFile = directory / filename;
                                    if( foundFile.Exists() )
                                    {
                                        m_FoundSourceDirectoryMappings[ requestedDirectory ] = directory;
                                        if( m_pCompilerLogger ) {  m_pCompilerLogger->LogInfo( "Found Directory Mapping: %s to %s\n", requestedDirectory.c_str(), directory.c_str() ); }
                                        bFoundMapping = true;
                                        break;
                                    }
                                }
                            }
                        }
                        existingPath = existingPath.ParentPath();
                    }
                    ++itr;
                }
            }
        }
    }

    if( !foundFile.Exists() )
    {
        if( m_pCompilerLogger ) {  m_pCompilerLogger->LogWarning( "Could not find Directory Mapping for: %s\n", input.c_str() ); }
        ++m_NumNotFoundSourceFiles;
    }
    return foundFile;
}
void RuntimeObjectSystem::SetupRuntimeFileTracking(const IAUDynArray<IObjectConstructor*>& constructors_)
{
#ifndef RCCPPOFF
	// for optimization purposes we skip some actions when running for the first time (i.e. no previous constructors)
	static bool bFirstTime = true;

	for (size_t i = 0, iMax = constructors_.Size(); i < iMax; ++i)
	{
		const char* pFilename = constructors_[i]->GetFileName(); // GetFileName returns full path including GetCompiledPath()
		if( !pFilename )
		{
			continue;
		}
		Path filePath = pFilename;
        filePath = filePath.GetCleanPath();
        filePath = FindFile( filePath );

        unsigned short projectId = constructors_[ i ]->GetProjectId();
        ProjectSettings& project = GetProject( projectId );
        AddToRuntimeFileList( filePath.c_str( ), projectId );

		if( !bFirstTime )
		{
 			//remove old include file mappings for this file
            TFileToFilesIterator itrCurr = project.m_RuntimeIncludeMap.begin( );
            while( itrCurr != project.m_RuntimeIncludeMap.end( ) )
			{
				if( itrCurr->second == filePath )
				{
                    TFileToFilesIterator itrErase = itrCurr;
                    ++itrCurr;
                    project.m_RuntimeIncludeMap.erase( itrErase );
				}
				else
				{
					++itrCurr;
				}
			}

            //remove previous link libraries for this file
            project.m_RuntimeLinkLibraryMap.erase( filePath );

            //remove previous source dependencies
            project.m_RuntimeSourceDependencyMap.erase( filePath );
		}

        //we need the compile path for some platforms where the __FILE__ path is relative to the compile path
		FileSystemUtils::Path compileDir = constructors_[i]->GetCompiledPath();

		//add include file mappings
		for (size_t includeNum = 0; includeNum <= constructors_[i]->GetMaxNumIncludeFiles(); ++includeNum)
		{
			const char* pIncludeFile = constructors_[i]->GetIncludeFile(includeNum);
			if( pIncludeFile )
			{
                FileSystemUtils::Path pathInc = compileDir / pIncludeFile;
                pathInc = FindFile( pathInc.GetCleanPath() );
				TFileToFilePair includePathPair;
				includePathPair.first = pathInc;
				includePathPair.second = filePath;
                AddToRuntimeFileList( pathInc.c_str(), projectId );
                project.m_RuntimeIncludeMap.insert( includePathPair );
			}
		}
            

 		//add link library file mappings
		for (size_t linklibraryNum = 0; linklibraryNum <= constructors_[i]->GetMaxNumLinkLibraries(); ++linklibraryNum)
		{
			const char* pLinkLibrary = constructors_[i]->GetLinkLibrary(linklibraryNum);
			if( pLinkLibrary )
			{
                // We do not use FindFiles for Linked Libraries as these are searched for on
                // the library paths, which are themselves searched for.
				TFileToFilePair linklibraryPathPair;
				linklibraryPathPair.first = filePath;
				linklibraryPathPair.second = pLinkLibrary;
                project.m_RuntimeLinkLibraryMap.insert( linklibraryPathPair );
			}
		}

        //add source dependency file mappings
		for (size_t num = 0; num <= constructors_[i]->GetMaxNumSourceDependencies(); ++num)
		{
			SourceDependencyInfo sourceDependency = constructors_[i]->GetSourceDependency(num);
			FileSystemUtils::Path pathInc[2];	// array of potential include files for later checks
			if( sourceDependency.filename )
			{
				FileSystemUtils::Path pathSrc;
				if( sourceDependency.relativeToPath )
				{
					pathSrc = sourceDependency.relativeToPath;
					if( pathSrc.HasExtension() )
					{
						pathInc[1] = compileDir / pathSrc;
						pathSrc =  compileDir / pathSrc.ParentPath() / sourceDependency.filename;
					}
					else
					{
						pathSrc =  compileDir / pathSrc / sourceDependency.filename;
					}
				}
				else
				{
					pathSrc = compileDir / sourceDependency.filename;
				}
                pathSrc.ToOSCanonicalCase();
                pathSrc = pathSrc.DelimitersToOSDefault();
                pathSrc = pathSrc.GetCleanPath();
				pathInc[0] = pathSrc;
				if( sourceDependency.extension )
				{
					pathSrc.ReplaceExtension( sourceDependency.extension );
				}
				pathSrc = FindFile( pathSrc.GetCleanPath() );
				TFileToFilePair sourcePathPair;
				sourcePathPair.first = filePath;
				sourcePathPair.second = pathSrc;
                project.m_RuntimeSourceDependencyMap.insert( sourcePathPair );
                
                // if the include file with a source dependancy is logged as an runtime include, then we mark this .cpp as compile dependencies on change
				for( int inc=0; inc<2; ++inc )
				{
					TFileToFilesEqualRange range = project.m_RuntimeIncludeMap.equal_range( pathInc[inc] );
					if( range.first != range.second )
					{
						// add source file to runtime file list
						AddToRuntimeFileList( pathSrc.c_str(), projectId );

						// also add this as a source dependency, so it gets force compiled on change of header (and not just compiled)
						TFileToFilePair includePathPair;
						includePathPair.first = pathInc[inc];
						includePathPair.second = pathSrc;
						project.m_RuntimeIncludeMap.insert( includePathPair );
					}
				}
			}
		}
	}

    bFirstTime = false;
#endif
}