# 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/IntrusiveAllocator.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/common.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/CoordinateFrame.cpp
    nodes/Transform.cpp
    nodes/VertexDraw.cpp
    nodes/VertexIndexDraw.cpp
    nodes/DepthSorted.cpp
    nodes/Layer.cpp
    nodes/Bin.cpp
    nodes/Switch.cpp
    nodes/StateGroup.cpp
    nodes/TileDatabase.cpp
    nodes/InstrumentationNode.cpp
    nodes/RegionOfInterest.cpp

    lighting/Light.cpp
    lighting/AmbientLight.cpp
    lighting/DirectionalLight.cpp
    lighting/PointLight.cpp
    lighting/SpotLight.cpp
    lighting/ShadowSettings.cpp
    lighting/HardShadows.cpp
    lighting/SoftShadows.cpp
    lighting/PercentageCloserSoftShadows.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/ClearImage.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/SetPrimitiveTopology.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/txt.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
    threading/DeleteQueue.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

    meshshaders/DrawMeshTasks.cpp
    meshshaders/DrawMeshTasksIndirect.cpp
    meshshaders/DrawMeshTasksIndirectCount.cpp

    animation/Animation.cpp
    animation/AnimationGroup.cpp
    animation/AnimationManager.cpp
    animation/CameraAnimationHandler.cpp
    animation/FindAnimations.cpp
    animation/Joint.cpp
    animation/JointSampler.cpp
    animation/MorphSampler.cpp
    animation/CameraSampler.cpp
    animation/TransformSampler.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
    ui/Keyboard.cpp

    vk/CommandBuffer.cpp
    vk/CommandPool.cpp
    vk/Context.cpp
    vk/DescriptorPool.cpp
    vk/DescriptorPools.cpp
    vk/Device.cpp
    vk/DeviceFeatures.cpp
    vk/DeviceMemory.cpp
    vk/DeviceExtensions.cpp
    vk/Fence.cpp
    vk/Framebuffer.cpp
    vk/Instance.cpp
    vk/InstanceExtensions.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/CommandLine.cpp
    utils/Builder.cpp
    utils/SharedObjects.cpp
    utils/ShaderSet.cpp
    utils/GraphicsPipelineConfigurator.cpp
    utils/ShaderCompiler.cpp
    utils/ComputeBounds.cpp
    utils/Intersector.cpp
    utils/Instrumentation.cpp
    utils/GpuAnnotation.cpp
    utils/LineSegmentIntersector.cpp
    utils/PolytopeIntersector.cpp
    utils/LoadPagedLOD.cpp
    utils/FindDynamicObjects.cpp
    utils/PropagateDynamicObjects.cpp
    utils/Profiler.cpp
)

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

if (${VSG_SUPPORTS_ShaderCompiler})
    list(INSERT LIBRARIES 0 PRIVATE glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV)
endif()

# Check for std::atomic
if(NOT MSVC AND NOT ANDROID AND NOT APPLE)
  include(CheckCXXSourceCompiles)

  check_cxx_source_compiles("
    #include <atomic>
    int main()
    {
        std::atomic_uint64_t a64(1);
        auto orig_value = a64.load();

        uint64_t new_value = 1;
        auto r = a64.compare_exchange_weak(orig_value, new_value);

        return 0;
    }"
    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 (VSG_SUPPORTS_Windowing)
    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/xcb/Xcb_Window.cpp)
        set(LIBRARIES ${LIBRARIES} PRIVATE PkgConfig::xcb)
    endif()
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 its 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)
if(WIN32)
    set_property(TARGET vsg PROPERTY RUNTIME_OUTPUT_NAME vsg-${VSG_SOVERSION})
endif()

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 ${VSG_INSTALL_TARGETS_DEFAULT_FLAGS})

# install 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
)

vsg_add_cmake_support_files(
    CONFIG_TEMPLATE vsgConfig.cmake.in
)
