include(CheckCXXSourceCompiles)

# collect all the headers in the source directory
file(GLOB HEADERS ${VSG_SOURCE_DIR}/include/vsg/*.h ${VSG_SOURCE_DIR}/include/vsg/*/*.h)

# for out of source builds collect all the auto-generated headers in the build directory
if (NOT (${VSG_SOURCE_DIR} STREQUAL ${VSG_BINARY_DIR}))
    file(GLOB AUTOGENERATED_HEADERS ${VSG_BINARY_DIR}/include/vsg/*.h ${VSG_BINARY_DIR}/include/vsg/*/*.h)
    set(HEADERS ${HEADERS} ${AUTOGENERATED_HEADERS})
endif()

# set up the source files explicitly.
set(SOURCES

    core/Allocator.cpp
    core/Auxiliary.cpp
    core/ConstVisitor.cpp
    core/Data.cpp
    core/External.cpp
    core/MemorySlots.cpp
    core/Object.cpp
    core/Objects.cpp
    core/Visitor.cpp
    core/Version.cpp

    maths/maths_transform.cpp

    nodes/Group.cpp
    nodes/Geometry.cpp
    nodes/Node.cpp
    nodes/QuadGroup.cpp
    nodes/CullGroup.cpp
    nodes/CullNode.cpp
    nodes/LOD.cpp
    nodes/PagedLOD.cpp
    nodes/AbsoluteTransform.cpp
    nodes/MatrixTransform.cpp
    nodes/Transform.cpp
    nodes/VertexDraw.cpp
    nodes/VertexIndexDraw.cpp
    nodes/DepthSorted.cpp
    nodes/Bin.cpp
    nodes/Switch.cpp
    nodes/StateGroup.cpp
    nodes/Light.cpp
    nodes/TileDatabase.cpp

    commands/BindIndexBuffer.cpp
    commands/BindVertexBuffers.cpp
    commands/Commands.cpp
    commands/BlitImage.cpp
    commands/CopyImage.cpp
    commands/CopyImageToBuffer.cpp
    commands/CopyAndReleaseBuffer.cpp
    commands/CopyAndReleaseImage.cpp
    commands/ClearAttachments.cpp
    commands/PipelineBarrier.cpp
    commands/Event.cpp
    commands/NextSubPass.cpp
    commands/Dispatch.cpp
    commands/Draw.cpp
    commands/DrawIndirect.cpp
    commands/DrawIndexed.cpp
    commands/DrawIndexedIndirect.cpp
    commands/SetDepthBias.cpp
    commands/SetLineWidth.cpp
    commands/SetScissor.cpp
    commands/SetViewport.cpp
    commands/ResolveImage.cpp
    commands/ResetQueryPool.cpp
    commands/WriteTimestamp.cpp
    commands/BeginQuery.cpp
    commands/EndQuery.cpp
    commands/CopyQueryPoolResults.cpp
    commands/ExecuteCommands.cpp
    commands/CopyImageViewToWindow.cpp

    state/ArrayState.cpp
    state/BindDescriptorSet.cpp
    state/Buffer.cpp
    state/BufferInfo.cpp
    state/BufferView.cpp
    state/ComputePipeline.cpp
    state/DescriptorSet.cpp
    state/GraphicsPipeline.cpp
    state/Descriptor.cpp
    state/DescriptorBuffer.cpp
    state/DescriptorImage.cpp
    state/DescriptorTexelBufferView.cpp
    state/DescriptorSetLayout.cpp
    state/ShaderModule.cpp
    state/ShaderStage.cpp
    state/PipelineLayout.cpp
    state/Sampler.cpp
    state/ResourceHints.cpp
    state/StateCommand.cpp
    state/StateSwitch.cpp
    state/Image.cpp
    state/ImageInfo.cpp
    state/ImageView.cpp
    state/VertexInputState.cpp
    state/InputAssemblyState.cpp
    state/TessellationState.cpp
    state/ViewportState.cpp
    state/RasterizationState.cpp
    state/MultisampleState.cpp
    state/DepthStencilState.cpp
    state/ColorBlendState.cpp
    state/DynamicState.cpp
    state/ViewDependentState.cpp
    state/QueryPool.cpp
    state/PushConstants.cpp

    io/convert_utf.cpp
    io/FileSystem.cpp
    io/AsciiInput.cpp
    io/DatabasePager.cpp
    io/AsciiOutput.cpp
    io/BinaryInput.cpp
    io/BinaryOutput.cpp
    io/Input.cpp
    io/Logger.cpp
    io/Output.cpp
    io/Options.cpp
    io/ObjectFactory.cpp
    io/Path.cpp
    io/ReaderWriter.cpp
    io/VSG.cpp
    io/spirv.cpp
    io/tile.cpp
    io/glsl.cpp
    io/read.cpp
    io/write.cpp
    io/mem_stream.cpp

    text/CpuLayoutTechnique.cpp
    text/GpuLayoutTechnique.cpp
    text/Font.cpp
    text/StandardLayout.cpp
    text/Text.cpp
    text/TextGroup.cpp


    threading/Affinity.cpp
    threading/OperationThreads.cpp

    app/Camera.cpp
    app/CompileManager.cpp
    app/EllipsoidModel.cpp
    app/Viewer.cpp
    app/Window.cpp
    app/WindowAdapter.cpp
    app/WindowTraits.cpp
    app/Trackball.cpp
    app/CommandGraph.cpp
    app/SecondaryCommandGraph.cpp
    app/RenderGraph.cpp
    app/Presentation.cpp
    app/RecordAndSubmitTask.cpp
    app/TransferTask.cpp
    app/WindowResizeHandler.cpp
    app/View.cpp
    app/ViewMatrix.cpp
    app/ProjectionMatrix.cpp
    app/UpdateOperations.cpp
    app/RecordTraversal.cpp
    app/CompileTraversal.cpp

    raytracing/AccelerationGeometry.cpp
    raytracing/AccelerationStructure.cpp
    raytracing/BottomLevelAccelerationStructure.cpp
    raytracing/BuildAccelerationStructureTraversal.cpp
    raytracing/DescriptorAccelerationStructure.cpp
    raytracing/RayTracingPipeline.cpp
    raytracing/RayTracingShaderGroup.cpp
    raytracing/TopLevelAccelerationStructure.cpp
    raytracing/TraceRays.cpp

    rtx/DrawMeshTasks.cpp
    rtx/DrawMeshTasksIndirect.cpp
    rtx/DrawMeshTasksIndirectCount.cpp

    ui/UIEvent.cpp
    ui/ApplicationEvent.cpp
    ui/KeyEvent.cpp
    ui/TouchEvent.cpp
    ui/PointerEvent.cpp
    ui/ScrollWheelEvent.cpp
    ui/WindowEvent.cpp
    ui/RecordEvents.cpp
    ui/CollectEvents.cpp
    ui/ShiftEventTime.cpp
    ui/PlayEvents.cpp
    ui/PrintEvents.cpp

    vk/CommandBuffer.cpp
    vk/CommandPool.cpp
    vk/Context.cpp
    vk/DescriptorPool.cpp
    vk/Device.cpp
    vk/DeviceFeatures.cpp
    vk/DeviceMemory.cpp
    vk/Extensions.cpp
    vk/Fence.cpp
    vk/Framebuffer.cpp
    vk/Instance.cpp
    vk/MemoryBufferPools.cpp
    vk/PhysicalDevice.cpp
    vk/Queue.cpp
    vk/RenderPass.cpp
    vk/Semaphore.cpp
    vk/Surface.cpp
    vk/Swapchain.cpp
    vk/ResourceRequirements.cpp

    utils/AnimationPath.cpp
    utils/CommandLine.cpp
    utils/Builder.cpp
    utils/SharedObjects.cpp
    utils/ShaderSet.cpp
    utils/GraphicsPipelineConfigurator.cpp
    utils/ShaderCompiler.cpp
    utils/ComputeBounds.cpp
    utils/Intersector.cpp
    utils/LineSegmentIntersector.cpp
    utils/LoadPagedLOD.cpp
)

# add platform specific Window implementation




# set up library dependencies
set(LIBRARIES PUBLIC
        Vulkan::Vulkan
        Threads::Threads
)

# Check for std::atomic
if(NOT MSVC AND NOT ANDROID AND NOT APPLE)
  check_cxx_source_compiles("
    #include <atomic>
    std::atomic<int> a;
    int main() { return a; }"
    HAVE_CXX_ATOMIC_WITHOUT_LIB
  )
  if(NOT HAVE_CXX_ATOMIC_WITHOUT_LIB)
    find_library(CXX_ATOMIC_LIBRARIES NAMES atomic atomic.so.1 libatomic.so.1 REQUIRED)
    list(APPEND LIBRARIES ${CXX_ATOMIC_LIBRARIES})
  endif()
endif()

if (glslang_FOUND)
    set(LIBRARIES ${LIBRARIES} PUBLIC ${glslang_LIBRARIES})

    include(CheckCXXSourceRuns)

    set(CMAKE_REQUIRED_LIBRARIES ${glslang_LIBRARY})
    set(CMAKE_REQUIRED_INCLUDES ${glslang_INCLUDE_DIR})

    if (NOT GLSLANG_ResourceLimits_maxDualSourceDrawBuffersEXT)
        check_cxx_source_compiles("
            #include <glslang/Include/ResourceLimits.h>

            int main()
            {
                TBuiltInResource resource;
                resource.maxDualSourceDrawBuffersEXT = 0;
                return 0;
            }
        " GLSLANG_ResourceLimits_maxDualSourceDrawBuffersEXT)
    endif()

    if (GLSLANG_ResourceLimits_maxDualSourceDrawBuffersEXT)
        set(EXTRA_DEFINES ${EXTRA_DEFINES} "GLSLANG_ResourceLimits_maxDualSourceDrawBuffersEXT")
    endif()

    if (NOT GLSLANG_EShLanguage_EShLangRayGen)
        check_cxx_source_compiles("
            #include <glslang/Public/ShaderLang.h>

            int main()
            {
                EShLanguage result = EShLangRayGen;
                return 0;
            }
        " GLSLANG_EShLanguage_EShLangRayGen)
    endif()

    if (NOT GLSLANG_EShLanguage_EShLangRayGenNV)
        check_cxx_source_compiles("
            #include <glslang/Public/ShaderLang.h>

            int main()
            {
                EShLanguage result = EShLangRayGenNV;
                return 0;
            }
        " GLSLANG_EShLanguage_EShLangRayGenNV)
    endif()

    if (GLSLANG_EShLanguage_EShLangRayGen)
        set(EXTRA_DEFINES ${EXTRA_DEFINES} "GLSLANG_EShLangRayGen")
    elseif (GLSLANG_EShLanguage_EShLangRayGenNV)
        set(EXTRA_DEFINES ${EXTRA_DEFINES} "GLSLANG_EShLangRayGenNV")
    endif()

    if (NOT DEFINED GLSLANG_ResourceLimits_maxMeshViewCountEXT)
        check_cxx_source_compiles("
            #include <glslang/Include/ResourceLimits.h>

            int main()
            {
                TBuiltInResource resource;
                resource.maxMeshViewCountEXT = 0;
                return 0;
            }
        " GLSLANG_ResourceLimits_maxMeshViewCountEXT)
    endif()

    if (GLSLANG_ResourceLimits_maxMeshViewCountEXT)
        set(EXTRA_DEFINES ${EXTRA_DEFINES} "GLSLANG_ResourceLimits_maxMeshViewCountEXT")
    endif()

    set(FIND_DEPENDENCY_GLSLANG "include(\"\${CMAKE_CURRENT_LIST_DIR}/vsg_glslangConfig.cmake\")")


endif()


if (ANDROID)
    set(HEADERS ${HEADERS} ${VSG_SOURCE_DIR}/include/vsg/platform/android/Android_Window.h)
    set(SOURCES ${SOURCES} platform/android/Android_Window.cpp)

    if(CMAKE_SYSTEM_VERSION GREATER 24)
        set(LIBRARIES ${LIBRARIES} PRIVATE ${AndroidLib} PRIVATE ${AndroidNativeWindowLib})
    else()
        set(LIBRARIES ${LIBRARIES} PRIVATE ${AndroidLib})
    endif()

elseif (WIN32)
    set(SOURCES ${SOURCES} platform/win32/Win32_Window.cpp)
elseif (IOS)
    set(HEADERS ${HEADERS}
        ${VSG_SOURCE_DIR}/include/vsg/platform/ios/iOS_Window.h
        ${VSG_SOURCE_DIR}/include/vsg/platform/ios/iOS_ViewController.h
    )
    set(SOURCES ${SOURCES}
        platform/ios/iOS_Window.mm
        platform/ios/iOS_ViewController.mm
    )
    set(LIBRARIES ${LIBRARIES} PRIVATE ${UIKIT_LIBRARY} PRIVATE ${QUARTZCORE_LIBRARY})
elseif (APPLE)
    set(SOURCES ${SOURCES} platform/macos/MacOS_Window.mm)
    set(LIBRARIES ${LIBRARIES} PRIVATE ${COCOA_LIBRARY} PRIVATE ${QUARTZCORE_LIBRARY})
else()
    set(SOURCES ${SOURCES} platform/unix/Xcb_Window.cpp)
    set(LIBRARIES ${LIBRARIES} PRIVATE PkgConfig::xcb)
endif()


add_library(vsg ${HEADERS} ${SOURCES})

# add definitions to enable building VulkanSceneGraph as part of submodule
add_library(vsg::vsg ALIAS vsg)
set(vsg_FOUND TRUE CACHE INTERNAL "vsg found.")
set(CMAKE_DISABLE_FIND_PACKAGE_vsg TRUE CACHE INTERNAL "Disable find_package(vsg) as it's not necessary.")


set_target_properties(vsg PROPERTIES FOLDER "VulkanSceneGraph")
if(MSVC)
    # ensure the libraries are all built in the lib directory
    macro(SET_OUTPUT_DIR_PROPERTY TARGET_TARGETNAME RELATIVE_OUTDIR)
        # Global properties (All generators but VS & Xcode)
        set_target_properties(${TARGET_TARGETNAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_LIBDIR}/${RELATIVE_OUTDIR}")
        set_target_properties(${TARGET_TARGETNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_LIBDIR}/${RELATIVE_OUTDIR}")
        set_target_properties(${TARGET_TARGETNAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_LIBDIR}/${RELATIVE_OUTDIR}")

        # Per-configuration property (VS, Xcode)
        foreach(CONF ${CMAKE_CONFIGURATION_TYPES})        # For each configuration (Debug, Release, MinSizeRel... and/or anything the user chooses)
            string(TOUPPER "${CONF}" CONF)                # Go uppercase (DEBUG, RELEASE...)

            # We use "FILE(TO_CMAKE_PATH", to create nice looking paths
            set_target_properties(${TARGET_TARGETNAME} PROPERTIES "ARCHIVE_OUTPUT_DIRECTORY_${CONF}" "${OUTPUT_LIBDIR}/${RELATIVE_OUTDIR}")
            set_target_properties(${TARGET_TARGETNAME} PROPERTIES "RUNTIME_OUTPUT_DIRECTORY_${CONF}" "${OUTPUT_LIBDIR}/${RELATIVE_OUTDIR}")
            set_target_properties(${TARGET_TARGETNAME} PROPERTIES "LIBRARY_OUTPUT_DIRECTORY_${CONF}" "${OUTPUT_LIBDIR}/${RELATIVE_OUTDIR}")
        endforeach()
    endmacro()

    SET_OUTPUT_DIR_PROPERTY(vsg "")

    option(ENABLE_MP_FLAG "Turning on this option will add the multi-processor flag in MSVC for VSG and it's included projects" ON)

    if(ENABLE_MP_FLAG)
        target_compile_options(vsg PRIVATE "/MP")
    endif()

    option(DISABLE_CHECKED_ITERATORS "Turning on this option will disable checked iterators in debug mode for visual studio. All imported projects must adhere to this requirement" OFF)

    if(DISABLE_CHECKED_ITERATORS)
        target_compile_definitions(vsg PUBLIC "_ITERATOR_DEBUG_LEVEL=0")
    endif()

endif()


# place header and source files into group folders to help IDE's present the files in a logical manner
function(ASSIGN_SOURCE_GROUPS GROUP_NAME ROOT_FOLDER)
    foreach(FILE IN ITEMS ${ARGN})
        if (IS_ABSOLUTE "${FILE}")
            file(RELATIVE_PATH RELATIVE_SOURCE "${ROOT_FOLDER}" "${FILE}")
        else()
            set(RELATIVE_SOURCE "${FILE}")
        endif()
        get_filename_component(SOURCE_PATH "${RELATIVE_SOURCE}" PATH)
        string(REPLACE "/" "\\" SOURCE_PATH_MSVC "${SOURCE_PATH}")
        source_group("${GROUP_NAME}\\${SOURCE_PATH_MSVC}" FILES "${FILE}")
    endforeach()
endfunction(ASSIGN_SOURCE_GROUPS)

# enable folders for MSVC
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

# group source files and headers
ASSIGN_SOURCE_GROUPS("Source Files" "${VSG_SOURCE_DIR}" ${SOURCES})
ASSIGN_SOURCE_GROUPS("Header Files" "${VSG_SOURCE_DIR}/include/vsg" ${HEADERS})


# set up versions and position independent code that is required for unix platforms
set_property(TARGET vsg PROPERTY VERSION ${VSG_VERSION_MAJOR}.${VSG_VERSION_MINOR}.${VSG_VERSION_PATCH})
set_property(TARGET vsg PROPERTY SOVERSION ${VSG_SOVERSION})
set_property(TARGET vsg PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET vsg PROPERTY CXX_STANDARD 17)

target_compile_definitions(vsg PRIVATE ${EXTRA_DEFINES})
target_include_directories(vsg PUBLIC $<BUILD_INTERFACE:${VSG_SOURCE_DIR}/include> $<BUILD_INTERFACE:${VSG_BINARY_DIR}/include>)

target_link_libraries(vsg ${LIBRARIES})

if (BUILD_SHARED_LIBS)
    target_compile_definitions(vsg INTERFACE VSG_SHARED_LIBRARY)
endif()

# install headers
install(DIRECTORY ${VSG_SOURCE_DIR}/include/vsg DESTINATION include)

if (NOT(${VSG_BINARY_DIR} STREQUAL ${VSG_SOURCE_DIR}))
    install(DIRECTORY ${VSG_BINARY_DIR}/include/vsg DESTINATION include)
endif()

# install lib
install(TARGETS vsg ${INSTALL_TARGETS_DEFAULT_FLAGS})

# instal cmake config files.
install(
    FILES
        "${VSG_SOURCE_DIR}/cmake/FindVulkan.cmake"
        "${VSG_SOURCE_DIR}/cmake/uninstall.cmake"
        "${VSG_SOURCE_DIR}/cmake/vsgMacros.cmake"
    DESTINATION
        ${CMAKE_INSTALL_LIBDIR}/cmake/vsg
)

if (glslang_FOUND)
    install(
        FILES vsg_glslangConfig.cmake
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/vsg
    )
endif()

vsg_add_cmake_support_files(
    CONFIG_TEMPLATE vsgConfig.cmake.in
)
