const TStringSet&
GetIncludeFileTypes( void )
{GUCEF_TRACE;

    static TStringSet fileTypes;
    if ( fileTypes.empty() )
    {
        fileTypes.insert( "h" );
        fileTypes.insert( "hpp" );
    }
    return fileTypes;
}
const TStringSet&
GetBinaryFileTypes( void )
{GUCEF_TRACE;

    static TStringSet fileTypes;
    if ( fileTypes.empty() )
    {
        fileTypes.insert( "pdb" );
        fileTypes.insert( "dll" );
        fileTypes.insert( "so" );
        fileTypes.insert( "lib" );
        fileTypes.insert( "manifest" );
    }
    return fileTypes;
}
CORE::CString
GenerateContentForAndroidMakefile( const TModuleInfoEntryPairVector& mergeLinks ,
                                   const TModuleInfo& moduleInfo                ,
                                   const CORE::CString& moduleRoot              ,
                                   bool addGeneratorCompileTimeToOutput         ,
                                   TStringSet& ndkModulesUsed                   )
{GUCEF_TRACE;

    CORE::CString contentPrefix = makefileHeader;

    if ( addGeneratorCompileTimeToOutput )
    {
        contentPrefix +=
          "#"
          "# The project generator version used was compiled on " __TIME__ __DATE__ "\n"
          "#\n\n";
    }

    contentPrefix +=
      "ifndef MY_MODULE_PATH\n"
      "  MY_MODULE_PATH := $(call my-dir)\n"
      "endif\n"
      "LOCAL_PATH := $(MY_MODULE_PATH)\n\n"
      "include $(CLEAR_VARS)\n\n"
      "@echo Module path: $(MY_MODULE_PATH)\n"
      "LOCAL_MODULE := " + moduleInfo.name + "\n";

    if ( ( MODULETYPE_SHARED_LIBRARY == moduleInfo.moduleType ) ||
         ( MODULETYPE_STATIC_LIBRARY == moduleInfo.moduleType )  )
    {
        contentPrefix += "LOCAL_MODULE_FILENAME := lib" + moduleInfo.name + "\n";
    }
    contentPrefix += "@echo Module name: $(LOCAL_MODULE)\n\n";

    // Generate the source files section
    CORE::CString srcFilesSection = "LOCAL_SRC_FILES := \\\n";
    bool firstLoop = true;
    TStringVectorMap::const_iterator i = moduleInfo.sourceDirs.begin();
    while ( i != moduleInfo.sourceDirs.end() )
    {
        const CORE::CString& srcDir = (*i).first;
        const TStringVector& srcFiles = (*i).second;

        TStringVector::const_iterator n = srcFiles.begin();
        while ( n != srcFiles.end() )
        {
            if ( !firstLoop )
            {
                srcFilesSection += " \\\n";
            }
            firstLoop = false;

            CORE::CString relFilePath = srcDir;
            CORE::AppendToPath( relFilePath, (*n) );

            srcFilesSection += "  " + relFilePath.ReplaceChar( '\\', '/' );

            ++n;
        }
        ++i;
    }

    // Add some spacing for readability
    srcFilesSection += "\n\n";

    // Generate the included files section
    // for android make files we only need the path
    // it will locate the header file on its own
    CORE::CString includeFilesSection = "LOCAL_C_INCLUDES := \\\n";
    i = moduleInfo.includeDirs.begin();
    firstLoop = true;
    while ( i != moduleInfo.includeDirs.end() )
    {
        if ( !firstLoop )
        {
            includeFilesSection += " \\\n";
        }
        firstLoop = false;

        const CORE::CString& dir = (*i).first;
        if ( !dir.IsNULLOrEmpty() )
        {
            includeFilesSection += "  $(MY_MODULE_PATH)/" + dir.ReplaceChar( '\\', '/' );
        }
        else
        {
            // Support the use-case where the include dir is empty because the moduleinfo dir == include dir
            includeFilesSection += "  $(MY_MODULE_PATH)";
        }

        ++i;
    }

    // We also need to add the include paths required to find headers
    // refered to because of dependencies
    TStringSet::const_iterator n = moduleInfo.dependencyIncludeDirs.begin();
    while ( n != moduleInfo.dependencyIncludeDirs.end() )
    {
        if ( !firstLoop )
        {
            includeFilesSection += " \\\n";
        }
        firstLoop = false;

        includeFilesSection += "  $(MY_MODULE_PATH)/" + (*n).ReplaceChar( '\\', '/' );

        ++n;
    }

    // Add some spacing for readability
    includeFilesSection += "\n\n";

    // Now we add the preprocessor definitions
    CORE::CString preprocessorSection;
    if ( !moduleInfo.preprocessorSettings.defines.empty() )
    {
        preprocessorSection = "LOCAL_CFLAGS :=";

        TStringSet::const_iterator m = moduleInfo.preprocessorSettings.defines.begin();
        while ( m != moduleInfo.preprocessorSettings.defines.end() )
        {
            preprocessorSection += " -D" + (*m);
            ++m;
        }

        // Add some spacing for readability
        preprocessorSection += "\n\n";
    }

    // Now we add the compiler flags, if any
    // For Android we only support the GCC compilers
    CORE::CString compilerSection;
    TStringMap::const_iterator p = moduleInfo.compilerSettings.compilerFlags.find( "GCC" );
    if ( p != moduleInfo.compilerSettings.compilerFlags.end() )
    {
        compilerSection = "LOCAL_CFLAGS +=" + (*p).second + "\n\n";
    }
    p = moduleInfo.compilerSettings.compilerFlags.find( "G++" );
    if ( p != moduleInfo.compilerSettings.compilerFlags.end() )
    {
        compilerSection = "LOCAL_CPPFLAGS +=" + (*p).second + "\n\n";
    }

    // Now we will add all the dependency linking instructions.
    // For some reason it matters, at specification time, to Android's build
    // system whether the module you are linking to is a dynamically linked module
    // or a statically linked module. As such we have to figure out which is which.
    //
    // We make an alphabetical list instead of creating the section right away because
    // we dont want the order to vary in the makefile because such changes cause the NDK
    // to build the code again for no reason.
    CORE::CString linkingErrorSection;
    TStringSet linkedSharedLibraries;
    TStringSet linkedStaticLibraries;
    TStringSet linkedRuntimeLibraries;
    TModuleTypeMap::const_iterator m = moduleInfo.linkerSettings.linkedLibraries.begin();
    while ( m != moduleInfo.linkerSettings.linkedLibraries.end() )
    {
        const CORE::CString& linkedLibName = (*m).first;
        TModuleType linkedLibType = (*m).second;
        switch ( linkedLibType )
        {
            case MODULETYPE_EXECUTABLE:
            {
                // This is really nasty but the best option for now...
                // It is possible to link to exported symbols from an executable
                // under linux and as such we will leverage this here
                linkedSharedLibraries.insert( linkedLibName );
                break;
            }
            case MODULETYPE_SHARED_LIBRARY:
            {
                linkedSharedLibraries.insert( linkedLibName );
                break;
            }
            case MODULETYPE_STATIC_LIBRARY:
            {
                linkedStaticLibraries.insert( linkedLibName );
                break;
            }
            case MODULETYPE_CODE_INTEGRATE_LOCATION:
            case MODULETYPE_HEADER_INTEGRATE_LOCATION:
            case MODULETYPE_HEADER_INCLUDE_LOCATION:
            {
                // Skip this, no linking required
                break;
            }
            default:
            {
                // Since the depedendency module type was not predefined we will investigate among
                // the other modules to try to determine the nature of the linked module
                const TModuleInfo* linkedDependency = FindModuleByName( mergeLinks, linkedLibName );
                if ( NULL != linkedDependency )
                {
                    // The module we are linking too is part of this project.
                    // As such we can simply check the other module's info
                    // to find out wheter its a dynamically linked module or not
                    // which in turn tells us how to instruct the Android build system
                    // to link.
                    switch( linkedDependency->moduleType )
                    {
                        case MODULETYPE_SHARED_LIBRARY:
                        {
                            linkedSharedLibraries.insert( linkedLibName );
                            break;
                        }
                        case MODULETYPE_STATIC_LIBRARY:
                        {
                            linkedStaticLibraries.insert( linkedLibName );
                            break;
                        }
                        case MODULETYPE_EXECUTABLE:
                        {
                            // This is really nasty but the best option for now...
                            // It is possible to link to exported symbols from an executable
                            // under linux and as such we will leverage this here
                            linkedSharedLibraries.insert( linkedLibName );
                            break;
                        }
                        case MODULETYPE_HEADER_INTEGRATE_LOCATION:
                        case MODULETYPE_CODE_INTEGRATE_LOCATION:
                        {
                            // Dont do anything.
                            // The files for this 'module' have already been merged into the dependent module
                            break;
                        }
                        case MODULETYPE_HEADER_INCLUDE_LOCATION:
                        {
                            // Don't have to do anything.
                            // Due to the auto-dependency tracking of include paths the header paths will have been added to
                            // whatever module depends on this 'module'
                            break;
                        }
                        default:
                        {
                            linkingErrorSection +=
                              "# *** ERROR *** Finish me\n"
                              "# Unable to determing module type from the source information\n"
                              "# Please edit the line below to manually set the correct linking method for this dependency\n";
                            linkingErrorSection += "#LOCAL_<(LDLIBS???)> += " + moduleInfo.name + "\n\n";

                            GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "Error: the module " + moduleInfo.name + " does not have a useable module type set, you will have to manually edit the file to correct the error" );
                            break;
                        }
                    }
                }
                else
                {
                    // If we get here then this dependency is not on a module which is part of the project
                    // As such we cannot build this module thus the only approriote linking method would seem
                    // to be the one where we simply instruct the linker to load this dependency at runtime.
                    // This will typically be the case for any Android NDK modules we have to link to.
                    linkedRuntimeLibraries.insert( linkedLibName );
                }
            }
        }
        ++m;
    }

    CORE::CString linkingSection;
    CORE::CString linkedSharedLibrariesSection = "\nLOCAL_SHARED_LIBRARIES := \\\n";
    CORE::CString linkedStaticLibrariesSection = "\nLOCAL_STATIC_LIBRARIES := \\\n";
    CORE::CString linkedRuntimeLibrariesSection = "\nLOCAL_LDLIBS := \\\n";

    // Based on what was found we will construct the linking section
    bool first = true;
    n = linkedSharedLibraries.begin();
    while ( n != linkedSharedLibraries.end() )
    {
        if ( !first )
        {
             linkedSharedLibrariesSection += " \\\n";
        }
        linkedSharedLibrariesSection += "  " + (*n);
        first = false;
        ++n;
    }
    first = true;
    n = linkedStaticLibraries.begin();
    while ( n != linkedStaticLibraries.end() )
    {
        if ( !first )
        {
             linkedStaticLibrariesSection += " \\\n";
        }
        linkedStaticLibrariesSection += "  " + (*n);
        first = false;
        ++n;
    }
    first = true;
    n = linkedRuntimeLibraries.begin();
    while ( n != linkedRuntimeLibraries.end() )
    {
        if ( !first )
        {
             linkedRuntimeLibrariesSection += " \\\n";
        }
        linkedRuntimeLibrariesSection += "  -l" + (*n);
        first = false;
        ++n;
    }

    if ( !linkedSharedLibraries.empty() )
    {
        linkingSection += linkedSharedLibrariesSection + "\n\n";
    }
    if ( !linkedStaticLibraries.empty() )
    {
        linkingSection += linkedStaticLibrariesSection + "\n\n";
    }
    if ( !linkedRuntimeLibraries.empty() )
    {
        linkingSection += linkedRuntimeLibrariesSection + "\n\n";
    }
    linkingSection += linkingErrorSection;

    // Check if we have a file on disk of information which is to be inserted into
    // our automatically generated make file
    CORE::CString manualContent;
    CORE::CString manualContentFilePath = moduleRoot;
    CORE::AppendToPath( manualContentFilePath, "AndroidAddition.mk" );
    if ( CORE::FileExists( manualContentFilePath ) )
    {
        if ( CORE::LoadTextFileAsString( manualContentFilePath ,
                                         manualContent         ) )
        {
            GUCEF_LOG( CORE::LOGLEVEL_NORMAL, "Successfully loaded manually defined content for module " + moduleInfo.name + " from addition file " + manualContentFilePath );
        }
        else
        {
            manualContent =
              "# *** ERROR *** Finish me\n"
              "# Unable to load manually defined content from detected AndroidAddition.mk file\n"
              "# Please manually insert its contents here\n\n";
            GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "Error: the module " + moduleInfo.name + " has manually defined content in a AndroidAddition.mk file but it could not be loaded, you will have to manually edit the file to correct the error" );
        }
    }

    // Now generate the latter part of the file which contains more meta data about the module
    CORE::CString contentSuffix;
    switch ( moduleInfo.moduleType )
    {
        case MODULETYPE_SHARED_LIBRARY:
        {
            contentSuffix += "include $(BUILD_SHARED_LIBRARY)\n\n";
            break;
        }
        case MODULETYPE_STATIC_LIBRARY:
        {
            contentSuffix += "include $(BUILD_STATIC_LIBRARY)\n\n";
            break;
        }
        case MODULETYPE_EXECUTABLE:
        {
            contentSuffix += "include $(BUILD_EXECUTABLE)\n\n";
            break;
        }
        default:
        {
            contentSuffix +=
              "# *** ERROR *** Finish me\n"
              "# Unable to determine module type from the source information\n"
              "# Please edit the line below to manually set the correct module type to build\n"
              "#include $(BUILD_???)\n\n";

            GUCEF_ERROR_LOG( CORE::LOGLEVEL_NORMAL, "Error: the module " + moduleInfo.name + " does not have a useable module type set, you will have to manually edit the file to correct the error" );
            break;
        }
    }

    // Check for NDK static libraries to import
    if ( !linkedStaticLibraries.empty() )
    {
        TStringSet::iterator a = linkedStaticLibraries.find( "android_native_app_glue" );
        if ( a != linkedStaticLibraries.end() )
        {
            ndkModulesUsed.insert( "android_native_app_glue" );
            contentSuffix += "$(call import-module,android/native_app_glue)\n";
        }
        a = linkedStaticLibraries.find( "cpufeatures" );
        if ( a != linkedStaticLibraries.end() )
        {
            ndkModulesUsed.insert( "cpufeatures" );
            contentSuffix += "$(call import-module,android/cpufeatures)\n";
        }
    }

    return contentPrefix + srcFilesSection + includeFilesSection + preprocessorSection + compilerSection + linkingSection + manualContent + contentSuffix;
}