

# Function injects variables from __ARG_IN into the calling scope, so the variables are ready to be used by user-supplied function 
# generate_targets() or declare_dependencies()
function(_instantiate_variables __ARGS __PARS __ARGS_LIST)
	if(NOT __ARGS_LIST)
		return()
		message(FATAL_ERROR "No variables to instantiate")
	endif()
	
	if(__BEETROOT_CLEAN_ALL_VARIABLES)
		get_cmake_property(__ALL_VARIABLES VARIABLES)
		foreach(__VAR IN LISTS __ALL_VARIABLES)
			if(NOT "${__VAR}" MATCHES "^__" AND NOT "${__VAR}" MATCHES "^CMAKE_" AND NOT "${__VAR}" MATCHES "^PROJECT_" AND NOT "${__VAR}" MATCHES "^ARG[CN]$" AND NOT "${__VAR}" MATCHES "^ARGV[0-9]?[0-9]?$")
#				message(STATUS "${__PADDING}_instantiate_variables(): Zeroing ${__VAR}")
				set(${__VAR})
			endif()
		endforeach()
	endif()
	
	foreach(__VAR IN LISTS __ARGS_LIST)
		if(__PARS)
			if(NOT ${__PARS}_${__VAR}__CONTAINER)
				message(FATAL_ERROR "Internal beetroot error: Unknown container: ${__PARS}_${__VAR}__CONTAINER is empty")
			endif()
			if("${${__PARS}_${__VAR}__CONTAINER}" STREQUAL "OPTION")
				if(${__ARGS}_${__VAR})
					set(${__VAR} "1" PARENT_SCOPE)
				else()
					set(${__VAR} "0" PARENT_SCOPE)
				endif()
			else()
				set(${__VAR} ${${__ARGS}_${__VAR}} PARENT_SCOPE)
			endif()
		else()
			set(${__VAR} ${${__ARGS}_${__VAR}} PARENT_SCOPE)
		endif()
	endforeach()
endfunction()

function(_make_cmake_args __PARS __ARGS __ARGS_LIST __OUT_CMAKE_ARGS)
#	message(STATUS "${__PADDING}_make_cmake_args(): __PARS: ${__PARS} __ARGS: ${__ARGS} __ARGS_LIST: ${__ARGS_LIST}")
	set(__CMAKE_ARGS)
	foreach(__VAR IN LISTS __ARGS_LIST)
#		message(STATUS "${__PADDING}_make_cmake_args(): processing ${__ARGS}_${__VAR}. Container ${__PARS}_${__VAR}__CONTAINER: ${${__PARS}_${__VAR}__CONTAINER}") # Container ${__PARS}_${__VAR}_CONTAINER: ${${__PARS}_${__VAR}_CONTAINER}, ${__PARS}_${__VAR}_TYPE: ${{__PARS}_${__VAR}_TYPE}
		if("${${__PARS}_${__VAR}__CONTAINER}" STREQUAL "OPTION" )	
			if(${__ARGS}_${__VAR})
				list(APPEND __CMAKE_ARGS "-D${__VAR}:BOOL=ON")
			else()
				list(APPEND __CMAKE_ARGS "-D${__VAR}:BOOL=OFF")
			endif()
		elseif("${${__PARS}_${__VAR}__TYPE}" STREQUAL "INTEGER" OR 
				"${${__PARS}_${__VAR}__TYPE}" STREQUAL "VERSION"  OR 
				"${${__PARS}_${__VAR}__TYPE}" STREQUAL "STRING"  OR 
				"${${__PARS}_${__VAR}__TYPE}" MATCHES "CHOICE\(.*\)")
			list(APPEND __CMAKE_ARGS "-D${__VAR}:STRING=${${__ARGS}_${__VAR}}")
		elseif("${${__PARS}_${__VAR}__TYPE}" STREQUAL "BOOL")
			list(APPEND __CMAKE_ARGS "-D${__VAR}:BOOL=${${__ARGS}_${__VAR}}")
		else()
			message(FATAL_ERROR "Unknown way to pass an argument ${__VAR} into the cmake external project, because it is ${${__PARS}_${__VAR}__CONTAINER} of type ${${__PARS}_${__VAR}__TYPE} and passing that type of arguments to the external project is not supported. You should convert it to the STRING.")
		endif()
	endforeach()
#	message(STATUS "${__PADDING}_make_cmake_args(): finished processing. ${__OUT_CMAKE_ARGS}: ${__CMAKE_ARGS}") 
	set(${__OUT_CMAKE_ARGS} ${__CMAKE_ARGS} PARENT_SCOPE)
endfunction()

# __FILE_HASH - file identifier needed for default values of the parameters.
#
# Return value __OUT_RELATION: 0 - the same, 1 - ARG1 has more features, 2 - ARG2 has more features, 3 - both ARG1 and ARG2 should be promoted 4 - No way to promote either of them
function(_single_feature_relation __FILE_HASH __VARNAME __PARS_PREFIX __ARG1_PREFIX __ARG2_PREFIX __OUT_RELATION)
#	message(STATUS "${__PADDING}_single_feature_relation(): __FILE_HASH: ${__FILE_HASH} __VARNAME: ${__VARNAME} __PARS_PREFIX: ${__PARS_PREFIX} __ARG1_PREFIX: ${__ARG1_PREFIX} __ARG2_PREFIX: ${__ARG2_PREFIX}")
#	message(STATUS "${__PADDING}_single_feature_relation(): __FILE_HASH: ${__FILE_HASH} ${__ARG1_PREFIX}_${__VARNAME}: ${${__ARG1_PREFIX}_${__VARNAME}} ${__ARG2_PREFIX}_${__VARNAME}: ${${__ARG2_PREFIX}_${__VARNAME}}")
	if("${${__PARS_PREFIX}_${__VARNAME}__CONTAINER}" STREQUAL "OPTION")
		if(${__ARG1_PREFIX}_${__VARNAME})
			if(${__ARG2_PREFIX}_${__VARNAME})
				set(${__OUT_RELATION} 0 PARENT_SCOPE)
			else()
				set(${__OUT_RELATION} 1 PARENT_SCOPE)
			endif()
		else()
			if(${__ARG2_PREFIX}_${__VARNAME})
				set(${__OUT_RELATION} 2 PARENT_SCOPE)
			else()
				set(${__OUT_RELATION} 0 PARENT_SCOPE)
			endif()
		endif()
	elseif("${${__PARS_PREFIX}_${__VARNAME}__CONTAINER}" STREQUAL "SCALAR")
		_retrieve_file_args("${__FILE_HASH}" DEFAULTS __ARG_DEFAULTS)
		if("${${__ARG1_PREFIX}_${__VARNAME}}" STREQUAL "${${__ARG2_PREFIX}_${__VARNAME}}")
			set(${__OUT_RELATION} 0 PARENT_SCOPE)
		else()
			if("${${__PARS_PREFIX}_${__VARNAME}__TYPE}" STREQUAL "INTEGER")
				if("${${__ARG1_PREFIX}_${__VARNAME}}" GREATER "${${__ARG2_PREFIX}_${__VARNAME}}")
					set(${__OUT_RELATION} 1 PARENT_SCOPE)
				else()
					set(${__OUT_RELATION} 2 PARENT_SCOPE)
				endif()
			elseif("${${__PARS_PREFIX}_${__VARNAME}__TYPE}" STREQUAL "VERSION")
				if("${${__ARG1_PREFIX}_${__VARNAME}}" VERSION_GREATER "${${__ARG2_PREFIX}_${__VARNAME}}")
					set(${__OUT_RELATION} 1 PARENT_SCOPE)
				else()
					set(${__OUT_RELATION} 2 PARENT_SCOPE)
				endif()
			elseif("${${__PARS_PREFIX}_${__VARNAME}__TYPE}" STREQUAL "BOOL" OR 
				"${${__PARS_PREFIX}_${__VARNAME}__TYPE}" STREQUAL "STRING" OR
				"${${__PARS_PREFIX}_${__VARNAME}__TYPE}" STREQUAL "PATH" OR
				"${${__PARS_PREFIX}_${__VARNAME}__TYPE}" MATCHES "^CHOICE.*")
				if("${__ARG_DEFAULTS_${__VARNAME}}" STREQUAL "${${__ARG1_PREFIX}_${__VARNAME}}" )
					if("${__ARG_DEFAULTS_${__VARNAME}}" STREQUAL "${${__ARG2_PREFIX}_${__VARNAME}}" )
						message(FATAL_ERROR "Internal beetroot error: this code path should be dead")
					else()
						set(${__OUT_RELATION} 2 PARENT_SCOPE)
					endif()
				else()
					if("${__ARG_DEFAULTS_${__VARNAME}}" STREQUAL "${${__ARG2_PREFIX}_${__VARNAME}}" )
						set(${__OUT_RELATION} 1 PARENT_SCOPE)
					else()
						message(WARNING "_single_feature_relation(): No way to unite ${__ARG1_PREFIX}_${__VARNAME}: ${${__ARG1_PREFIX}_${__VARNAME}}  and ${__ARG2_PREFIX}_${__VARNAME}: ${${__ARG2_PREFIX}_${__VARNAME}} because both are non-default and different (__ARG_DEFAULTS_${__VARNAME}: ${__ARG_DEFAULTS_${__VARNAME}})")
						set(${__OUT_RELATION} 4 PARENT_SCOPE)
					endif()
				endif()
			else()
				message(FATAL_ERROR "Internal beetroot error: Unknown variable type: ${__PARS_PREFIX}_${__VARNAME}__TYPE: ${${__PARS_PREFIX}_${__VARNAME}__TYPE}")
			endif()
		endif()
	elseif("${${__PARS_PREFIX}_${__VARNAME}__CONTAINER}" STREQUAL "VECTOR")
		list_diff(__DIFF12 ${__ARG1_PREFIX}_${__VARNAME} ${__ARG2_PREFIX}_${__VARNAME})
		list_diff(__DIFF21 ${__ARG2_PREFIX}_${__VARNAME} ${__ARG1_PREFIX}_${__VARNAME})
#		message(STATUS "${__PADDING}_single_feature_relation(): __DIFF12: ${__DIFF12} __DIFF21: ${__DIFF21}")
		if(__DIFF12)
			if(__DIFF21)
				set(${__OUT_RELATION} 3 PARENT_SCOPE)
			else()
				set(${__OUT_RELATION} 1 PARENT_SCOPE)
			endif()
		else()
			if(__DIFF21)
				set(${__OUT_RELATION} 2 PARENT_SCOPE)
			else()
				set(${__OUT_RELATION} 0 PARENT_SCOPE)
			endif()
		endif()
	else()
		message(FATAL_ERROR "Internal beetroot error: unsupported container ${__PARS_PREFIX}_${__VARNAME}__CONTAINER: ${${__PARS_PREFIX}_${__VARNAME}__CONTAINER}")
	endif()
endfunction()

function(_compare_featuresets __FILE_HASH __PARS_PREFIX __ARG1_PREFIX __ARG2_PREFIX __OUT_RELATION)
	list_union(__VARS ${__ARG1_PREFIX}__LIST ${__ARG2_PREFIX}__LIST)
#	message(STATUS "${__PADDING}_compare_featuresets(): __VARS: ${__VARS} __PARS_PREFIX: ${__PARS_PREFIX}")
	_make_promoted_featureset(${__FILE_HASH} "${__VARS}" ${__PARS_PREFIX} ${__ARG1_PREFIX} ${__ARG2_PREFIX} __TMP __COMP)
	set(${__OUT_RELATION} ${__COMP} PARENT_SCOPE)
endfunction()


#Function compiles a set of smallest features that are greater or equal to features in __ARG1_PREFIX and __ARG2_PREFIX. 
#It also returns __OUT_RELATION that inform what is the direction of the changes.
#If __OUT_RELATION==3 it means that there is no way to promote both featuresets to the common denominator, 
#  and __OUT_ARGS__LIST points to the list of the conflict variables.
function(_make_promoted_featureset __FILE_HASH __VARNAMES __PARS_PREFIX __ARG1_PREFIX __ARG2_PREFIX __OUT_ARGS __OUT_RELATION)
	set(__RESULT_RELATION 0)
	set(__CONFLICTS)
	set(__COMMON__LIST)
#	message(STATUS "${__PADDING}_make_promoted_featureset(): __VARNAMES: ${__VARNAMES}")
	foreach(__VARNAME IN LISTS __VARNAMES)
		list(APPEND __COMMON__LIST ${__VARNAME})
		_single_feature_relation(${__FILE_HASH} ${__VARNAME} ${__PARS_PREFIX} ${__ARG1_PREFIX} ${__ARG2_PREFIX} __SINGLE_RELATION)
#		message(STATUS "${__PADDING}_make_promoted_featureset(): __VARNAME: ${__VARNAME}, ${__ARG1_PREFIX}: ${${__ARG1_PREFIX}_${__VARNAME}} vs ${__ARG2_PREFIX}: ${${__ARG2_PREFIX}_${__VARNAME}} __SINGLE_RELATION: ${__SINGLE_RELATION}")
		if("${__SINGLE_RELATION}" STREQUAL "0")
			set(__COMMON_${__VARNAME} ${${__ARG1_PREFIX}_${__VARNAME}})
		elseif("${__SINGLE_RELATION}" STREQUAL "1")
			set(__COMMON_${__VARNAME} ${${__ARG1_PREFIX}_${__VARNAME}})
			list(APPEND __LEFTS ${__VARNAME})
			if("${__RESULT_RELATION}" STREQUAL "2" OR "${__RESULT_RELATION}" STREQUAL "3")
				set(__RESULT_RELATION 3)
			elseif("${__RESULT_RELATION}" STREQUAL "0" OR "${__RESULT_RELATION}" STREQUAL "1")
				set(__RESULT_RELATION 1)
			elseif("${__RESULT_RELATION}" STREQUAL "4")
				#do nothing
			else()
				message(FATAL_ERROR "Internal beetroot error: dead code")
			endif()
		elseif("${__SINGLE_RELATION}" STREQUAL "2")
			set(__COMMON_${__VARNAME} ${${__ARG2_PREFIX}_${__VARNAME}})
			list(APPEND __RIGHTS ${__VARNAME})
			if("${__RESULT_RELATION}" STREQUAL "1" OR "${__RESULT_RELATION}" STREQUAL "3")
				set(__RESULT_RELATION 3)
			elseif("${__RESULT_RELATION}" STREQUAL "0" OR "${__RESULT_RELATION}" STREQUAL "2")
				set(__RESULT_RELATION 2)
			elseif("${__RESULT_RELATION}" STREQUAL "4")
				#do nothing
			else()
				message(FATAL_ERROR "Internal beetroot error: dead code")
			endif()
		elseif("${__SINGLE_RELATION}" STREQUAL "3")
			list_union(__COMMON_${__VARNAME} ${__ARG2_PREFIX}_${__VARNAME} ${__ARG1_PREFIX}_${__VARNAME})
			list(APPEND __RIGHTS ${__VARNAME})
			list(APPEND __LEFTS ${__VARNAME})
			if("${__RESULT_RELATION}" STREQUAL "0" OR "${__RESULT_RELATION}" STREQUAL "1" OR "${__RESULT_RELATION}" STREQUAL "2" OR "${__RESULT_RELATION}" STREQUAL "3")
				set(__RESULT_RELATION 3)
			elseif("${__RESULT_RELATION}" STREQUAL "4")
				#do nothing
			else()
				message(FATAL_ERROR "Internal beetroot error: dead code")
			endif()
		elseif("${__SINGLE_RELATION}" STREQUAL "4")
			list(APPEND __CONFLICTS ${__VARNAME})
			set(__RESULT_RELATION 4)
		else()
			message(FATAL_ERROR "Internal beetroot error: dead branch. This statement should not have executed.")
		endif()
	endforeach()
	set(${__OUT_RELATION} "${__RESULT_RELATION}" PARENT_SCOPE)
	if("${__RESULT_RELATION}" STREQUAL "4")
		set(${__OUT_ARGS}__LIST ${__CONFLICTS} PARENT_SCOPE)
	else()
		_pass_arguments_higher(__COMMON ${__OUT_ARGS})
	endif()
endfunction()

function(__rename_arguments __IN_PREFIX __OUT_PREFIX)
	_pass_arguments_higher(${__IN_PREFIX} ${__OUT_PREFIX})
#	message(STATUS "${__PADDING}__rename_arguments(): ${__IN_PREFIX}_MYPAR: ${${__IN_PREFIX}_MYPAR}, ${__OUT_PREFIX}_MYPAR: ${${__OUT_PREFIX}_MYPAR}")
endfunction()

macro(_pass_arguments_higher __IN_PREFIX __OUT_PREFIX)
#	message(FATAL_ERROR "${__IN_PREFIX}__LIST: ${${__IN_PREFIX}__LIST}")
	foreach(__VAR IN LISTS ${__IN_PREFIX}__LIST)
#		message(FATAL_ERROR "set(${__OUT_PREFIX}_${__VAR} \"${${__IN_PREFIX}_${__VAR}}\" PARENT_SCOPE)")
		set(${__OUT_PREFIX}_${__VAR} ${${__IN_PREFIX}_${__VAR}} PARENT_SCOPE)
		if(NOT "${${__IN_PREFIX}__SRC_${__VAR}}" STREQUAL "")
			set(${__OUT_PREFIX}__SRC_${__VAR} ${${__IN_PREFIX}__SRC_${__VAR}} PARENT_SCOPE)
		endif()
	endforeach()
	set(${__OUT_PREFIX}__LIST ${${__IN_PREFIX}__LIST} PARENT_SCOPE)
	set(${__OUT_PREFIX}__LIST_MODIFIERS ${${__IN_PREFIX}__LIST_MODIFIERS} PARENT_SCOPE)
	set(${__OUT_PREFIX}__LIST_FEATURES  ${${__IN_PREFIX}__LIST_FEATURES} PARENT_SCOPE)
	set(${__OUT_PREFIX}__LIST_LINKPARS  ${${__IN_PREFIX}__LIST_LINKPARS} PARENT_SCOPE)
endmacro()

macro(_pass_parameters_higher __IN_PREFIX __OUT_PREFIX)
	foreach(__VAR IN LISTS ${__IN_PREFIX}__LIST)
		set(${__OUT_PREFIX}_${__VAR}__CONTAINER ${${__IN_PREFIX}_${__VAR}__CONTAINER} PARENT_SCOPE)
		set(${__OUT_PREFIX}_${__VAR}__TYPE ${${__IN_PREFIX}_${__VAR}__TYPE} PARENT_SCOPE)
		set(${__OUT_PREFIX}_${__VAR}__DEFAULT ${${__IN_PREFIX}_${__VAR}__DEFAULT} PARENT_SCOPE)
	endforeach()
	set(${__OUT_PREFIX}__LIST ${${__IN_PREFIX}__LIST} PARENT_SCOPE)
	set(${__OUT_PREFIX}__LIST_MODIFIERS ${${__IN_PREFIX}__LIST_MODIFIERS} PARENT_SCOPE)
	set(${__OUT_PREFIX}__LIST_FEATURES  ${${__IN_PREFIX}__LIST_FEATURES} PARENT_SCOPE)
	set(${__OUT_PREFIX}__LIST_LINKPARS  ${${__IN_PREFIX}__LIST_LINKPARS} PARENT_SCOPE)
endmacro()

#Removes all variables which source is default from ARGS __IN_ARGS
function(_remove_default_variables __IN_ARGS __IN_ARGS_LIST __OUT_ARGS)
	set(__TMP_LIST)
	foreach(__VAR IN LISTS ${__IN_ARGS_LIST})
		if("${${__IN_ARGS}__SRC_${__VAR}}" STREQUAL "")
			message(FATAL_ERROR "Internal beetroot error: unknown source for argument ${__VAR}")
		endif()
		if(NOT "${${__IN_ARGS}__SRC_${__VAR}}" STREQUAL "default")
#			message(STATUS "${__PADDING}_remove_default_variables(): non-default source of ${__IN_ARGS}__SRC_${__VAR}: ${${__IN_ARGS}__SRC_${__VAR}}")
			list(APPEND __TMP_LIST "${__VAR}")
		endif()
	endforeach()
#	set(${__IN_ARGS}__LIST ${__TMP_LIST})
#	message(STATUS "${__PADDING}_remove_default_variables(): ${__IN_ARGS}__LIST: ${${__IN_ARGS}__LIST}")
	set(${__OUT_ARGS} ${__TMP_LIST} PARENT_SCOPE)
endfunction()

function(_remove_variables_with_default_value __IN_ARGS __IN_PARS __IN_ARGS_LIST __OUT_ARGS)
	set(__TMP_LIST)
	foreach(__VAR IN LISTS ${__IN_ARGS_LIST})
#		message(STATUS "${__PADDING}_remove_variables_with_default_value(): testing variable ${__VAR} with value ${${__IN_ARGS}_${__VAR}} and comparing with the default of ${__IN_PARS}_${__VAR}__DEFAULT: ${${__IN_PARS}_${__VAR}__DEFAULT}")
		if(NOT "${${__IN_PARS}_${__VAR}__DEFAULT}" STREQUAL "${${__IN_ARGS}_${__VAR}}")
			list(APPEND __TMP_LIST "${__VAR}")
#			message(STATUS "${__PADDING}_remove_variables_with_default_value(): variable ${__VAR}  changed from \"${${__IN_ARGS}_${__VAR}}\" to \"${${__IN_PARS}_${__VAR}__DEFAULT}\".")
		endif()
	endforeach()
#	set(${__IN_ARGS}__LIST ${__TMP_LIST})
#	message(STATUS "${__PADDING}_remove_variables_with_default_value(): __IN_ARGS__LIST->${__IN_ARGS}__LIST->${${__IN_ARGS}__LIST}")
	set(${__OUT_ARGS} ${__TMP_LIST} PARENT_SCOPE)
endfunction()

#Clears all variables in list __IN_ARGS_LIST
function(_clear_variables __IN_ARGS_LIST)
	foreach(__VAR IN LISTS ${__IN_ARGS_LIST})
#		if("${__VAR}" STREQUAL "FLOAT_PRECISION")
#			message(WARNING "Clearing FLOAT_PRECISION=${FLOAT_PRECISION}...")
#		endif()
		unset(__VAR PARENT_SCOPE)
#		if("${__VAR}" STREQUAL "FLOAT_PRECISION")
#			message(STATUS "${__PADDING}_clear_variables(): After clearing, FLOAT_PRECISION=${FLOAT_PRECISION}")
#		endif()
	endforeach()
endfunction()

# forces all variables in list __IN_ARGS_LIST but not in __BLACKLIST_LIST to contain ""
function(_blank_variables __IN_ARGS_LIST __BLACKLIST_LIST)
#	message(STATUS "${__PADDING}_blank_variables():")
	foreach(__VAR IN LISTS ${__IN_ARGS_LIST})
		if(NOT "${__VAR}" IN_LIST __BLACKLIST_LIST)
			set(${__VAR} "" PARENT_SCOPE)
		endif()
	endforeach()
endfunction()


