## CMAKE_DOCUMENTATION_START DLWin3rdPartySDK.cmake
## This file should be include and use only on WIN32 OS and once. \\n
## It allow to auto check/download and use a preconfigured 3rdParty binaries for cmake usage. \\n
## It use the downloadAndExtractZipFile cmake module to work.
## CMAKE_DOCUMENTATION_END
## Created/updated by jesnault while last cmake version was 3.0.2
if(__Win3rdParty_cmake_INCLUDED__)
	return()
else()
	set(__Win3rdParty_cmake_INCLUDED__ ON)
endif()

cmake_minimum_required(VERSION 3.0)

## CMAKE_DOCUMENTATION_START check_cached_var
## To be sur to reset an empty cached variable but keep any other kind of variables. \\n
##  \\n
## Usage:
## \\code
## check_cached_var(<var> <resetedCachedValue> <cacheType> <cacheDoc> [FORCE])
## \\endcode
## \\n
## <var> is the cached cmake variable you need to reset  \\n
## <resetedCachedValue> is the new default value of the reseted cached cmake variable \\n
## <cacheType> is the kind of GUI cache input can be : FILEPATH; PATH; STRING or BOOL \\n
## <cacheDoc> is the associated GUI cache input documentation display in the GUI \\n
## FORCE option could be use to reset a cached variable even if it is not empty. \\n
## CMAKE_DOCUMENTATION_END
## Created by jesnault
macro(check_cached_var var resetedCachedValue cacheType cacheDoc)
    include(CMakeParseArguments)
    cmake_parse_arguments(ccv "FORCE" "" "" ${ARGN})
    if(ccv_FORCE)
        set(FORCE FORCE)
    else()
        set(FORCE )
    endif()
	if(NOT ${var} OR ccv_FORCE)
		unset(${var} CACHE)
		set(${var}	"${resetedCachedValue}" CACHE ${cacheType} "${cacheDoc}" ${FORCE})
	endif()
endmacro()


## CMAKE_DOCUMENTATION_START DLWin3rdPartySDK
## DLWin3rdPartySDK function allow to specify a directory which contain all necessary windows dependenties.
## By uploading 3rdParty directory (which contain dependencies, *.lib, *.dll... for a specific version of compiler) onto Gforge file tab,
## you get back an URL of download you can give to this function with a directory name. So you can provide multiple 3rdParty version of same dependencies (MSVC11, MSVC12...).
## By providing a prefix to this function, you allow to use different kind of 3rdParty which can be handled by CMAKE OPTIONS depending on what your framework need for example.
##
## Usage 1: 
##    DLWin3rdPartySDK(<prefix> MSVC<XX> <DirName> <URL>
##					[MSVC<XX> <DirName> <URL>] [...]
##					[VCID] [DEFAULT_USE] [VERBOSE] )
##
## * <prefix> allow to identify which 3rdParty you process (prefix name)
## * MSVC<XX> flag could be MSVC11 or MSVC12 (any element of the MSVC_VERSIONS_LIST) and refer to a 3rdParty compiler with :
##   * <DirName> which will be the local pathName of the downloaded 3rdParty : relative to CMAKE_BINARY_DIR 
##   * <URL> which is the link location of the 3rdParty zip
## * VCID flag will make available a cache variable ${prefix}_WIN3RDPARTY_VCID
## * DEFAULT_USE flag [ON|OFF] may be used to set default value of cmake cached variable : <prefix>_WIN3RDPARTY_USE [default to ON]
##
## WARNING: 
## This function define CACHE variables you can use after :
## * ${prefix}_WIN3RDPARTY_USE : allow to check/downloaded win3rdParty dir (it will force the cached variables for this dependency folder generally <prefix>_DIR>)
## * ${prefix}_WIN3RDPARTY_DIR : where is your local win3rdParty dir (the PATH)
## * ${prefix}_WIN3RDPARTY_VCID : [if VCID flag is used] the MSVC id (commonly used to prefix/suffix library name, see boost or CGAL)
##
## If you want to add a win3rdParty version, please:
## 1- build dependencies on your local side with the compiler you want
## 2- build your own zip with your built dependencies
## 3- upload it (onto the forge where the project is stored) and copy the link location in order to use it for this function
## 4- if you just introduced a new MSVC version, add it to the MSVC_VERSIONS_LIST bellow
##
## In a second pass, you can also use this function to set necessary cmake cached variables in order to let cmake find packages of these 3rdParty.
##
## Usage 2:
##    DLWin3rdPartySDK(<prefix> [VERBOSE] MULTI_SET|SET 
##          CHECK_CACHED_VAR <cmakeVar> <cmakeCacheType> [LIST] <cmakeValue> [DOC <stringToolTips>]
##        [ CHECK_CACHED_VAR <cmakeVar> <cmakeCacheType> [LIST] <cmakeValue> [DOC <stringToolTips>] ] [...]
##
## * MULTI_SET or SET flags are used to tell cmake that all next arguments will use repeated flags with differents entries (SET mean we will provide only one set of arguments, without repetition)
## * CHECK_CACHED_VAR are the repeated flag which contain differents entries
##      * <cmakeVar> is the cmake variable you want to be cached for the project
##      * <cmakeCacheType> is the kind of cmake variable (couble be: FILEPATH; PATH; STRING or BOOL) => see check_cached_var.
##      * LIST optional flag could be used with CHECK_CACHED_VAR when <cmakeCacheType> = STRING. It allow to handle multiple STRINGS value list.
##      * <cmakeValue> is the value of the variable (if FILEPATH, PATH or STRING: use quotes, if BOOL : use ON/OFF)
##      * DOC optional flag is used to have a tooltips info about this new cmake variable entry into the GUI (use quotes).
##
## Full example 1 :
##    DLWin3rdPartySDK(COMMON MSVC11 "DLWin3rdPartySDK-MSVC11" "https://gforge.inria.fr/frs/download.php/file/33885/win3rdParty-MSVC11.7z"
##                SET CHECK_CACHED_VAR SuiteSparse_DIR PATH "SuiteSparse-4.2.1" DOC "default empty doc"
##    ) 
## 
## WARNING:
## For the 2nd usage (with MULTI_SET), if you planned to set some CACHED_VAR using/composed by ${prefix}_WIN3RDPARTY_* just set in this macro (usage 1),
## then (due to the not yet existing var) you will need to call this function 2 times :
## One for the 1st usage (downloading of the current compiler 3rdParty).
## One for the MLUTI_SET flag which will use existsing ${prefix}_WIN3RDPARTY_* cached var.
##
## Full example 2 :
##    DLWin3rdPartySDK(COMMON MSVC11    "DLWin3rdPartySDK-MSVC11" "https://gforge.inria.fr/frs/download.php/file/33885/win3rdParty-MSVC11.7z")
##    DLWin3rdPartySDK(COMMON MULTI_SET 
##       CHECK_CACHED_VAR CGAL_INCLUDE_DIR  PATH "CGAL-4.3/include" DOC "default empty doc"
##       CHECK_CACHED_VAR CGAL_LIBRARIES	STRING LIST "debug;CGAL-4.3/lib${LIB_POSTFIX}/CGAL-${WIN3RDPARTY_COMMON_VCID}-mt-gd-4.3.lib;optimized;CGAL-4.3/lib${LIB_POSTFIX}/CGAL-${WIN3RDPARTY_COMMON_VCID}-mt-4.3.lib"
##
##
## WARNING: This function use internaly :
## * downloadAndExtractZipFile.cmake
## * parse_arguments_multi.cmake
## * check_cached_var macro
##
## Created by jerome esnault
function(DLWin3rdPartySDK prefix )

    if(NOT WIN32)
        return()
    endif()

    ## set the handled version of MSVC 
    ## if you plan to add a win3rdParty dir to download with a new MSVC version: build the win3rdParty dir and add the MSCV entry here.
    set(MSVC_VERSIONS_LIST "MSVC11;MSVC12")

    include(CMakeParseArguments)
    cmake_parse_arguments(w3p "VCID" "VERBOSE;TIMEOUT;DEFAULT_USE" "${MSVC_VERSIONS_LIST};MULTI_SET;SET" ${ARGN})
    
    if(NOT w3p_TIMEOUT)
        set(w3p_TIMEOUT 300)
    endif()
	
	if(NOT DEFINED w3p_DEFAULT_USE)
		set(w3p_DEFAULT_USE ON)
	endif()
	
    ## 1st use (check/update|download) : 
    set(${prefix}_WIN3RDPARTY_USE ${w3p_DEFAULT_USE} CACHE BOOL "Use requiered 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist")

    ## We want to test if each version of MSVC was filled by the function (see associated parameters)
    ## As CMake is running only for one version of MSVC, if that MSVC version was filled, we get back associated parameters,
    ## otherwise we can't use the downloadAndExtractZipFile with win3rdParty.
    set(enableWin3rdParty OFF)
    foreach(MSVC_VER ${MSVC_VERSIONS_LIST})
        if(${MSVC_VER} AND w3p_${MSVC_VER})
            list(LENGTH w3p_${MSVC_VER} count)
            if("${count}" LESS "2")
                message(WARNING "You are using ${MSVC_VER} with ${prefix}_WIN3RDPARTY_USE=${${prefix}_WIN3RDPARTY_USE}, but win3rdParty function isn't filled for ${MSVC_VER}!")
            else()
                list(GET w3p_${MSVC_VER} 0 Win3rdPartyName)
                list(GET w3p_${MSVC_VER} 1 Win3rdPartyUrl)
                if(w3p_VCID)
                    ## try to get the VcId of MSVC. See also MSVC_VERSION cmake var in the doc.
                    string(REGEX REPLACE "MS([A-Za-z_0-9-]+)" "\\1" vcId ${MSVC_VER})
                    string(TOLOWER ${vcId} vcId)
                    set(${prefix}_WIN3RDPARTY_VCID "${vcId}0" CACHE STRING "the MSVC id (commonly used to prefix/suffix library name, see boost or CGAL)")
                    mark_as_advanced(${prefix}_WIN3RDPARTY_VCID)
                endif()
                set(enableWin3rdParty ON)
                set(suffixCompilerID ${MSVC_VER})
				break()
            endif()
        endif()
    endforeach()
    
    ## If previous step succeed to get MSVC dirname and URL of the current MSVC version, use it to auto download/update the win3rdParty dir
    if(enableWin3rdParty AND ${prefix}_WIN3RDPARTY_USE)
        if(IS_ABSOLUTE "${Win3rdPartyName}")
        else()
            set(Win3rdPartyName "${CMAKE_BINARY_DIR}/${Win3rdPartyName}")
        endif()
        if(NOT EXISTS "${Win3rdPartyName}")
            file(MAKE_DIRECTORY ${Win3rdPartyName})
        endif()
        include(downloadAndExtractZipFile)
        downloadAndExtractZipFile(  "${Win3rdPartyUrl}"                             ## URL link location
                                    "Win3rdParty-${prefix}-${suffixCompilerID}.7z"  ## where download it: relative path, so default to CMAKE_BINARY_DIR
                                    "${Win3rdPartyName}"                            ## where extract it : fullPath (default relative to CMAKE_BINARY_DIR)
            CHECK_DIRTY_URL "${Win3rdPartyName}/Win3rdPartyUrl"                     ## last downloaded url file : fullPath (default relative to CMAKE_BINARY_DIR)
            TIMEOUT ${w3p_TIMEOUT}
            VERBOSE ${w3p_VERBOSE}
        )
		file(GLOB checkDl "${Win3rdPartyName}/*")
		list(LENGTH checkDl checkDlCount)
		if("${checkDlCount}" GREATER "1")
		else()
			message("The downloadAndExtractZipFile didn't work...?")
			set(enableWin3rdParty OFF)
		endif()
    endif()
    
    ## Try to auto set ${prefix}_WIN3RDPARTY_DIR or let user set it manually
    set(${prefix}_WIN3RDPARTY_DIR "" CACHE PATH "windows ${Win3rdPartyName} dir to ${prefix} dependencies of the project")
    if(NOT ${prefix}_WIN3RDPARTY_DIR AND ${prefix}_WIN3RDPARTY_USE)
        if(EXISTS "${Win3rdPartyName}")
            unset(${prefix}_WIN3RDPARTY_DIR CACHE)
            set(${prefix}_WIN3RDPARTY_DIR "${Win3rdPartyName}" CACHE PATH "dir to ${prefix} dependencies of the project")
        endif()
    endif()
    if(EXISTS ${${prefix}_WIN3RDPARTY_DIR})
        message(STATUS "Found a 3rdParty ${prefix} dir : ${${prefix}_WIN3RDPARTY_DIR}.")
        set(enableWin3rdParty ON)
    elseif(${prefix}_WIN3RDPARTY_USE)
        message(WARNING "${prefix}_WIN3RDPARTY_USE=${${prefix}_WIN3RDPARTY_USE} but ${prefix}_WIN3RDPARTY_DIR=${${prefix}_WIN3RDPARTY_DIR}.")
        set(enableWin3rdParty OFF)
    endif()
    
    ## Final check
    if(NOT enableWin3rdParty)
        message("Disable ${prefix}_WIN3RDPARTY_USE (cmake cached var will be not set), due to a win3rdParty problem.")
        message("You still can set ${prefix}_WIN3RDPARTY_DIR to an already downloaded Win3rdParty directory location.")
        set(${prefix}_WIN3RDPARTY_USE OFF CACHE BOOL "Use requiered 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist" FORCE)
    endif()
    
    ## 2nd use : handle multi values args to set cached cmake variables in order to ease the next find_package call
    if(${prefix}_WIN3RDPARTY_USE AND ${prefix}_WIN3RDPARTY_DIR)
        if(w3p_VERBOSE)
            message(STATUS "Try to set cmake cached variables for ${prefix} requiered libraries directly from : ${${prefix}_WIN3RDPARTY_DIR}.")
        endif()
        
        include(parse_arguments_multi)
        function(parse_arguments_multi_function ) ## overloaded function to handle all CHECK_CACHED_VAR values list (see: parse_arguments_multi)
            ## we know the function take 3 args : var cacheType resetedCachedValue (see check_cached_var)
            include(CMakeParseArguments)
            cmake_parse_arguments(pamf "" "DOC" "LIST" ${ARGN})
            
            ## var and cacheType are mandatory (with the resetedCachedValue)
            set(var         ${ARGV0})
            set(cacheType   ${ARGV1})
            if(pamf_DOC)
                set(cacheDoc    ${pamf_DOC})
            else()
                set(cacheDoc    "")
            endif()
            if(pamf_LIST)
                set(value ${pamf_LIST})
            else()
                set(value ${ARGV2})
            endif()
            if("${cacheType}" MATCHES "PATH" AND EXISTS "${${prefix}_WIN3RDPARTY_DIR}/${value}")
                set(resetedCachedValue "${${prefix}_WIN3RDPARTY_DIR}/${value}") ## path relative to ${prefix}_WIN3RDPARTY_DIR
            elseif("${cacheType}" MATCHES "STRING")
                foreach(var IN LISTS value)
                    if(EXISTS "${${prefix}_WIN3RDPARTY_DIR}/${var}")
                        list(APPEND resetedCachedValue "${${prefix}_WIN3RDPARTY_DIR}/${var}") ## string item of the string list is a path => make relative to ${prefix}_WIN3RDPARTY_DIR
                    else()
                        list(APPEND resetedCachedValue ${var}) ## string item of the string list is not an existing path => simply use the item
                    endif()
                endforeach()
            else()
                set(resetedCachedValue "${value}") ## could be a BOOL or a STRING
            endif()
            
            ## call our macro to reset cmake cache variable if empty
            check_cached_var(${var} "${resetedCachedValue}" ${cacheType} "${cacheDoc}" FORCE)
            
        endfunction()
        
        if(w3p_MULTI_SET)
            parse_arguments_multi(CHECK_CACHED_VAR w3p_MULTI_SET ${w3p_MULTI_SET}) ## internaly will call our overloaded parse_arguments_multi_function
        elseif(w3p_SET)
            parse_arguments_multi(CHECK_CACHED_VAR w3p_SET ${w3p_SET})
        endif()
        
    endif()
	
endfunction()

## CMAKE_DOCUMENTATION_START Win3rdPartyGlobalCacheAction
## cmake variables introspection to globally activate/deactivate ${prefix}_WIN3RDPARTY_USE \\n
## This "one shot" call (only one for the next cmake configure) will automatically then reset the global variable WIN3RDPARTY_USE to UserDefined (do nothing).\\n
## Use (call it) before and after the call of all your win3rdParty functions.\\n
## Created by jerome esnault
## CMAKE_DOCUMENTATION_END
function(Win3rdPartyGlobalCacheAction )
	set(WIN3RDPARTY_USE "UserDefined" CACHE STRING "Choose how to handle all cmake cached *_WIN3RDPARTY_USE for the next configure.\nCould be:\nUserDefined [default]\nActivateAll\nDesactivateAll" )
	set_property(CACHE WIN3RDPARTY_USE PROPERTY STRINGS "UserDefined;ActivateAll;DesactivateAll" )
	if(${WIN3RDPARTY_USE} MATCHES "UserDefined")
	else()
		if(${WIN3RDPARTY_USE} MATCHES "ActivateAll")
			set(win3rdPvalue ON)
		elseif(${WIN3RDPARTY_USE} MATCHES "DesactivateAll")
			set(win3rdPvalue OFF)
		endif()
		get_cmake_property(_variableNames CACHE_VARIABLES)
		foreach (_variableName ${_variableNames})
			string(REGEX MATCH 	"[A-Za-z_0-9-]+_WIN3RDPARTY_USE" win3rdpartyUseCacheVar ${_variableName})
			if(win3rdpartyUseCacheVar)
				string(REGEX REPLACE "([A-Za-z_0-9-]+_WIN3RDPARTY_USE)" "\\1" win3rdpartyUseCacheVar ${_variableName})
				set(${win3rdpartyUseCacheVar} ${win3rdPvalue} CACHE BOOL "Use requiered 3rdParty binaries from ${prefix}_WIN3RDPARTY_DIR or download it if not exist" FORCE)
				message(STATUS "${win3rdpartyUseCacheVar} cached variable set to ${win3rdPvalue}.")
			endif()
		endforeach()
		set(WIN3RDPARTY_USE "UserDefined" CACHE STRING "Choose how to handle all cmake cached *_WIN3RDPARTY_USE for the next configure.\nCould be:\nUserDefined [default]\nActivateAll\nDesactivateAll" FORCE)
		message(STATUS "reset WIN3RDPARTY_USE to UserDefined.")
	endif()
	mark_as_advanced(WIN3RDPARTY_USE)
endfunction()
