# SLX - The Simple Library Extensions Library
#
# Copyright (c) 2021 William Berry
# email: wsberry@gmail.com
# github: https://github.com/wsberry
#
# Licensed under the Apache License, Version 2.0 (the "License");
# You may freely use this source code and its projects in compliance with the License.
#
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License src distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# About
# This file implements the installation of SLX sample project dependencies.
#
# Notes:
#  - The Homebrew package manager is assumed for macOS and Linux environments.
#  - vcpkg manager is not supported for a number of reasons (e.g. has not
#    stayed current with gRPC releases).
#  - This is an initial prototype which means there may be significant
#    changes coming as it goes through various development and test stages.

# SLX CMake:
include(utils)

# CMake:
include(CTest)
include(FetchContent)

# Following are the dependency sources to be installed.
#
# Version numbers are ignored when installing from brew, which will always
# install the latest package. If versioning of a specific package is
# available you may use: 'brew install <packageName>@1.2.3' where '1.2.3'
# is the desired version number.
#
SET(OPEN_SSL_PATH "openssl@3/3.0.0_1")
SET(Version_Catch2   "v2.13.6")
SET(Version_Cista    "")
SET(Version_Elements "")
SET(Version_FmtLib   "")
SET(Version_JSONCons "")
SET(Version_PortableFileDialogs "")
SET(Version_Protobuf "v3.19.1")
SET(Version_gRPC     "v1.41.1")

SET(URL_Catch2                     "https://github.com/catchorg/Catch2.git")
SET(URL_Cista                      "https://github.com/felixguendling/cista.git")
SET(URL_Elements                   "https://github.com/cycfi/elements.git")
SET(URL_FmtLib                     "https://github.com/fmtlib/fmt.git")
SET(URL_JSONCons                   "https://github.com/danielaparker/jsoncons.git")
SET(URL_PortableFileDialogs        "https://github.com/samhocevar/portable-file-dialogs.git")
SET(URL_Protobuf                   "https://github.com/protocolbuffers/protobuf.git")
SET(URL_gRPC                       "https://github.com/grpc/grpc.git")

SET(Target_DIR_Catch2              ${PROJECT_DEPENDENCY_DIR}/Catch2-src)
SET(Target_DIR_Cista               ${PROJECT_DEPENDENCY_DIR}/cista-src)
SET(Target_DIR_Elements            ${PROJECT_DEPENDENCY_DIR}/elements-src)
SET(Target_DIR_FmtLib              ${PROJECT_DEPENDENCY_DIR}/fmtlib-src)
SET(Target_DIR_JSONCons            ${PROJECT_DEPENDENCY_DIR}/jsoncons-src)
SET(Target_DIR_PortableFileDialogs ${PROJECT_DEPENDENCY_DIR}/portable-file-dialogs-src)
SET(Target_DIR_Protobuf            ${PROJECT_DEPENDENCY_DIR}protobuf-src)
SET(Target_DIR_gRPC                ${PROJECT_DEPENDENCY_DIR}/grpc-src)

SET(Target_Name_Catch2              Catch2)
SET(Target_Name_Cista               cista)
SET(Target_Name_Elements            elements)
SET(Target_Name_FmtLib              fmtlib)
SET(Target_Name_JSONCons            jsoncons)
SET(Target_Name_PortableFileDialogs portable-file-dialogs)
SET(Target_Name_Protobuf            protobuf)
SET(Target_Name_gRPC                grpc)

# State flags used to indicate if a package has been installed.
#
SET(Installed_Catch2              OFF)
SET(Installed_Cista               OFF)
SET(Installed_Elements            OFF)
SET(Installed_FmtLib              OFF)
SET(Installed_JSONCons            OFF)
SET(Installed_PortableFileDialogs OFF)
SET(Installed_Protobuf            OFF)
SET(Installed_gRPC                OFF)

# Sets the Installed_{package name} state flag of each dependency.
#
macro(check_installed_state)

	if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" )
	else()
		MESSAGE(FATAL_ERROR "TODO: 'check_installed_state' NOT IMPLEMENTED for '${CMAKE_HOST_SYSTEM_NAME}'!")
	endif()

	if (USE_PACKAGE_MANAGER)
		SET(OPENSSL_ROOT_DIR ${BREW_DEPS_DIR}/${OPEN_SSL_PATH})
		SET(OPENSSL_LIBRARIES ${BREW_DEPS_DIR}/${OPEN_SSL_PATH}/lib)
		SET(OPEN_SSL_PATH ${OPENSSL_ROOT_DIR} CACHE STRING "")
		# is verified by 'CMake include(FindOpenSSL)'
	else()
		MESSAGE(FATAL_ERROR "TODO: 'check_installed_state', Open SSL verify is NOT IMPLEMENTED for '${CMAKE_HOST_SYSTEM_NAME}'!")
	endif()

	if (EXISTS ${BREW_DEPS_DIR}/catch2 OR EXISTS ${Target_DIR_Catch2})
		SET(Installed_Catch2 ON)
	endif()

	if (EXISTS ${BREW_DEPS_DIR}/cista OR EXISTS ${Target_DIR_Cista})
		SET(Installed_Cista ON)
	endif()

	if (EXISTS ${Target_DIR_Elements})
		SET(Installed_Elements ON)
	endif()

	if (EXISTS ${Target_DIR_FmtLib})
		SET(Installed_FmtLib ON)
	endif()

	if (EXISTS ${Target_DIR_JSONCons})
		SET(Installed_JSONCons ON)
	endif()

	if (EXISTS ${BREW_DEPS_DIR}/protobuf OR EXISTS ${Target_DIR_Protobuf})
		SET(Installed_Protobuf ON)
	endif()

	if (EXISTS ${BREW_DEPS_DIR}/grpc OR EXISTS ${Target_DIR_gRPC})
		SET(Installed_gRPC ON)
	endif()

	if (EXISTS ${Target_DIR_PortableFileDialogs})
		SET(Installed_PortableFileDialogs ON)
	endif()

endmacro(check_installed_state)

# Installs the Elements C++ GUI Framework.
# Elements is a lightweight, resolution independent, GUI library written in C++.
#
function(install_elements)

	if (Installed_Elements)
		publish_package_found("Elements" ON)
		return()
	endif()

	# Ensure the dependency directory exists for CMake.
	#
	FILE(MAKE_DIRECTORY ${PROJECT_DEPENDENCY_DIR})

	if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
		# Notes:
		# '--recurse-submodules' is not supported by CMake at the time of this writing.
		# 'nohup' is required for 'execute_process' to succeed.
		#
		if (NOT EXISTS "${PROJECT_DEPENDENCY_DIR}/elements-src")
			execute_process(COMMAND nohup "git" "clone" "--recurse-submodules" "${URL_Elements}" "elements-src"
					WORKING_DIRECTORY "${PROJECT_DEPENDENCY_DIR}")
		endif()

		if (EXISTS "${PROJECT_DEPENDENCY_DIR}/elements")
			include_directories("${PROJECT_DEPENDENCY_DIR}/elements-src/lib/include")
		endif()

		# These libraries will be installed by the Homebrew package manager.
		#
		LIST(APPEND ELEMENTS_DEPENDENCIES_LIST
				"/usr/local/Cellar/cairo"
				"/usr/local/Cellar/fontconfig"
				"/usr/local/Cellar/bzip2"
				"/usr/local/Cellar/expat"
				"/usr/local/Cellar/zlib"
				"/usr/local/Cellar/libpng"
				)

		foreach(dependency_path IN LISTS ELEMENTS_DEPENDENCIES_LIST)
			STRING(REPLACE "/usr/local/Cellar/"  "" name  ${dependency_path})
			if (NOT EXISTS "${dependency_path}")
				#
				# Note 'nohup' is required for 'execute_process' to succeed
				# in running the Homebrew package manager.
				#
				execute_process(COMMAND nohup ${PACKAGE_MANAGER_PATH} "install" ${name})
			endif()
		endforeach()

	elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
		MESSAGE(FATAL_ERROR "TODO: 'install_elements' NOT IMPLEMENTED!")
	elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
		#
		# Available CMake modules:
		# https://cmake.org/cmake/help/latest/manual/cmake-modules.7.html
		MESSAGE(FATAL_ERROR "TODO: 'install_elements' NOT IMPLEMENTED!")

		# TODO
		#cd elements
		#mkdir build
		#cd build
		#cmake -G"Visual Studio 16 2019" ..//

	endif()
endfunction(install_elements)

function (install_slx_dependencies)

	diagnostic_message("\nCalling 'install_slx_dependencies' func.")

	check_installed_state()

	#                       repo_name                  url             version            installed_state
	fetch_repository("${Target_Name_Catch2}"   "${URL_Catch2}"   "${Version_Catch2}"   "${Installed_Catch2}")
	fetch_repository("${Target_Name_Cista}"    "${URL_Cista}"    "${Version_Cista}"    "${Installed_Cista}" )
	fetch_repository("${Target_Name_FmtLib}"   "${URL_FmtLib}"   "${Version_FmtLib}"   "${Installed_FmtLib}")
	fetch_repository("${Target_Name_JSONCons}" "${URL_JSONCons}" "${Version_JSONCons}" "${Installed_JSONCons}")
	fetch_repository("${Target_Name_PortableFileDialogs}"
			"${URL_PortableFileDialogs}"
			"${Version_PortableFileDialogs}"
			"${Installed_PortableFileDialogs}"
	)

	install_elements()

	MESSAGE(STATUS "")

endfunction(install_slx_dependencies)

function (install_grpc)

	# Sets the Installed_{package name} state flags.
	# Dependencies found will be ignored.
	#
	check_installed_state()

	if (USE_PACKAGE_MANAGER)
		LIST(APPEND gRPC_DEPENDENCIES_LIST
				"${BREW_DEPS_DIR}/autoconf"
				"${BREW_DEPS_DIR}/automake"
				"${BREW_DEPS_DIR}/libtool"
				"${BREW_DEPS_DIR}/shtool"
				"${BREW_DEPS_DIR}/${OPEN_SSL_PATH}"
				"${BREW_DEPS_DIR}/grpc"
				#
				# Note:
				# brew will install the gRPC protobuf dependency to /usr/local/Cellar/protobuf
		)

		foreach(dependency_path IN LISTS gRPC_DEPENDENCIES_LIST)
			STRING(REPLACE "${BREW_DEPS_DIR}/"  "" name  ${dependency_path})
			if (NOT EXISTS "${dependency_path}")
				#
				# Since OpenSSL breaks the naming conventions for a brew package
				# the following specialize code is required.
				#
				# Note this section of code WILL BREAK when brew installs
				# a newer version of the Open SSL library.
				#
				# Why OpenSSL did you have to be different?
				#
				if (${OPEN_SSL_PATH} STREQUAL ${name})
					SET(name "openssl")
				endif()
				execute_process(COMMAND nohup ${PACKAGE_MANAGER_PATH} "install" ${name})
			endif()
		endforeach()

	else()
		if (NOT Installed_Protobuf)
			fetch_repository(${Target_Name_Protobuf} ${URL_Protobuf} ${Version_Protobuf} ${Installed_Protobuf})
		endif()

		if (NOT Installed_gRPC)
			#
			# I did not find a means to enable '--recurse-submodules' with
			# CMake.
			#
			execute_process(COMMAND
					nohup "git" "clone" "--recurse-submodules"
					"-b" "${Version_gRPC}"
					${URL_Protobuf}
					${Target_DIR_gRPC})
		endif()

	endif(USE_PACKAGE_MANAGER)

	check_installed_state()

endfunction(install_grpc)

function(verify_grpc_dependencies)

	check_installed_state()

	find_package(gRPC CONFIG REQUIRED)

	if(gRPC_FOUND)
		set(GRPCPP_INCLUDE_DIRS $<TARGET_PROPERTY:gRPC::grpc++,INTERFACE_INCLUDE_DIRECTORIES>)
		set(GRPCPP_LIBRARIES $<TARGET_PROPERTY:gRPC::grpc++,INTERFACE_LINK_LIBRARIES>)
	endif()

	# Have CMake find these packages:
	#
	include(FindProtobuf)
	SET(Installed_Protobuf ${Protobuf_FOUND})

	include(FindOpenSSL)
	SET(Installed_OpenSSL ${OPENSSL_FOUND})

	MESSAGE(STATUS "\nVerifying Install:")
	publish_package_found("gRPC" ${gRPC_FOUND})
	publish_package_found("Protobuf" ${Protobuf_FOUND})
	publish_package_found("Open SSL" ${OPENSSL_FOUND})

	if (gRPC_FOUND)
		MESSAGE(STATUS "\nInstalled Source Folders:")
		if (USE_PACKAGE_MANAGER)
			MESSAGE(STATUS " - gRPC: '${BREW_DEPS_DIR}/grpc'")
		else()
			MESSAGE(STATUS " - gRPC: '${Target_DIR_gRPC}'")
		endif()

		if (Protobuf_FOUND)
			if (USE_PACKAGE_MANAGER)
				MESSAGE(STATUS " - gRPC: '${BREW_DEPS_DIR}/protobuf'")
			else()
				MESSAGE(STATUS " - gRPC: '${Target_DIR_Protobuf}'")
			endif()
		endif()

		if (OPENSSL_FOUND)
			if (USE_PACKAGE_MANAGER)
				MESSAGE(STATUS " - Open SSL: '${OPEN_SSL_PATH}'\n")
			else()
				MESSAGE(FATAL_ERROR " - Open SSL: NOT IMPLEMENTED")
			endif()
		endif()
	endif()
endfunction(verify_grpc_dependencies)

function (verify_slx_dependencies)
	check_installed_state()
	publish_package_found("Catch2"   ${Installed_Catch2})
	publish_package_found("Cista"    ${Installed_Cista})
	publish_package_found("Elements" ${Installed_Elements})
	publish_package_found("FmtLib"   ${Installed_FmtLib})
	publish_package_found("JSONCons" ${Installed_JSONCons})
	publish_package_found("PortableFileDialogs" ${Installed_PortableFileDialogs})
endfunction(verify_slx_dependencies)

macro(find_grpc_package)
	find_package(gRPC CONFIG)
	if(gRPC_FOUND)
		verify_grpc_dependencies()
	else()
		MESSAGE("USE_SYSTEM_INSTALLED_COMPONENTS was selected but a gRPC installation was not located!")
		RETURN()
	endif()
endmacro(find_grpc_package)