# This functions checks if the dependencies for the Yan LR plugin are available.
#
# If they are, the function sets the variable `FOUND_DEPENDENCIES` to `TRUE`. The function then also sets:
#
# - `ANTLR_EXECUTABLE` to the name of the ANTLR executable,
# - `ANTLR4CPP_LIBRARIES` to the paths of the libraries provided by ANTLR’s C++ runtime, and
# - `ANTLR4CPP_INCLUDE_DIRS` to the paths of the included directories of ANTLR’s C++ runtime
#
# . If the function was unsuccessful it sets `FOUND_DEPENDENCIES` to `FALSE` and stores the reason for the failure in the variable
# `FAILURE_MESSAGE`.
function (check_dependencies)

	set (FOUND_DEPENDENCIES FALSE PARENT_SCOPE)
	unset (ANTLR_EXECUTABLE)
	unset (FAILURE_MESSAGE)

	execute_process (COMMAND antlr4 RESULT_VARIABLE ANTLR4_NOT_AVAILABLE OUTPUT_QUIET)
	execute_process (COMMAND antlr RESULT_VARIABLE ANTLR_NOT_AVAILABLE OUTPUT_QUIET)
	if (ANTLR4_NOT_AVAILABLE AND ANTLR_NOT_AVAILABLE)
		set (FAILURE_MESSAGE "ANTLR 4 executable (antlr4, antlr) not found" PARENT_SCOPE)
		return ()
	else (ANTLR4_NOT_AVAILABLE AND ANTLR_NOT_AVAILABLE)
		if (ANTLR4_NOT_AVAILABLE)
			set (ANTLR_EXECUTABLE antlr PARENT_SCOPE)
		else (ANTLR4_NOT_AVAILABLE)
			set (ANTLR_EXECUTABLE antlr4 PARENT_SCOPE)
		endif (ANTLR4_NOT_AVAILABLE)
	endif (ANTLR4_NOT_AVAILABLE AND ANTLR_NOT_AVAILABLE)

	find_package (ANTLR4CPP QUIET)
	if (NOT ANTLR4CPP_FOUND)
		set (FAILURE_MESSAGE "ANTLR 4 CPP runtime (antlr4-cpp-runtime) not found" PARENT_SCOPE)
		return ()
	endif (NOT ANTLR4CPP_FOUND)
	set (ANTLR4CPP_LIBRARIES ${ANTLR4CPP_LIBRARIES} PARENT_SCOPE)
	set (ANTLR4CPP_INCLUDE_DIRS ${ANTLR4CPP_INCLUDE_DIRS} PARENT_SCOPE)

	set (DISABLE_PLUGIN_ASAN
	     ${ENABLE_ASAN}
	     AND
	     "${CMAKE_CXX_COMPILER_ID}"
	     MATCHES
	     "GNU"
	     AND
	     ${CMAKE_CXX_COMPILER_VERSION}
	     VERSION_LESS
	     7)
	if (${DISABLE_PLUGIN_ASAN})
		set (FAILURE_MESSAGE "ASAN enabled GCC builds of the plugin report memory leaks" PARENT_SCOPE)
		return ()
	endif (${DISABLE_PLUGIN_ASAN})

	set (FOUND_DEPENDENCIES TRUE PARENT_SCOPE)
endfunction (check_dependencies)

# This functions generates the source files of the YAML parser using the given ANTLR executable (`ANTLR_EXECUTABLE`). The function also
# invokes the script `RenameSymbols.cmake` to replace the symbol names used by ANTLR in error messages by a more human readable form.
#
# The function exports the list
#
# - `GENERATED_SOURCE_FILES`, which contains the list of source files generated by ANTLR and `RenameSymbols.cmake`
#
# .
function (generate_code ANTLR_EXECUTABLE)
	set (GRAMMAR_NAME YAML)
	set (GRAMMAR_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${GRAMMAR_NAME}.g4)
	set (TOKEN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${GRAMMAR_NAME}.tokens)
	set (GENERATED_SOURCE_FILES_NAMES BaseListener Listener)
	set (PARSER_SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${GRAMMAR_NAME}.cpp)
	set (PARSER_MODIFIED_SOURCE_FILE ${CMAKE_CURRENT_BINARY_DIR}/${GRAMMAR_NAME}ImprovedSymbolNames.cpp)

	foreach (file ${GENERATED_SOURCE_FILES_NAMES} "")
		foreach (extension "cpp" "h")
			set (filepath ${CMAKE_CURRENT_BINARY_DIR}/${GRAMMAR_NAME}${file}.${extension})
			set_source_files_properties (${filepath} PROPERTIES GENERATED TRUE)
			if (CMAKE_COMPILER_IS_GNUCXX)
				set_source_files_properties (${filepath} PROPERTIES COMPILE_FLAGS "-Wno-shadow")
			endif (CMAKE_COMPILER_IS_GNUCXX)
			if (NOT ${filepath} STREQUAL ${PARSER_SOURCE_FILE})
				list (APPEND GENERATED_SOURCE_FILES_EXPORT)
			endif (NOT ${filepath} STREQUAL ${PARSER_SOURCE_FILE})
			list (APPEND GENERATED_SOURCE_FILES
				     ${filepath})
		endforeach (extension "cpp" "h")
	endforeach (file ${GENERATED_SOURCE_FILES_NAMES})

	add_custom_command (OUTPUT ${GENERATED_SOURCE_FILES}
			    COMMAND ${ANTLR_EXECUTABLE} -Werror -Dlanguage=Cpp -o ${CMAKE_CURRENT_BINARY_DIR} -package antlr ${GRAMMAR_FILE}
			    DEPENDS ${GRAMMAR_FILE}
				    ${TOKEN_FILE}
			    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
	add_custom_target (parser_generated DEPENDS ${PARSER_SOURCE_FILE})

	set_source_files_properties (${PARSER_MODIFIED_SOURCE_FILE} PROPERTIES GENERATED TRUE)
	if (CMAKE_COMPILER_IS_GNUCXX)
		set_source_files_properties (${PARSER_MODIFIED_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "-Wno-shadow")
	endif (CMAKE_COMPILER_IS_GNUCXX)
	add_custom_command (OUTPUT ${PARSER_MODIFIED_SOURCE_FILE}
			    COMMAND ${CMAKE_COMMAND}
				    ARGS -D
					 PARSER_SOURCE_FILE=${PARSER_SOURCE_FILE}
					 -D
					 PARSER_MODIFIED_SOURCE_FILE=${PARSER_MODIFIED_SOURCE_FILE}
					 -P
					 ${CMAKE_CURRENT_SOURCE_DIR}/RenameSymbols.cmake
			    DEPENDS parser_generated
				    RenameSymbols.cmake
			    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

	set (GENERATED_SOURCE_FILES ${GENERATED_SOURCE_FILES_EXPORT} ${PARSER_MODIFIED_SOURCE_FILE} PARENT_SCOPE)

endfunction (generate_code)

if (DEPENDENCY_PHASE)
	check_dependencies ()
	if (NOT FOUND_DEPENDENCIES)
		remove_plugin (yanlr ${FAILURE_MESSAGE})
	else (NOT FOUND_DEPENDENCIES)
		generate_code (${ANTLR_EXECUTABLE})
		set (SOURCE_FILES
		     "${GENERATED_SOURCE_FILES}"
		     listener.hpp
		     listener.cpp
		     error_listener.hpp
		     error_listener.cpp
		     yaml_lexer.hpp
		     yaml_lexer.cpp
		     yanlr.hpp
		     yanlr.cpp)
	endif (NOT FOUND_DEPENDENCIES)
endif (DEPENDENCY_PHASE)

# The generated parser code seems to contain a double free that causes the unit test to crash with a segfault on **some** systems that use
# `glibc`. If AddressSanitizer is enabled everything seems to work fine.
set (TEST_ARGUMENTS ADD_TEST CPP_TEST)
if ("${CMAKE_SYSTEM_NAME}"
    STREQUAL
    Linux
    AND NOT
	${ENABLE_ASAN})

	# We only disable the test, if we detect a GNU C Library based system.
	execute_process (COMMAND ldd --version
			 RESULT_VARIABLE ANTLR4_NOT_AVAILABLE
			 OUTPUT_VARIABLE LDD_OUTPUT)
	if ("${LDD_OUTPUT}" MATCHES "GLIBC|GNU libc")
		set (TEST_ARGUMENTS "")
	endif ("${LDD_OUTPUT}" MATCHES "GLIBC|GNU libc")

endif ("${CMAKE_SYSTEM_NAME}" STREQUAL Linux AND NOT ${ENABLE_ASAN})

add_plugin (yanlr
	    CPP
	    ${TEST_ARGUMENTS}
	    SOURCES ${SOURCE_FILES}
	    INCLUDE_SYSTEM_DIRECTORIES ${ANTLR4CPP_INCLUDE_DIRS}
	    LINK_LIBRARIES ${ANTLR4CPP_LIBRARIES}
	    LINK_ELEKTRA elektra-ease
	    INSTALL_TEST_DATA
	    TEST_README
	    # Unfortunately it looks like ANTLR’s code [causes a container-overflow](https://github.com/antlr/antlr4/issues/2332).
	    TEST_ENVIRONMENT "ASAN_OPTIONS=detect_container_overflow=0"
	    TEST_REQUIRED_PLUGINS directoryvalue
				  yamlsmith)
