
INCLUDE(CheckIncludeFiles)
INCLUDE(CheckIncludeFileCXX)

CHECK_INCLUDE_FILES("sys/types.h;sys/stat.h;sys/unistd.h" HAVE_GNU_SYSLIB_H)

# Configuration header file for the platform
INCLUDE (CheckTypeSize) 
CHECK_TYPE_SIZE(uint UINT)
# sets HAVE_UINT

INCLUDE(CTest)

###########################
## Target definitions
###########################

ADD_EXECUTABLE(MorpheusSIM "")
SET_TARGET_PROPERTIES(MorpheusSIM PROPERTIES  OUTPUT_NAME ${MORPHEUS_EXEC_NAME} )

ADD_LIBRARY(MorpheusCore OBJECT ""  )
TARGET_INCLUDE_DIRECTORIES(MorpheusCore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})

IF(APPLE)
	IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "13.0.0")
		TARGET_COMPILE_DEFINITIONS(MorpheusCore PRIVATE "NO_HW_INFERENCE_SIZE=1")
	ENDIF()
ENDIF()

###############################
## Internal target dependencies
###############################

TARGET_LINK_LIBRARIES_PATCHED(MorpheusSIM PRIVATE MorpheusCore)


TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC MorpheusMLconv XMLUtils muParser xmlParser gnuplot_interface tiny-process-library)

IF(HAVE_OPENMP)
	if (CMAKE_VERSION VERSION_LESS 3.10)
		# set_property( TARGET MorpheusCore APPEND COMPILE_FLAGS ${OpenMP_CXX_FLAGS})
		SET_TARGET_PROPERTIES(MorpheusCore PROPERTIES COMPILE_OPTIONS ${OpenMP_CXX_FLAGS} INCLUDE_DIRECTORIES ${OpenMP_CXX_INCLUDE_DIRS})
		target_link_libraries_PATCHED(MorpheusCore PUBLIC ${OpenMP_CXX_LIBRARIES})
	ELSE()
		TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC OpenMP::OpenMP_CXX)
	ENDIF()
ENDIF()

## c++17 filesystem -- we use the boost version due to limited support on MacOS 
# MESSAGE(STATUS "COMPILER is ${CMAKE_CXX_COMPILER_ID} version ${CMAKE_CXX_COMPILER_VERSION}")
#SET(CXX_FILESYSTEM_NAMESPACE  "std::filesystem")
#SET(CXX_FILESYSTEM_HEADER  "filesystem")
#IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
#	IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.1")
#		target_link_libraries_patched(MorpheusCore PUBLIC  "-lstdc++fs")
#		IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8.0")
#			SET(CXX_FILESYSTEM_HEADER  "experimental/filesystem")
#			SET(CXX_FILESYSTEM_NAMESPACE  "std::experimental::filesystem")
#		ENDIF()
#	ENDIF()
#ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
#	IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0")
#		target_link_libraries_patched(MorpheusCore PUBLIC "-lc++fs")
#		IF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "7.0")
#			SET(CXX_FILESYSTEM_HEADER  "experimental/filesystem")
#			SET(CXX_FILESYSTEM_NAMESPACE  "std::experimental::filesystem")
#		ENDIF()
#	ENDIF()
#ENDIF()


################################
## External Package dependencies
################################

if (MORPHEUS_STATIC_BUILD OR NOT BUILD_TESTING)
	set(Boost_USE_STATIC_LIBS ON)
endif()
FIND_PACKAGE(Boost COMPONENTS program_options filesystem REQUIRED)
SET(HAVE_BOOST 1)
TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC Boost::boost Boost::program_options)


#   ### Xtensor ###
SET(XTENSOR_USE_XSIMD ON CACHE BOOL TRUE)
SET(XTENSOR_USE_OPENMP ON CACHE BOOL ${HAVE_OPENMP})
FIND_PACKAGE( xtl QUIET )
FIND_PACKAGE( xsimd QUIET )
SET(xtensor_DIR_stored ${xtensor_DIR})
FIND_PACKAGE (xtensor 0.26.0 QUIET)
IF (TARGET xtensor)
	SET(XTENSOR_NEW_HEADERS 1)
ELSE ()
	SET(xtensor_DIR ${xtensor_DIR_stored})
	find_package(xtensor QUIET)
ENDIF()
IF ( TARGET xtensor AND TARGET xsimd AND TARGET xtl)
ELSE()
 	message(STATUS "Building xtensor internally" )
	add_subdirectory(../3rdparty/xtensor xtensor EXCLUDE_FROM_ALL)
ENDIF()

target_compile_definitions( xtensor INTERFACE XTENSOR_USE_XSIMD=1 )
target_link_libraries_patched(xtensor INTERFACE xtl xsimd)
IF(HAVE_OPENMP)

	target_compile_definitions( xtensor INTERFACE XTENSOR_USE_OPENMP=1 XTENSOR_OPENMP_TRESHOLD=1000 )
	IF( (NOT TARGET OpenMP::OpenMP_CXX_xtensor) AND ( CMAKE_VERSION VERSION_GREATER_EQUAL 3.18) )
		add_library(OpenMP::OpenMP_CXX_xtensor ALIAS OpenMP::OpenMP_CXX)
	ENDIF()
ENDIF()

get_target_property( XTL_INCLUDE_DIRS xtl INTERFACE_INCLUDE_DIRECTORIES )
MESSAGE(STATUS "Using xtl library at ${XTL_INCLUDE_DIRS}.")
get_target_property( XSIMD_INCLUDE_DIRS xsimd INTERFACE_INCLUDE_DIRECTORIES )
MESSAGE(STATUS "Using xsimd library at ${XSIMD_INCLUDE_DIRS}.")
get_target_property( XTENSOR_INCLUDE_DIRS xtensor INTERFACE_INCLUDE_DIRECTORIES)
MESSAGE(STATUS "Using xtensor library at ${XTENSOR_INCLUDE_DIRS}.")


TARGET_LINK_LIBRARIES(MorpheusCore PUBLIC xtensor )


FIND_PACKAGE(ZLIB REQUIRED)
FIND_PACKAGE(TIFF REQUIRED)
TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC ${ZLIB_LIBRARY} ${TIFF_LIBRARY} ${TIFF_LIBRARY})

CONFIGURE_FILE(config.h.cmake config.h)

IF(MORPHEUS_STATIC_BUILD)
	## We guess the static library dependencies from the corresponding dynamic library dependencies
	#		message(FATAL_ERROR "Static binary build only supported on *nix systems") 
	IF(UNIX)
		execute_process( COMMAND /sbin/ldconfig -p 
						 COMMAND grep "libtiff.so "
						 OUTPUT_VARIABLE TIFF_DYN_LIB
		)
		separate_arguments(TIFF_DYN_LIB)
		list(GET TIFF_DYN_LIB -1 TIFF_DYN_LIB)
		string(STRIP ${TIFF_DYN_LIB} TIFF_DYN_LIB)
	# 	message(STATUS "LibTIFF dynamic lib  ${TIFF_DYN_LIB}")
		
		execute_process( COMMAND ldd ${TIFF_DYN_LIB}
						 OUTPUT_VARIABLE TIFF_DEPENDS
		)
		string(REPLACE "\n" ";" TIFF_DEPENDS ${TIFF_DEPENDS})
		SET(LIB_LIST "")
		FOREACH(DEP ${TIFF_DEPENDS})
			string(REPLACE "." ";" DEP ${DEP})
			LIST(GET DEP 0 DEP)
			STRING(STRIP ${DEP} DEP)
			LIST(APPEND LIB_LIST ${DEP})
		ENDFOREACH()
		SET(TIFF_DEPENDS ${LIB_LIST})
	#	message(STATUS "LibTIFF dependencies ${TIFF_DEPENDS}")
		IF("libjpeg" IN_LIST TIFF_DEPENDS)
			FIND_PACKAGE(JPEG REQUIRED)
			message(STATUS "LibTIFF requires libjpeg")
			TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC ${JPEG_LIBRARY})
		ENDIF()
		IF("liblzma" IN_LIST TIFF_DEPENDS)
			FIND_PACKAGE(LibLZMA REQUIRED) # newer ubuntu versions
			message(STATUS "LibTIFF requires liblzma")
			TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC ${LIBLZMA_LIBRARIES})
		ENDIF()
		IF("libjbig" IN_LIST TIFF_DEPENDS)
			message(STATUS "LibTIFF requires libjbig")
			TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC -ljbig )
		ENDIF()
		IF("libwebp" IN_LIST TIFF_DEPENDS)
			message(STATUS "LibTIFF requires webp")
			TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC -lwebp -lsharpyuv )
		ENDIF()
		IF("libzstd" IN_LIST TIFF_DEPENDS)
			message(STATUS "LibTIFF requires zstd")
			TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC -lzstd )
		ENDIF()
		IF("libdeflate" IN_LIST TIFF_DEPENDS)
			message(STATUS "LibTIFF requires libdeflate")
			TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC -ldeflate )
		ENDIF()
		IF("libLerc" IN_LIST TIFF_DEPENDS)
			message(STATUS "LibTIFF requires libLerc")
			TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC -lLerc )
		ENDIF()
	ELSEIF(WIN32)
		MESSAGE(STATUS " WIN32 using MSYS: ${MSYS}")
		# LibTIFF requires libjpeg
		FIND_PACKAGE(JPEG REQUIRED)
		TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC ${JPEG_LIBRARY})
		# LibTIFF requires liblzma
		FIND_PACKAGE(LibLZMA REQUIRED) # newer ubuntu versions
		TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC ${LIBLZMA_LIBRARIES})
		# LibTIFF requires zstd
		TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PUBLIC -lzstd )
	ENDIF()
ENDIF()

add_subdirectory(testing)

add_subdirectory(plugins)
add_subdirectory(utils)
add_subdirectory(core)

IF(MORPHEUS_STATIC_BUILD)
	target_link_libraries_PATCHED(MorpheusCore PUBLIC -Wl,--whole-archive -lpthread -Wl,--no-whole-archive)
# 	TARGET_LINK_LIBRARIES_PATCHED(MorpheusCore PRIVATE -static-libstdc++ -static-libgcc)
ENDIF()

IF  ("${MORPHEUS_OS}" STREQUAL "WIN32")
	# TARGET_LINK_LIBRARIES_PATCHED(MorpheusSIM pthread psapi)

# 	If (MORPHEUS_STATIC_BUILD)
# 		set(STD_LIB_SUFFIX ${CMAKE_FIND_LIBRARY_SUFFIXES})
# 		set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
# 	ENDIF()
# 	
# 	if ( HAVE_OPENMP AND MORPHEUS_STATIC_BUILD)
# 		SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread")
# # 		FIND_LIBRARY(PTHREADS_LIBRARY NAMES pthread)
# # 		LINK_LIBRARIES( ${PTHREADS_LIBRARY} )
# 	ENDIF()
# 	SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpsapi")
# # 	FIND_LIBRARY(PSAPI_LIBRARY NAMES psapi)
# # 	LINK_LIBRARIES( ${PSAPI_LIBRARY} )
# 	# find_library(PTHREADS_LIBRARY NAMES libpthread)
# 	If (MORPHEUS_STATIC_BUILD)
# 		set(CMAKE_FIND_LIBRARY_SUFFIXES ${STD_LIB_SUFFIX})
# 	ENDIF()
ENDIF()

# dump_target_properties(MorpheusCore)
# Installation of the program

IF  ( WIN32 )
	INSTALL(TARGETS MorpheusSIM RUNTIME DESTINATION . COMPONENT Morpheus )
	
	Find_PROGRAM(GNUPLOT_EXECUTABLE NAMES gnuplot.exe HINTS ${GNUPLOT_ROOT}/bin ENV GNUPLOT_ROOT REQUIRED)
	INSTALL(PROGRAMS ${GNUPLOT_EXECUTABLE}
		DESTINATION . 
		COMPONENT Morpheus
		EXCLUDE_FROM_ALL)
        
	Find_PROGRAM(FFMPEG_EXECUTABLE NAMES ffmpeg.exe HINTS ${FFMPEG_ROOT}/bin ENV FFMPEG_ROOT REQUIRED)
        INSTALL(PROGRAMS ${FFMPEG_EXECUTABLE}
                DESTINATION .
                COMPONENT Morpheus
                EXCLUDE_FROM_ALL)

	
	set(APPS "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${MORPHEUS_BUNDLE_PREFIX}")
	
	# Directories to look for dependencies
	set(DIRS "${CMAKE_BINARY_DIR}")
	# Path used for searching by FIND_XXX(), with appropriate suffixes added
	LIST(APPEND CMAKE_PREFIX_PATH "C:/msys64/ucrt64")
	if(CMAKE_PREFIX_PATH)
		foreach(dir ${CMAKE_PREFIX_PATH})
			list(APPEND DIRS "${dir}/bin" "${dir}/lib")
		endforeach()
	endif()
	#Append Qt's lib folder which is two levels above Qt5Widgets_DIR
	list(APPEND DIRS "${Qt5Widgets_DIR}/../..")
	
	INSTALL(CODE "
		SET(EXECUTABLES \"${APPS}/morpheus.exe;${APPS}/gnuplot.exe;${APPS}/ffmpeg.exe\")
		#MESSAGE(\"CPACK: - Searching binaries in ${APPS}/..\")
		message(\"CPACK:  - APPS: \${EXECUTABLES}\")
		message(\"CPACK:  - DIRS: ${DIRS}\")
		file(GET_RUNTIME_DEPENDENCIES
			RESOLVED_DEPENDENCIES_VAR deps_resolved
			UNRESOLVED_DEPENDENCIES_VAR deps_unresolved
			EXECUTABLES \${EXECUTABLES}
			DIRECTORIES ${DIRS})
		foreach(dep \${deps_resolved})
			if( dep MATCHES \"C:[/\\\\]Windows.*\" )
				# do not install system libraries
			else()
				MESSAGE(\"CPACK:  - adding \${dep}\")
				file(INSTALL \"\${dep}\"
				DESTINATION \"${APPS}/${INSTALL_RUNTIME_DIR}\")
			endif()
		endforeach()
		# MESSAGE(\"CPACK:  - Resolved dependencies \${deps_resolved}\")
		# MESSAGE(\"CPACK:  - Unresolved dependencies \${deps_unresolved}\")
		"
		COMPONENT Morpheus
		EXCLUDE_FROM_ALL
	)
	
		
ELSEIF(APPLE)
	IF (MORPHEUS_RELEASE_BUNDLE)
		SET(INSTALL_RUNTIME_DIR "${MORPHEUS_BUNDLE_DIR}/Contents/MacOS" )
		INSTALL(TARGETS MorpheusSIM 
			RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR}
			COMPONENT Morpheus 
		)
		#Find_PROGRAM(GNUPLOT_EXECUTABLE NAMES gnuplot HINTS ENV GNUPLOT REQUIRED)
		#IF(IS_SYMLINK ${GNUPLOT_EXECUTABLE})
			#SET(GNUPLOT_SYMEX ${GNUPLOT_EXECUTABLE})
			#FILE(READ_SYMLINK ${GNUPLOT_EXECUTABLE} GNUPLOT_EXECUTABLE)
			#IF(NOT IS_ABSOLUTE ${GNUPLOT_EXECUTABLE})
				#get_filename_component(dir "${GNUPLOT_SYMEX}" DIRECTORY)
				#set(GNUPLOT_EXECUTABLE "${dir}/${GNUPLOT_EXECUTABLE}")
			#endif()
		#ENDIF()
		#INSTALL(PROGRAMS ${GNUPLOT_EXECUTABLE} 
			#DESTINATION ${INSTALL_RUNTIME_DIR} 
			#COMPONENT Morpheus_SC
			#EXCLUDE_FROM_ALL)

	ELSE()
		INSTALL(TARGETS MorpheusSIM RUNTIME DESTINATION bin COMPONENT Morpheus )
	ENDIF()
ELSE()
# 	if ( CMAKE_INSTALL_PREFIX MATCHES ".*/\\.local$" )
# 		MESSAGE(STATUS "Installing to '.local' path")
# 		SET(CMAKE_INSTALL_BINDIR "../bin")
# 	ELSE()
# 		SET(CMAKE_INSTALL_BINDIR "bin")
# 	ENDIF()
	INSTALL(TARGETS MorpheusSIM RUNTIME DESTINATION bin COMPONENT Morpheus )
	
ENDIF()



#####################################
##  Docu and Language definition  ##
#####################################

SET(XSD_SOURCES 
	${CMAKE_CURRENT_SOURCE_DIR}/core/base_types.xsd
	${CMAKE_CURRENT_SOURCE_DIR}/core/simulation.xsd
	${CMAKE_CURRENT_SOURCE_DIR}/core/interaction_energy.xsd
)

SET(APP_DOC_HEADERS 
	${CMAKE_CURRENT_SOURCE_DIR}/core/docu.h
	#${CMAKE_CURRENT_SOURCE_DIR}/core/diffusion.h
	${CMAKE_CURRENT_SOURCE_DIR}/core/cpm_sampler.h
	${CMAKE_CURRENT_SOURCE_DIR}/core/interaction_energy.h
	${CMAKE_CURRENT_SOURCE_DIR}/core/expression_evaluator.h
)

get_target_property(sources MorpheusCore SOURCES)

foreach(source ${sources})
	IF ( EXISTS ${source} )
		STRING(REGEX REPLACE "\\.cpp$" ".h" header ${source})
		STRING(REGEX REPLACE "\\.cpp$" ".xsd" xsd ${source})
		#  check the header for the PLUGIN macro
		IF ( EXISTS ${header} )
			file(STRINGS "${header}" lines REGEX "DECLARE_PLUGIN")
			IF (lines)
				list(APPEND APP_DOC_HEADERS ${header})
				if ( EXISTS ${xsd} )
					list(APPEND XSD_SOURCES ${xsd})
				else()
					message(WARNING "Missing plugin language description ${xsd}" )
				endif()
			ENDIF()
		ENDIF()
	ELSE()
		MESSAGE(WARNING "Source file ${source} does not exist.")
	ENDIF()
endforeach(source)

SET(XML_MERGE_TRANS_SCRIPT  "${CMAKE_CURRENT_SOURCE_DIR}/merge.xslt")

FIND_PROGRAM(XSLTPROC "xsltproc" NAMES "xsltproc xsltproc.exe" DOC "XSLTProc executable")
FIND_PROGRAM(XMLLINT "xmllint" NAMES "xmllint xsltproc.exe" DOC "XMLLint executable")

IF( NOT XSLTPROC )
	message(FATAL_ERROR "Unable to locate 'xsltproc' executable")
ENDIF()


SET(XSD_NAME "${PROJECT_NAME}.xsd")

UNSET(MERGE_INTERMEDIATE)
FOREACH( xsd_file ${XSD_SOURCES})
	IF ( DEFINED MERGE_INTERMEDIATE )
		get_filename_component(xname ${xsd_file} NAME )
		SET(MERGE_OUTPUT "${xname}_merged")
		STRING(REPLACE " " "%20" SCRIPT_SAVE_INTERMEDIATE ${MERGE_INTERMEDIATE})
		SET(SCRIPT_SAVE_INTERMEDIATE \\\"${SCRIPT_SAVE_INTERMEDIATE}\\\")
		ADD_CUSTOM_COMMAND(
			OUTPUT ${MERGE_OUTPUT}
			COMMAND ${XSLTPROC} -o ${MERGE_OUTPUT} --param with ${SCRIPT_SAVE_INTERMEDIATE} ${XML_MERGE_TRANS_SCRIPT} "${xsd_file}"
			DEPENDS ${xsd_file} ${MERGE_INTERMEDIATE} ${XML_MERGE_TRANS_SCRIPT}
			COMMENT "Merging in ${xsd_file} into ${XSD_NAME}"
		)
		SET(MERGE_INTERMEDIATE "${CMAKE_CURRENT_BINARY_DIR}/${MERGE_OUTPUT}")
	ELSE()
		SET(MERGE_INTERMEDIATE ${xsd_file})
	ENDIF()
ENDFOREACH()
SET(MERGED_SCHEMA ${MERGE_INTERMEDIATE})


IF ( "${XMLLINT}" STREQUAL "XMLLINT-NOTFOUND" )
	MESSAGE( STATUS "Unable to locate 'xmllint' executable.\nSkipping xsd pretty printing.")
	ADD_CUSTOM_COMMAND(
		OUTPUT ${XSD_NAME}
		COMMAND cmake -E copy ${MERGED_SCHEMA} ${XSD_NAME}
		DEPENDS ${MERGED_SCHEMA} 
	)
ELSE()
	ADD_CUSTOM_COMMAND(
		OUTPUT ${XSD_NAME}
		COMMAND ${XMLLINT} --format --noblanks --output ${XSD_NAME} ${MERGED_SCHEMA}
		DEPENDS ${MERGED_SCHEMA}
		COMMENT "Pretty formatting XML schema ${XSD_NAME}"
	)
ENDIF()

ADD_CUSTOM_TARGET( 
	xmlSchema
	DEPENDS ${XSD_NAME} ${XSD_SOURCES}
)


SET(APP_DOC_HEADERS ${APP_DOC_HEADERS} PARENT_SCOPE)
SET(MORPHEUS_XSD_FILE ${CMAKE_CURRENT_BINARY_DIR}/${XSD_NAME} PARENT_SCOPE)


