# Tested generators:
# Command line makefile generator
# "MinGW Makefiles": MSYS2/Mingw32 GCC 8.3, 10.3 build
# IDE project file generators
# "Visual Studio 15 2017" optional platform generator Win32 and x64
# "Visual Studio 16 2019" optional platform generator Win32 and x64. optional toolset "v141_xp"
# "Visual Studio 17 2022" optional platform generator Win32 and x64. optional toolset "v141_xp" or "clangcl"
# "Visual Studio 18 2026" optional platform generator Win32 and x64. optional toolset "v141_xp" or "clangcl"

# Intel C++ Compilers
# Minimum Cmake version for Intel C++ Compiler 2023: 3.22.3 (Win) 3.20 (Linux)
# Howto: https://www.intel.com/content/www/us/en/developer/articles/technical/using-oneapi-compilers-with-cmake-in-visual-studio.html
# "Intel(R) oneAPI DPC++ Compiler", two flavours. DPCPP is not compatible with Avisynth.
# - Intel® NextGen Compiler (in base kit, LLVM based): TOOLSET = "Intel C++ Compiler 2023", COMPILER EXE NAME = icx.exe
# - Intel® Classic Compiler (in extra HPC kit): TOOLSET = "Intel C++ Compiler 19.2", COMPILER EXE NAME = icl.exe
# CMake support files.
# - Info from: c:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\IntelDPCPP\ReadMe.txt
# - copy c:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\IntelDPCPP\IntelDPCPPConfig.cmake to c:\Program Files\CMake\share\cmake-3.20\Modules\
# CMake GUI: 
# - Generator: "Visual Studio 17 2022"
# - Optional toolset to use (-T option): 
#   For LLVM based icx: Intel C++ Compiler 2023 (or earlier 2022, 2021)
#   For classic icl: Intel C++ Compiler 19.2
# - Specify native compilers: browse for the appropriate compiler executable path.
#   icx: C:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\bin\icx.exe
#   icl: C:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\bin\intel64\icl.exe
# If you have errors like "xilink: : error : Assertion failed (shared/driver/drvutils.c, line 312" then
# as a workaround you must copy clang.exe (by default it is located in C:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\bin)
# to the folder beside xilink (for x64 configuration it is in C:\Program Files (x86)\Intel\oneAPI\compiler\latest\windows\bin\intel64).
# Successful log looks like:
#   The CXX compiler identification is IntelLLVM 2023.0.0 with MSVC-like command-line
#   Check for working CXX compiler: C:/Program Files (x86)/Intel/oneAPI/compiler/2023.0.0/windows/bin/icx.exe - skipped
# or
#   The CXX compiler identification is IntelLLVM 2021.4.0 with MSVC-like command-line
#   Check for working CXX compiler: C:/Program Files (x86)/Intel/oneAPI/compiler/2021.4.0/windows/bin/icx.exe
# or
#   The CXX compiler identification is Intel 2021.4.0.20210910
#   Check for working CXX compiler: C:/Program Files (x86)/Intel/oneAPI/compiler/2021.4.0/windows/bin/intel64/icl.exe
#
# command line:
#   run "C:\Program Files (x86)\Intel\oneAPI\setvars.bat"   to configure the environment
#   cmake -T "Intel C++ Compiler 2023" -DCMAKE_CXX_COMPILER="icx.exe" ../ 
#   or 
#   cmake -T "Intel C++ Compiler 19.2" -DCMAKE_CXX_COMPILER="icl.exe" ../ 

# "Visual Studio 16 2019" + LLVM 8.0 (clang) optional platform generator Win32 and x64

CMAKE_MINIMUM_REQUIRED( VERSION 3.6.2...3.11 )
# VS2019: 3.14.1
# Intel 2023: 3.22.3 (Win) 3.20 (Linux)
# VS2022: 3.21
# VS2026: 4.2

# Get PROJECT_VERSION property from 'avs_core/core/version.h.in'
file(READ "avs_core/core/version.h.in" versioning)

string(REGEX MATCH "AVS_MAJOR_VER[ \\t]+([0-9]*)" _ ${versioning})
set(version_major ${CMAKE_MATCH_1})
string(REGEX MATCH "AVS_MINOR_VER[ \\t]+([0-9]*)" _ ${versioning})
set(version_minor ${CMAKE_MATCH_1})
string(REGEX MATCH "AVS_BUGFIX_VER[ \\t]+([0-9]*)" _ ${versioning})
set(version_bugfix ${CMAKE_MATCH_1})
# Combine version variables for use in the project command
set(PROJECT_VERSION_STRING "${version_major}.${version_minor}.${version_bugfix}")

# Get AVISYNTH_INTERFACE_VERSION from avs_core/include/avisynth.h
file(READ "avs_core/include/avisynth.h" versioning)
string(REGEX MATCH "AVISYNTH_INTERFACE_VERSION = ([0-9]*)" _ ${versioning})
set(AVISYNTH_INTERFACE_VERSION ${CMAKE_MATCH_1})

option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones." ON)
if(NOT ${BUILD_SHARED_LIBS})
  message(WARNING "You must satisfy the conditions of the GPL license when linking against the AviSynth library.")
endif()

option(HEADERS_ONLY "Install only the Headers" ${INSTALL_ONLY_HEADER})
if(${INSTALL_ONLY_HEADER})
  set(INSTALL_ONLY_HEADER OFF)
endif()

if(NOT HEADERS_ONLY)

  project("AviSynth+" VERSION ${PROJECT_VERSION_STRING} LANGUAGES CXX)

  # message("Compiler ID: ${CMAKE_CXX_COMPILER_ID} ") 

  include(GNUInstallDirs)

  # Avoid uselessly linking to unused libraries
  set(CMAKE_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)
  set(CMAKE_C_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)
  set(CMAKE_CXX_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)

  # We require C++17 or higher.
  if(CMAKE_VERSION VERSION_GREATER 3.7)
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
    set(CMAKE_CXX_EXTENSIONS FALSE)
  endif()

  # Detect Intel processors and turn Intel SIMD on or off automatically.
  # Detect AArch64 processors and turn AArch64 NEON SIMD on or off automatically.
  # Old logic relied on the host processor: ${CMAKE_SYSTEM_PROCESSOR}

  set(INTEL_SIMD "OFF")
  set(NEON_SIMD "OFF")
  # Use a list of known Intel-compatible/aarch64 architecture names for the default ON state.
  set(INTEL_ARCH_NAMES "win32" "x64" "x86" "i386" "amd64" "x86_64" "i686")
  set(ARM_ARCH_NAMES "arm64" "aarch64")

  # Check the TARGET architecture using the most reliable variables (CMAKE_GENERATOR_PLATFORM and PLATFORMID_LOWER)
  string(TOLOWER "${PLATFORMID}" PLATFORMID_LOWER)
  string(TOLOWER "${CMAKE_GENERATOR_PLATFORM}" GEN_PLATFORM_LOWER) # Often holds x64, ARM64, etc.
  string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" HOST_ARCH_LOWER) # ninja with ative aarch64 gcc: probably only this exists

  # --- DEBUG OUTPUT START ---
  # message(STATUS "--- SIMD Detection Variables ---")
  # message(STATUS "CMAKE_SYSTEM_PROCESSOR (Host Arch): ${CMAKE_SYSTEM_PROCESSOR}")
  # message(STATUS "Host Arch Lower: ${HOST_ARCH_LOWER}")
  # message(STATUS "PLATFORMID_LOWER (VS Target Platform): ${PLATFORMID_LOWER}")
  # message(STATUS "CMAKE_GENERATOR_PLATFORM (Generator Target): ${CMAKE_GENERATOR_PLATFORM}")
  # message(STATUS "INTEL_ARCH_NAMES: ${INTEL_ARCH_NAMES}")
  # message(STATUS "------------------------------------")
  # --- DEBUG OUTPUT END ---
  # e.g. ARM64 cross-compile on x64 machine:
  # CMAKE_SYSTEM_PROCESSOR (Host Arch): AMD64
  # Host Arch Lower: amd64
  # PLATFORMID_LOWER (VS Target Platform): 
  # CMAKE_GENERATOR_PLATFORM (Generator Target): ARM64  

  # check aarch64 variants
  list(FIND ARM_ARCH_NAMES "${HOST_ARCH_LOWER}" _found_arm_host) # New check
  list(FIND ARM_ARCH_NAMES "${PLATFORMID_LOWER}" _found_arm_platform_id)
  list(FIND ARM_ARCH_NAMES "${GEN_PLATFORM_LOWER}" _found_arm_gen)
  # check intel variants
  list(FIND INTEL_ARCH_NAMES "${HOST_ARCH_LOWER}" _found_arch)
  list(FIND INTEL_ARCH_NAMES "${PLATFORMID_LOWER}" _found_target_platform_id)
  list(FIND INTEL_ARCH_NAMES "${GEN_PLATFORM_LOWER}" _found_target_gen)

  # 1. Check if the target platform is explicitly known non-Intel (ARM64, AARCH64)
  if(_found_arm_platform_id GREATER -1 OR _found_arm_gen GREATER -1 OR _found_arm_host GREATER -1)
      set(INTEL_SIMD "OFF")
      set(NEON_SIMD "ON")
      message(STATUS "Target is aarch64, turn NEON_SIMD ON.")
  else()
      # 2. Inclusion Check: We are NOT targeting ARM64. 
      
      # Define a boolean check: Did we find a match in the explicit target variables OR the host architecture?
      if(_found_target_gen GREATER -1 OR _found_target_platform_id GREATER -1)
          # Found a match in a generator-set variable (e.g., Win32, x64 when explicitly chosen)
          set(INTEL_SIMD "ON")
          message(STATUS "Target architecture is explicitly set and Intel-compatible, INTEL_SIMD set ON.")
      elseif(_found_arch GREATER -1 AND "${GEN_PLATFORM_LOWER}" STREQUAL "")
          # FALLBACK: Target platform is NOT set (Default Configuration), but the Host is Intel-compatible.
          # The default target platform for a multi-config generator on a bare x64 host is x64.
          set(INTEL_SIMD "ON")
          message(STATUS "Target platform not specified; defaulting to Host (${CMAKE_SYSTEM_PROCESSOR}), INTEL_SIMD set ON.")
      endif()
  endif()
  # message(STATUS "Final INTEL_SIMD initial assumption: ${INTEL_SIMD}")
  option(ENABLE_INTEL_SIMD "Enable SIMD intrinsics for Intel processors" "${INTEL_SIMD}")
  option(ENABLE_NEON_SIMD "Enable SIMD intrinsics for AArch64 processors" "${NEON_SIMD}")

  option(ENABLE_PLUGINS "Build set of default external plugins" ON)
  set(USER_AVS_PLUGINDIR_LOCATION ".local/lib/avisynth" CACHE STRING "Override path for user-local plugins, with $HOME omitted (default: .local/lib/avisynth)")
  option(ENABLE_CUDA "Enable CUDA support" OFF)
  set(CORE_PLUGIN_INSTALL_PATH "${CMAKE_INSTALL_FULL_LIBDIR}" CACHE STRING "Set system plugin install parent directory (default: value of CMAKE_INSTALL_FULL_LIBDIR)")

  if(CMAKE_VERSION VERSION_GREATER 3.9)
    get_cmake_property(MULTI_CONFIG GENERATOR_IS_MULTI_CONFIG)
    if(MULTI_CONFIG)
      if(CMAKE_CONFIGURATION_TYPES)
        set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo)
        set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "Reset the configurations to what we need" FORCE)
      endif()
      message("-- Build type: Multi-configuration (${CMAKE_CONFIGURATION_TYPES})")
    else()
      # When CMAKE_BUILD_TYPE is not defined, CMake defaults to a simple -O0 configuration, no compiler optimizations
      # and no debug symbols. For single-configuration generators (Makefiles, Ninja, etc.) we can make Release the
      # assumed default if it isn't explicitly set by the user. Multi-config generators like Visual Studio ignore
      # CMAKE_BUILD_TYPE.

      # Unfortunately, this is not visible in CMakeCache, but it can be seen to take effect in build.ninja or running
      # make with VERBOSE=1 adding the appropriate Release flags.
      if(NOT CMAKE_BUILD_TYPE)
        set(CMAKE_BUILD_TYPE "Release")
      endif()
      message("-- Build type: ${CMAKE_BUILD_TYPE}")
    endif()
  endif()

  # Use this one to be safe:
  # Check for the Visual Studio generator OR the MSVC compiler/toolchain on Windows 
  # (which includes Ninja with cl.exe).
  # We use the MSVC variable to ensure the configuration block runs when cl.exe is detected.
  if( CMAKE_GENERATOR MATCHES "Visual Studio" OR (MSVC AND WIN32) )
  ## IF( MSVC ) # Check for Visual Studio

    #1910-1919 = VS 15.0 (v141 toolset) Visual Studio 2017
    #1920-1929 = VS 16.0 (v142 toolset) Visual Studio 2019
    #1930-1949 = VS 17.x (v143 toolset) Visual Studio 2022
    #  ( 1940-1949 = VS v17.10+: Toolset v143 (Still!) | Compiler 19.4x)
    #1950-1959 = VS 18.0 (v145 toolset) Visual Studio 2026
    
    # --- Determine MSVC target platform, Ninja makes a bit more work for us
    # Since CMAKE_VS_PLATFORM_NAME is empty, only valid for Visual Studio generators.

    # Detect the target platform based on the most reliable variables for MSVC/Ninja:
    # 1. start with the VS Generator's specific variable. If it's not set, it will be empty.
    set(TARGET_PLATFORM_TO_CHECK "${CMAKE_VS_PLATFORM_NAME}")

    # message(STATUS "DEBUG: CMAKE_VS_PLATFORM_NAME = '${CMAKE_VS_PLATFORM_NAME}'")
    # message(STATUS "DEBUG: CMAKE_GENERATOR_PLATFORM = '${CMAKE_GENERATOR_PLATFORM}'")
    # message(STATUS "DEBUG: CMAKE_SIZEOF_VOID_P = '${CMAKE_SIZEOF_VOID_P}'")

    # 2. If CMAKE_VS_PLATFORM_NAME is empty (e.g., Ninja generator), try to infer from pointer size.
    if(NOT TARGET_PLATFORM_TO_CHECK)
        # 3. Check the pointer size (most reliable indicator for x64/Win32)
        if(CMAKE_SIZEOF_VOID_P EQUAL 8)
            # 64-bit target
            set(TARGET_PLATFORM_TO_CHECK "x64")
        elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
            # 32-bit target
            set(TARGET_PLATFORM_TO_CHECK "Win32")
        endif()
    endif()

    # 4. final attempt: If CMAKE_GENERATOR_PLATFORM has a value and TARGET_PLATFORM_TO_CHECK is still empty
    if(NOT TARGET_PLATFORM_TO_CHECK AND CMAKE_GENERATOR_PLATFORM)
        set(TARGET_PLATFORM_TO_CHECK "${CMAKE_GENERATOR_PLATFORM}")
    endif()
    
    # detect if the target is for x86
    # PLATFORMID_LOWER is just a lowercase copy of CMAKE_VS_PLATFORM_NAME
    string(TOLOWER "${TARGET_PLATFORM_TO_CHECK}" PLATFORMID_LOWER)
    # message("-- PLATFORMID_LOWER: ${PLATFORMID_LOWER}")
 
    if(("${PLATFORMID_LOWER}" STREQUAL "win32") OR
       ("${PLATFORMID_LOWER}" STREQUAL "x64") OR
       ("${PLATFORMID_LOWER}" STREQUAL "arm64"))   # allow arm64 targets
        set(SUPPORTED_MSVC_PLATFORM "ON")
    else()
        set(SUPPORTED_MSVC_PLATFORM "OFF")
    endif()
    
    if(SUPPORTED_MSVC_PLATFORM STREQUAL "OFF")
        message(FATAL_ERROR "MSVC/ClangCL only supported on Win32, x64, or ARM64 targets. Use MinGW (llvm-mingw or gcc). Unsupported target: ${CMAKE_VS_PLATFORM_NAME}.")
    endif()

#[[
    # check to disable ARM64 by default
    # PF: re-allowed; VS2026 can build ARM64 even with LLVM
    if(("${PLATFORMID_LOWER}" STREQUAL "arm64") AND (NOT DEFINED ENABLE_ARM64_WITH_MSVC_TESTING))
        # This FATAL_ERROR can be commented out or protected by the ENABLE_ARM64_TESTING cache variable.
        # To enable the build, define -DENABLE_ARM64_TESTING=ON when running cmake.
        message(FATAL_ERROR "MSVC ARM64 platform is currently disabled by default. Define ENABLE_ARM64_WITH_MSVC_TESTING=ON to proceed or use MinGW (llvm-mingw or gcc).")
    endif()
#]]

    IF( MSVC_VERSION VERSION_LESS 1910 )
      MESSAGE(FATAL_ERROR "Visual C++ 2017 or newer required.")
    ENDIF()

    # Milestones
    # C++17 COMPLETENESS: Compiler versions 19.10-19.19 (VS 2017) had incomplete C++17 support.
    # Full C++17 support: starting with compiler version 19.20 (VS 2019 RTM).
    # C++20 COMPLETENESS: Compiler version 19.29 (VS 2019 v16.10+): full C++20 standard library support.
    # VS 2022 (v19.30+) is generally recommended for best C++20 stability and language feature compliance.
    
    # Damn XP support prevents defining a minimum.
    # When v141_xp toolset is used, MSVC_VERSION is 1916
    # So we temporarily inactivate the minimum version check:
    IF(FALSE)
        IF( MSVC_VERSION VERSION_LESS 1929 )
          message("--MSVC_VERSION is (${MSVC_VERSION})")
          MESSAGE(FATAL_ERROR "Visual C++ 2019 (v16.10+) or newer is required for C++20 support. Recommended: Visual Studio 2022.")
        ENDIF()
    ENDIF()

    file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/Output/plugins")
    file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/Output/system")
    file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/Output/c_api")

    IF(MSVC_IDE)
      message("MSVC_IDE support found, reported CMAKE_GENERATOR_TOOLSET is: ${CMAKE_GENERATOR_TOOLSET}")
      string( TOLOWER "${CMAKE_GENERATOR_TOOLSET}" cmake_gentoolset_lower)

      IF(cmake_gentoolset_lower STREQUAL "intel c++ compiler 2021" OR
      cmake_gentoolset_lower STREQUAL "intel c++ compiler 2022" OR
      cmake_gentoolset_lower STREQUAL "intel c++ compiler 2023" OR
      cmake_gentoolset_lower STREQUAL "intel c++ compiler 2024" OR
      cmake_gentoolset_lower STREQUAL "intel c++ compiler 2025" OR
      cmake_gentoolset_lower STREQUAL "intel c++ compiler 2026"
      )
        # IntelLLVM
        set(IntelLLVM_IN_VS "1")
      ELSEIF(cmake_gentoolset_lower STREQUAL "intel c++ compiler 19.2")
        # Intel Classic
        set(IntelClassic_IN_VS "1")
      ELSEIF(cmake_gentoolset_lower MATCHES "intel")
        MESSAGE(FATAL_ERROR "Possibly unknown or unsupported Intel compiler") # version update needed in the above lines
      ELSEIF(cmake_gentoolset_lower STREQUAL "llvm" OR cmake_gentoolset_lower STREQUAL "clangcl")
        if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")  # hope: always
          message("LLVM toolset was specified via -T. Compiler ID is: ${CMAKE_CXX_COMPILER_ID}; CMAKE_CXX_COMPILER_VERSION is: ${CMAKE_CXX_COMPILER_VERSION}")
          # Clang; 9.0.0
          # These are probably not supported when clang is downloaded as a ready-made binary: CLANG_VERSION_MAJOR CLANG_VERSION_MINOR CLANG_VERSION_STRING
          # string (REGEX REPLACE ".*clang version ([0-9]+\\.[0-9]+).*" "\\1" CLANG_VERSION_STRING ${clang_full_version_string})
          if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0.1 )
            MESSAGE(FATAL_ERROR "Clang 7.0.1 or newer required") # as of November 2021 actually we are using 12.0
          endif()
        endif()
        set(CLANG_IN_VS "1")
      ELSEIF(cmake_gentoolset_lower STREQUAL "v141_clang_c2")
          #1900 is reported
        message("v141_clang_c2 toolset was specified via -T. Reported MSVC_VERSION is: ${MSVC_VERSION}")
        message("May not work, try clangcl or LLVM")
        set(CLANG_IN_VS "1")
        # For LLVM Clang installed separately, specify llvm
        # Since Visual Studio 2019 v16.4, LLVM 9.0 or newer is integrated, for this use Toolset: clangcl
      ENDIF()

      option(WINXP_SUPPORT "Make binaries compatible with Windows XP and Vista" OFF)
      if(WINXP_SUPPORT)
        # We want our project to also run on Windows XP
        # Not for LLVM: Clang stopped XP support in 2016
        IF(NOT CLANG_IN_VS STREQUAL "1")
          # Setting CMAKE_GENERATOR_TOOLSET here has no effect, only when passed (-T option) or set directly, so we just check it
          IF(CMAKE_GENERATOR_TOOLSET STREQUAL "v141_xp")
              # v141_xp is still available in Visual Studio 2022 and 2026.
              message("CMAKE_GENERATOR_TOOLSET is XP compatible: ${CMAKE_GENERATOR_TOOLSET}, extra XP options added")
              # https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics
              # later done: add_definitions("/Zc:threadSafeInit-")
          ELSE()
              message(FATAL_ERROR "For XP you must specify v141_xp toolset with -T option (or 'Optional toolset to use' in CMake GUI)!")
          ENDIF()
        ENDIF()
      endif()
    ENDIF()

    if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
        set(CLANG_IN_VS "1")
    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
        set(IntelLLVM_IN_VS "1")
    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
        set(IntelClassic_IN_VS "1")
    endif()

    IF(IntelClassic_IN_VS STREQUAL "1")
        # Intel C++ Compiler 19.2
        message("Intel Classic chosen, setting additional flags")
        set(DELETE_THIS "/std:c++17") # if it would co-exist with /Qstd=c++17
        STRING( REPLACE "${DELETE_THIS}" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
        STRING( REPLACE "${DELETE_THIS}" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
        
        # Workaround as of 19.2: 
        # Statically link with /MT instead of /MD against linker error message
        # "unresolved external symbol wmemcmp referenced in function std::filesystem ... lexically_normal"
        # Linker bug?
        set(CompilerFlags
                CMAKE_CXX_FLAGS
                CMAKE_CXX_FLAGS_DEBUG
                CMAKE_CXX_FLAGS_RELEASE
                CMAKE_CXX_FLAGS_MINSIZEREL
                CMAKE_CXX_FLAGS_RELWITHDEBINFO
                CMAKE_C_FLAGS
                CMAKE_C_FLAGS_DEBUG
                CMAKE_C_FLAGS_RELEASE
                CMAKE_C_FLAGS_MINSIZEREL
                CMAKE_C_FLAGS_RELWITHDEBINFO
                )
        foreach(CompilerFlag ${CompilerFlags})
            string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
            set(${CompilerFlag} "${${CompilerFlag}}" CACHE STRING "msvc compiler flags" FORCE)
            message("MSVC flags: ${CompilerFlag}:${${CompilerFlag}}")
        endforeach()    
    ELSE()
        # Plain MSVC branch continues; per-target options are applied later
    ENDIF()

    # CPU_ARCH can be overridden with the corresponding values when using MSVC:
    # IA32 (disabled),
    # SSE (Pentium III and higher, 1999),
    # SSE2 (Pentium 4 and higher, 2000/2001),
    # AVX (Sandy Bridge and higher, 2011),
    # AVX2 (Haswell and higher, 2013)
    if(CMAKE_SIZEOF_VOID_P EQUAL 4)
        set(MSVC_CPU_ARCH "SSE2" CACHE STRING "Set MSVC architecture optimization level (default: SSE2)")
        # /arch: applied later per-target
    endif()

  ELSE()
    # not MS Visual Studio IDE
    if(WIN32)
      SET( CMAKE_SHARED_LINKER_FLAGS "-Wl,--enable-stdcall-fixup" )
      # __CRT__NO_INLINE: moved to per-target definitions
    elseif(APPLE)
      # macOS uses Clang's linker, doesn't like --no-undefined
      SET( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error" )
    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
      # make sure there are no undefined symbols
      SET( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" )
    endif()

  ENDIF()

  add_subdirectory("avs_core")

  # Start tracking targets before the plugins subdirectory is processed
  get_property(_targets_before_plugins GLOBAL PROPERTY BUILT_TARGETS) 
  
  if(ENABLE_PLUGINS)
    add_subdirectory("plugins")
  endif()
  
  # End tracking targets and apply fixes here
  get_property(_targets_after_plugins GLOBAL PROPERTY BUILT_TARGETS)
  
  # Calculate targets added in the plugins subdirectory
  list(REMOVE_ITEM _targets_after_plugins ${_targets_before_plugins})
  set(NEW_PLUGIN_TARGETS ${_targets_after_plugins})
  
  # Combine AvsCore and the newly discovered plugins into one list for fixing
  set(ALL_AFFECTED_TARGETS)
  if(TARGET AvsCore)
      list(APPEND ALL_AFFECTED_TARGETS AvsCore)
  endif()
  list(APPEND ALL_AFFECTED_TARGETS ${NEW_PLUGIN_TARGETS})
  
  # -----------------------------------------------------------------------
  # Per-target, modern & safe application of options/defines (CORE and PLUGINS)
  # -----------------------------------------------------------------------
  foreach(AFFECTED_TARGET ${ALL_AFFECTED_TARGETS})
    if(TARGET ${AFFECTED_TARGET})
      message(STATUS "Applying compiler fixes to target: ${AFFECTED_TARGET}")

      # Check if the target is a library or executable (exclude INTERFACE targets)
      get_target_property(_target_type ${AFFECTED_TARGET} TYPE)
      if(NOT (
          ${_target_type} STREQUAL "SHARED_LIBRARY" OR 
          ${_target_type} STREQUAL "STATIC_LIBRARY" OR
          ${_target_type} STREQUAL "MODULE_LIBRARY"
      ))
        # Skip if it's not a compileable target (e.g., if it was an interface target)
        continue()
      endif()

      # Use a compound check: If the generator is Visual Studio, or the compiler ID 
      # is MSVC AND we are on Windows, run the MSVC-specific logic.
      if( CMAKE_GENERATOR MATCHES "Visual Studio" OR (MSVC AND WIN32) ) 
      # Using only if(MSVC) will fail with MinGW if the compiler ID is MSVC but the generator is not VS.
      # This robust check ensures that the MSVC block only runs in an MSVC environment.

        # Handle ARM64 soft intrinsics if requested (for AvsCore logic)
        # Was experimental, never true
        if("${PLATFORMID}" STREQUAL "ARM64" AND ENABLE_INTEL_SIMD)
          target_compile_definitions(${AFFECTED_TARGET} PRIVATE USE_SOFT_INTRINSICS)
        endif()
        
        # Needed to properly handle __has_include(<filesystem>) in avs_core/filesystem.h
        # See note in filesystem/README.md
        target_compile_options(${AFFECTED_TARGET} PRIVATE /Zc:__cplusplus)
        
        # Prevent VC++ secure CRT warnings, and add __SSE2__ flag. (Why?)
        target_compile_definitions(${AFFECTED_TARGET} PRIVATE
          _CRT_SECURE_NO_WARNINGS
          _SECURE_SCL=0
          _CRTDBG_MAP_ALLOC
          __SSE2__
        )

        # INTEL intrinsics master switch (if enabled)
        if(ENABLE_INTEL_SIMD)
          target_compile_definitions(${AFFECTED_TARGET} PRIVATE INTEL_INTRINSICS)
        endif()
        # NEON intrinsics master switch (if enabled)
        if(ENABLE_NEON_SIMD)
          target_compile_definitions(${AFFECTED_TARGET} PRIVATE NEON_INTRINSICS)
        endif()
        
        # FIXME: With Ninja, we can see cl : Command line warning D9025 : overriding '/EHs' with '/EHa'
        
        # Compiler-specific options for IntelLLVM / Clang
        if(CLANG_IN_VS STREQUAL "1")
          target_compile_options(${AFFECTED_TARGET} PRIVATE /EHa -Wno-inconsistent-missing-override)
          target_compile_options(${AFFECTED_TARGET} PRIVATE -Wno-unused-function -Wno-reorder -Wno-unused-value -Wno-gcc-compat)
        elseif(IntelLLVM_IN_VS STREQUAL "1")
          target_compile_options(${AFFECTED_TARGET} PRIVATE -Wno-inconsistent-missing-override /EHa /fp:precise)
          target_compile_options(${AFFECTED_TARGET} PRIVATE -Wno-unused-function -Wno-reorder -Wno-unused-value -Wno-gcc-compat)
        elseif(IntelClassic_IN_VS STREQUAL "1")
          target_compile_options(${AFFECTED_TARGET} PRIVATE /EHa)
        else()
          # Plain MSVC
          target_compile_options(${AFFECTED_TARGET} PRIVATE /EHa)
          if (NOT (MSVC_VERSION LESS 1930)) # VS2022+
            target_compile_options(${AFFECTED_TARGET} PRIVATE /fp:contract)
          endif()
        endif()
        
        # Enable standards-conformance mode for MSVC compilers that support this
        # flag (Visual C++ 2017 and later). Default. DirectShowSource will remove if needed.
        # The headers in the XP-side SDK also have errors if built in conformance mode,
        # so if we're building for XP, don't turn that on.
        
        # Plus check: Skip /permissive- if the target (e.g. DirectShowSource) requests it.
        # Filled in plugin's CMakeLists.txt
        get_target_property(_skip_permissive ${AFFECTED_TARGET} SKIP_PERMISSIVE_FLAG)

        if(NOT _skip_permissive) # old DirectShowSource
          if (NOT WINXP_SUPPORT)
            if (NOT (MSVC_VERSION LESS 1910))
              target_compile_options(${AFFECTED_TARGET} PRIVATE /permissive-)
            endif()
          endif()
        endif()

        # Check if XP support is enabled AND if the XP toolset is used (v141_xp)
        if (WINXP_SUPPORT)
          if (CMAKE_GENERATOR_TOOLSET STREQUAL "v141_xp")
            # C++11 static initialization fix for XP/Server 2003
            target_compile_options(${AFFECTED_TARGET} PRIVATE "/Zc:threadSafeInit-")
          endif()
        endif()
        
        # 32-bit arch tuning
        if(CMAKE_SIZEOF_VOID_P EQUAL 4)
          target_compile_options(${AFFECTED_TARGET} PRIVATE /arch:${MSVC_CPU_ARCH})
        endif()
      else()
        # CMAKE_CXX_STANDARD doesn't cover the use-case of pre-final C++17 support,
        # but I'd assume most setups with a new enough version of CMake to use
        # CMAKE_CXX_STANDARD 17 would also be running a version of GCC/Clang new enough
        # to not need this. So this will most likely only ever be used by setups running
        # older versions of CMake; regardless, it shouldn't be necessary to force a
        # CMAKE_VERSION check on this part unless the mere presence of CMAKE_CXX_STANDARD 17
        # ends up causing problems for the older compilers here.
        # Legacy C++17 support for older GCC/Clang
        if( ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU")  AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)) OR
            ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)) )
            target_compile_options(${AFFECTED_TARGET} PRIVATE -std=c++1z)
        endif()
    
        if(ENABLE_INTEL_SIMD)
          target_compile_options(${AFFECTED_TARGET} PRIVATE -msse2)
          target_compile_definitions(${AFFECTED_TARGET} PRIVATE INTEL_INTRINSICS)
        endif()
        if(ENABLE_NEON_SIMD)
          target_compile_definitions(${AFFECTED_TARGET} PRIVATE NEON_INTRINSICS)
        endif()
        if(WIN32)
          target_compile_definitions(${AFFECTED_TARGET} PRIVATE __CRT__NO_INLINE=1)
        endif()
      endif()
    endif()
  endforeach()
  

else()
  # HEADERS_ONLY is ON
  project(AviSynth-Headers VERSION ${PROJECT_VERSION_STRING} LANGUAGES CXX)
  message(STATUS "Install Only Headers: ON")

  add_library(${PROJECT_NAME} INTERFACE)
  target_include_directories(${PROJECT_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/avs_core/include")
  add_library(AviSynth::Headers ALIAS ${PROJECT_NAME})

  # Determine target architecture
  include("${CMAKE_CURRENT_LIST_DIR}/avs_core/TargetArch.cmake")
  target_architecture(AVS_ARCH)
  CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/avs_core/core/arch.h.in ${CMAKE_CURRENT_BINARY_DIR}/arch.h @ONLY)

  # Dynamically generate the sequential version info from Git
  # Based on the example here: http://www.cmake.org/pipermail/cmake/2010-July/038015.html
  FIND_PACKAGE(Git)
  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
  ADD_CUSTOM_TARGET(
    VersionGen ALL
    ${CMAKE_COMMAND} -D SRC=${CMAKE_CURRENT_SOURCE_DIR}/avs_core/core/version.h.in
                      -D DST=${CMAKE_CURRENT_BINARY_DIR}/version.h
                      -D GIT=${GIT_EXECUTABLE}
                      -D REPO=${CMAKE_SOURCE_DIR}
                      -P ${CMAKE_CURRENT_SOURCE_DIR}/avs_core/Version.cmake
  )

  include(GNUInstallDirs)

  install(
    FILES ${CMAKE_CURRENT_SOURCE_DIR}/avs_core/include/avisynth.h
          ${CMAKE_CURRENT_SOURCE_DIR}/avs_core/include/avisynth_c.h
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/avisynth
  )

  install(
    DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/avs_core/include/avs
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/avisynth
  )

  install(
    FILES "${CMAKE_CURRENT_BINARY_DIR}/version.h"
          "${CMAKE_CURRENT_BINARY_DIR}/arch.h"
    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/avisynth/avs"
  )


endif()

# uninstall target
configure_file(
  "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
  "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
  IMMEDIATE @ONLY)

add_custom_target(uninstall
  COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)