
find_package(Perl REQUIRED)
find_package(Threads REQUIRED)

# Detect architecture.
if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64")
	set(ARCH_X64 1)
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86")
	set(ARCH_X86 1)
elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm.*")
	string(REGEX REPLACE "armv([0-9]+).*" "\\1" ARM_VERSION "${CMAKE_SYSTEM_PROCESSOR}")
	if(${ARM_VERSION} LESS 8)
		set(ARCH_ARM32 1)
	else()
		set(ARCH_ARM64 1)
	endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "aarch64")
	set(ARCH_ARM64 1)
else()
	if(CMAKE_SIZEOF_VOID_P EQUAL 4)
		set(ARCH_GENERIC32 1)
	elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
		set(ARCH_GENERIC64 1)
	endif()
endif()

# Detect OS.
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
	set(OS_WINDOWS 1)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
	set(OS_LINUX 1)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
	set(OS_DARWIN 1)
else()
	message(FATAL_ERROR "Unsupported system. Supported: Windows, Linux, macOS.")
endif()

# Detect compiler.
if(OS_WINDOWS)
	if(MSVC)
		set(COMPILER_MSVC 1)
	else()
		message(FATAL_ERROR "Unsupported compiler. Supported: MSVC.")
	endif()
elseif(OS_LINUX)
	if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
		set(COMPILER_GCC 1)
	elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
		set(COMPILER_CLANG 1)
	else()
		message(FATAL_ERROR "Unsupported compiler. Supported: GCC, Clang.")
	endif()
elseif(OS_DARWIN)
	if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang")
		message(FATAL_ERROR "Unsupported compiler. Supported: AppleClang.")
	endif()
endif()

# Prepare configuration option for OpenSSL.
if(OS_WINDOWS)
	if(COMPILER_MSVC)
		set(OPENSSL_MAKE "nmake")
		if(ARCH_X86)
			set(OPENSSL_CONFIGURE_ARCH "VC-WIN32")
		elseif(ARCH_X64)
			set(OPENSSL_CONFIGURE_ARCH "VC-WIN64A")
		elseif(ARCH_ARM32)
			set(OPENSSL_CONFIGURE_ARCH "VC-WIN32-ARM")
		elseif(ARCH_ARM64)
			set(OPENSSL_CONFIGURE_ARCH "VC-WIN64-ARM")
		endif()
	endif()
elseif(OS_LINUX)
	set(OPENSSL_MAKE make -j${CPUS})
	if(ARCH_X86)
		if(COMPILER_GCC)
			set(OPENSSL_CONFIGURE_ARCH "linux-x86")
		elseif(COMPILER_CLANG)
			set(OPENSSL_CONFIGURE_ARCH "linux-x86-clang")
		endif()
	elseif(ARCH_X64)
		if(COMPILER_GCC)
			set(OPENSSL_CONFIGURE_ARCH "linux-x86_64")
		elseif(COMPILER_CLANG)
			set(OPENSSL_CONFIGURE_ARCH "linux-x86_64-clang")
		endif()
	elseif(ARCH_ARM32)
		set(OPENSSL_CONFIGURE_ARCH "linux-armv4")
	elseif(ARCH_ARM64)
		set(OPENSSL_CONFIGURE_ARCH "linux-aarch64")
	elseif(ARCH_GENERIC32)
		set(OPENSSL_CONFIGURE_ARCH "linux-generic32")
	elseif(ARCH_GENERIC64)
		set(OPENSSL_CONFIGURE_ARCH "linux-generic64")
	endif()
elseif(OS_DARWIN)
	set(OPENSSL_MAKE make -j${CPUS})
	if(ARCH_X86)
		set(OPENSSL_CONFIGURE_ARCH "darwin-i386-cc")
	elseif(ARCH_X64)
		set(OPENSSL_CONFIGURE_ARCH "darwin64-x86_64-cc")
	endif()
endif()

# Unrecognized architecture.
if(NOT DEFINED OPENSSL_CONFIGURE_ARCH)
	message(FATAL_ERROR "Unsupported architecture. Supported: x86, x86-64, ARM, generic 32-bit (Linux only), generic 64-bit (Linux only).")
endif()

set(OPENSSL_PREFIX         "${CMAKE_CURRENT_BINARY_DIR}/openssl")
set(OPENSSL_SRC_DIR        "${OPENSSL_PREFIX}/src/openssl")
set(OPENSSL_INSTALL_DIR    "${OPENSSL_PREFIX}/src/openssl-install")
set(OPENSSL_VERSION        "1.1.1c")
set(OPENSSL_DOWNLOAD_NAME  "openssl.tar.gz")

set(OPENSSL_CONFIGURE_OPTIONS
	no-shared
	no-asm
	--prefix=${OPENSSL_INSTALL_DIR}
	--openssldir=${OPENSSL_INSTALL_DIR}
)

ExternalProject_Add(openssl
	PREFIX ${OPENSSL_PREFIX}
	URL ${OPENSSL_URL}
	URL_HASH SHA256=${OPENSSL_ARCHIVE_SHA256}
	DOWNLOAD_NAME ${OPENSSL_DOWNLOAD_NAME}
	CONFIGURE_COMMAND
		${PERL_EXECUTABLE}
		${OPENSSL_SRC_DIR}/Configure
		${OPENSSL_CONFIGURE_OPTIONS}
		${OPENSSL_CONFIGURE_ARCH}
	BUILD_IN_SOURCE 1
	BUILD_COMMAND
		${OPENSSL_MAKE}
	INSTALL_COMMAND
		# Install only the software parts (libraries and includes), not
		# e.g. documentation and manual pages.
		${OPENSSL_MAKE} install_sw
	LOG_DOWNLOAD 1
	LOG_BUILD 1
	LOG_CONFIGURE 1
	LOG_INSTALL 1
)

set(OPENSSL_CRYPTO_LIB_NAME  libcrypto${CMAKE_STATIC_LIBRARY_SUFFIX})
set(OPENSSL_CRYPTO_LIB_PNAME libretdec-openssl-crypto${CMAKE_STATIC_LIBRARY_SUFFIX})
set(OPENSSL_CRYPTO_LIB       ${OPENSSL_INSTALL_DIR}/lib/${OPENSSL_CRYPTO_LIB_NAME})

# Create target.
add_library(openssl-crypto INTERFACE)
add_library(retdec::deps::openssl-crypto ALIAS openssl-crypto)
add_dependencies(openssl-crypto openssl)

target_include_directories(openssl-crypto
	SYSTEM INTERFACE
		$<BUILD_INTERFACE:${OPENSSL_INSTALL_DIR}/include>
		$<INSTALL_INTERFACE:${RETDEC_INSTALL_DEPS_INCLUDE_DIR}>
)

target_link_libraries(openssl-crypto
	INTERFACE
		$<BUILD_INTERFACE:${OPENSSL_CRYPTO_LIB}>
		$<INSTALL_INTERFACE:retdec::deps::openssl-crypto-libs>
		Threads::Threads
)
if(MSVC)
	target_link_libraries(openssl-crypto
		INTERFACE
			ws2_32
			gdi32
			advapi32
			crypt32
			user32
		)
elseif(UNIX)
	target_link_libraries(openssl-crypto
		INTERFACE
			${CMAKE_DL_LIBS}
	)
endif()

# Install includes.
install(
    DIRECTORY   ${OPENSSL_INSTALL_DIR}/include/
    DESTINATION ${RETDEC_INSTALL_DEPS_INCLUDE_DIR}
)

# Install libs.
install(
    FILES       ${OPENSSL_CRYPTO_LIB}
	DESTINATION ${RETDEC_INSTALL_LIB_DIR}
	RENAME      ${OPENSSL_CRYPTO_LIB_PNAME}
)

# Install targets.
install(TARGETS openssl-crypto
	EXPORT openssl-crypto-targets
)

# Export targets.
install(EXPORT openssl-crypto-targets
	FILE "retdec-openssl-crypto-targets.cmake"
	NAMESPACE retdec::deps::
	DESTINATION ${RETDEC_INSTALL_CMAKE_DIR}
)

# Configure config file.
set(OPENSSL_CRYPTO_LIB_INSTALLED "${RETDEC_INSTALL_LIB_DIR_ABS}/${OPENSSL_CRYPTO_LIB_PNAME}")
configure_package_config_file(
	"retdec-openssl-crypto-config.cmake"
	"${CMAKE_CURRENT_BINARY_DIR}/retdec-openssl-crypto-config.cmake"
	INSTALL_DESTINATION ${RETDEC_INSTALL_CMAKE_DIR}
	PATH_VARS
		OPENSSL_CRYPTO_LIB_INSTALLED
)

# Install CMake files.
install(
	FILES
        "${CMAKE_CURRENT_BINARY_DIR}/retdec-openssl-crypto-config.cmake"
	DESTINATION
		"${RETDEC_INSTALL_CMAKE_DIR}"
)
