#flag used to emit warnings only once
set(SANITIZERS_PROCESSED false)

if(NOT WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_BUILD_TYPE MATCHES "(Debug|DebugFast)")
	set(SANITIZERS none CACHE STRING "Clang C++ Sanitizers. Possible values: none, address, undefined, memory, thread. Can be combined using \";\" (e.g. \"address;undefined\")")
	set(MEMSAN_LIBCXX CACHE PATH "Instrumented C++ standard library location for MemorySanitizer")

	#normalize the option string
	string(TOLOWER "${SANITIZERS}" SANITIZERS)
	list(REMOVE_DUPLICATES SANITIZERS)
	list(REMOVE_ITEM SANITIZERS "")

	#if the list is empty, set it to "none"
	string(LENGTH "${SANITIZERS}" SANITIZERS_LEN)
	if(SANITIZERS_LEN EQUAL 0)
		list(APPEND SANITIZERS "none")
	endif()

	#someone wanted to use "none" with other sanitizers...? Must have been a mistake, correct it.
	list(LENGTH "${SANITIZERS}" SANITIZERS_COUNT)
	list(FIND "${SANITIZERS}" none SANITIZER_NONE_IDX)
	if((NOT SANITIZER_NONE_IDX EQUAL -1) AND SANITIZERS_COUNT GREATER 1)
		list(REMOVE_AT SANITIZERS ${SANITIZER_NONE_IDX})
	endif()

	set(SANITIZER_ADDRESS   false)
	set(SANITIZER_UNDEFINED false)
	set(SANITIZER_MEMORY    false)
	set(SANITIZER_THREAD    false)
endif(NOT WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_BUILD_TYPE MATCHES "(Debug|DebugFast)")

if(NOT WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_BUILD_TYPE MATCHES "(Debug|DebugFast)" AND NOT SANITIZERS STREQUAL "none")

	set(enabled_sanitizers "")
	foreach(sanitizer IN LISTS SANITIZERS)
		list(APPEND enabled_sanitizers "${sanitizer}")

		if(sanitizer STREQUAL "address")
			if(SANITIZER_MEMORY AND NOT SANITIZERS_PROCESSED)
				message(WARNING "MemorySanitizer and AddressSanitizer cannot be combined!")
			elseif(SANITIZER_THREAD AND NOT SANITIZERS_PROCESSED)
				message(WARNING "ThreadSanitizer and AddressSanitizer cannot be combined!")
			else()
				if(WIN32 AND NOT SANITIZERS_PROCESSED)
					message(WARNING "AddressSanitizer is not supported on Windows")
				else(WIN32 AND NOT SANITIZERS_PROCESSED)
					if (NOT SANITIZERS_PROCESSED)
						message(STATUS "Note: AddressSanitizer might cause undefined symbol issues in shared libraries when linked with certain linkers (e.g. gold)")
					endif(NOT SANITIZERS_PROCESSED)
					set(SANITIZER_ADDRESS true)
					add_compile_options(-fsanitize=address)
					link_libraries(-fsanitize=address)
					if(NOT (CMAKE_SYSTEM_NAME MATCHES "Linux" AND ARCH_X64) AND NOT SANITIZERS_PROCESSED)
						message(STATUS "Note: LeakSanitizer is available only on 64-bit Linux")
					endif(NOT (CMAKE_SYSTEM_NAME MATCHES "Linux" AND ARCH_X64) AND NOT SANITIZERS_PROCESSED)
				endif(WIN32 AND NOT SANITIZERS_PROCESSED)
			endif()

		elseif(sanitizer STREQUAL "undefined")
			set(SANITIZER_UNDEFINED true)
			add_compile_options(-fsanitize=undefined,unsigned-integer-overflow)
			link_libraries(-fsanitize=undefined,unsigned-integer-overflow)

		elseif(sanitizer STREQUAL "memory")
			if(SANITIZER_ADDRESS AND NOT SANITIZERS_PROCESSED)
				message(WARNING "AddressSanitizer and MemorySanitizer cannot be combined!")
			elseif(SANITIZER_THREAD AND NOT SANITIZERS_PROCESSED)
				message(WARNING "ThreadSanitizer and MemorySanitizer cannot be combined!")
			else()
				if(NOT (CMAKE_SYSTEM_NAME MATCHES "Linux" AND ARCH_X64) AND NOT SANITIZERS_PROCESSED)
					message(WARNING "MemorySanitizer is available only on 64-bit Linux. Ignoring.")
				else()
					#NOTE(vuko): disabled for standalone (we'd have to instrument Xorg and OpenGL to use it, etc.), consider using Valgrind instead
					set(SANITIZER_MEMORY true)
					add_compile_options(-fsanitize=memory -fsanitize-memory-track-origins=2 -stdlib=libc++)
					link_libraries(-fsanitize=memory -fsanitize-memory-track-origins=2 -stdlib=libc++)
					find_library(CXXLIB_FOUND c++ "${MEMSAN_LIBCXX}/lib")
					if((NOT EXISTS "${MEMSAN_LIBCXX}" OR NOT CXXLIB_FOUND) AND NOT SANITIZERS_PROCESSED)
						message(FATAL_ERROR "Given path (MEMSAN_LIBCXX: \"${MEMSAN_LIBCXX}\") does not seem to contain the MemorySanitizer-instrumented libc++ library required!")
					endif()
					include_directories(${MEMSAN_LIBCXX}/include)
					link_directories(${MEMSAN_LIBCXX}/lib)
					link_libraries(c++abi)
				endif()
			endif()

		elseif(sanitizer STREQUAL "thread")
			if(SANITIZER_ADDRESS AND NOT SANITIZERS_PROCESSED)
				message(WARNING "AddressSanitizer and ThreadSanitizer cannot be combined!")
			elseif(SANITIZER_MEMORY AND NOT SANITIZERS_PROCESSED)
				message(WARNING "MemorySanitizer and ThreadSanitizer cannot be combined!")
			else()
				if(NOT (CMAKE_SYSTEM_NAME MATCHES "Linux" AND ARCH_X64) AND NOT SANITIZERS_PROCESSED)
					message(WARNING "ThreadSanitizer is available only on 64-bit Linux. Ignoring.")
				else()
					set(SANITIZER_THREAD true)
					add_compile_options(-fsanitize=thread)
					link_libraries(-fsanitize=thread)
				endif()
			endif()

		elseif(NOT SANITIZERS_PROCESSED)
			list(REMOVE_ITEM enabled_sanitizers "${sanitizer}")
			message(WARNING "Unknown sanitizer requested: ${sanitizer}. Ignoring.")
		endif()

	endforeach()

	set(SANITIZERS_PROCESSED true)
	string(LENGTH enabled_sanitizers enabled_sanitizers_len)
	if(enabled_sanitizers_len GREATER 1)
		message(STATUS "Enabled sanitizers: ${enabled_sanitizers}.")
	endif()

endif(NOT WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_BUILD_TYPE MATCHES "(Debug|DebugFast)" AND NOT SANITIZERS STREQUAL "none")
