#!
#! @ingroup group_sdk_cmake_camitk_test
#!
#! camitk_tests_requirement is a macro to disable one or more tests.
#! It allows for specific test management in case the developer knows that a given test 
#! requires a given environment or can not be run in a given environment.
#! 
#! It encapsulates CMake set_tests_properties and adds CamiTK specificities and force the developer
#! to give a (good) reason for bypassing tests.
#!
#! You can use this macro in conjunction with the camitk_extension macro. In camitk_extension using ENABLE_AUTO_TEST
#! automatically generate a number of tests. Some of these tests might need to be disabled due to
#! the environment (e.g. a different VTK version, an OpenGL problem on windows VM...). 
#! Use camitk_tests_requirement below the camitk_extension (i.e. once the tests are created).
#!
#! You can also use this macro just after a camitk_add_test to disable the test in some cases (e.g.
#! if the test cannot run on Windows, use it with REQUIRES "NOT WIN32")
#!
#! Usage:
#! \code
#! camitk_tests_requirement(TESTS test1 [test2...]
#!                          REASON "reason"
#!                          REQUIRES environment_test_statement
#! )
#! \endcode
#!
#! \param TESTS      Names of the test(s) to disable separated by space
#! \param REASON     A text to explain why the test is disabled. This will appear during configuration
#!                   if the test(s) is/are disabled due to unmet requirement.
#! \param REQUIRES   A CMake test statement that can check the current environment (OS,
#!                   library/dependencies version...etc... 
#!                   If the requirements are not met, the listed test(s) is/are disabled
#!                   REQUIRES statement looks like a CMake test statement but is restricted to a
#!                   subset of CMake if statement.
#!                   There is three types of REQUIRES statement:
#!                   - one-part statement are used to test the OS. 
#!                     Possible value are "WIN32", "WIN64", "MSVC", "APPLE" or "UNIX"
#!                   - two-parts statement are used to test the opposite of the one-part statement
#!                     using the "NOT" prefix.
#!                     Possible values are "NOT WIN32", "NOT WIN64", "NOT MSVC", "NOT APPLE" or "NOT UNIX"
#!                   - three-parts statement are used to check the version of library/dependencies.
#!                     It is composed of a left value (that is evaluated by the caller CMakeList, not here),
#!                     followed by a test statement and a right value (also evaluated by the caller CMakeList).
#!                     Possible test statements are VERSION_LESS, VERSION_EQUAL or VERSION_GREATER
#!
#!
#! Example invocation:
#!
#! \code
#!
#! camitk_extension(ACTION_EXTENSION
#!                 ...
#!                 ENABLE_AUTO_TEST
#!                 ...
#! )
#!
#! ...
#! # Specific test management
#!
#! # a three-parts statement to disable test if VTK version is not a specific version
#! camitk_tests_requirement(TESTS action-myaction-level1-3 action-myaction-level1-6 action-myaction-level1-11
#!                          REQUIRES "${VTK_VERSION} VERSION_EQUAL 6.3"
#!                          REASON "VTK version is not equals than 6.3
#!  The default test files are written using the currently supported version of VTK (VTK 6.3).
#!  This test will therefore fail when comparing the input to the output if another version of VTK is used."
#!)
#!
#! # a two-parts statement to disable test on WIN32
#! camitk_tests_requirement(TESTS action-itkfilters-integration-test
#!                          REQUIRES "NOT WIN32"
#!                          REASON "WIN32 OpenGL failure on a VM
#!  This test will always fail when run inside a VM due to OpenGL crash.
#!  This test will pass when run directly from a physical windows machine."
#! )
#!
#! \endcode
#
#! @sa camitk_disable_tests
macro(camitk_tests_requirement)
    set(options "")
    set(oneValueArgs REASON 
                     REQUIRES)
    set(multiValueArgs TESTS)
    cmake_parse_arguments(CAMITK_TESTS_REQUIREMENT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
    
    # -- analyze requirements
    # transform statement to list in order to access left/middle/right value if the requirement is a test
    string(REPLACE " " ";" REQUIRES_STATEMENT ${CAMITK_TESTS_REQUIREMENT_REQUIRES})
    list(LENGTH REQUIRES_STATEMENT REQUIRES_STATEMENT_LENGTH)
    
    if(REQUIRES_STATEMENT_LENGTH EQUAL 3)
        # Requirement is a test, separate into parts and evaluate the requirement
        list(GET REQUIRES_STATEMENT 0 REQUIRES_STATEMENT_LEFT_VALUE)
        list(GET REQUIRES_STATEMENT 1 REQUIRES_STATEMENT_TEST)
        list(GET REQUIRES_STATEMENT 2 REQUIRES_STATEMENT_RIGHT_VALUE)
        # check the test statement. Can be either VERSION_LESS, VERSION_EQUAL or VERSION_GREATER
        if(REQUIRES_STATEMENT_TEST STREQUAL "VERSION_LESS")
            if (${REQUIRES_STATEMENT_LEFT_VALUE} VERSION_LESS ${REQUIRES_STATEMENT_RIGHT_VALUE})
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        elseif(REQUIRES_STATEMENT_TEST STREQUAL "VERSION_EQUAL")
            if (${REQUIRES_STATEMENT_LEFT_VALUE} VERSION_EQUAL ${REQUIRES_STATEMENT_RIGHT_VALUE})
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        elseif(REQUIRES_STATEMENT_TEST STREQUAL "VERSION_GREATER")
            if (${REQUIRES_STATEMENT_LEFT_VALUE} VERSION_GREATER ${REQUIRES_STATEMENT_RIGHT_VALUE})
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        else()
            message(FATAL_ERROR "\n Unknown three-parts requirement test: \"${CAMITK_TESTS_REQUIREMENT_REQUIRES}\"\n Use one of VERSION_LESS, VERSION_EQUAL or VERSION_GREATER.\n Check call stack below to fix this error.")
        endif()
    else()
        if(REQUIRES_STATEMENT_LENGTH EQUAL 2)
            list(GET REQUIRES_STATEMENT 0 REQUIRES_STATEMENT_NOT)
            list(GET REQUIRES_STATEMENT 1 REQUIRES_STATEMENT_TEST)
            # REQUIRES_STATEMENT_NOT should be "NOT"
            if(NOT REQUIRES_STATEMENT_NOT STREQUAL "NOT")
                message(FATAL_ERROR "\n Unknown two-parts requirement test: \"${CAMITK_TESTS_REQUIREMENT_REQUIRES}\"\n Only NOT is supported.\n Check call stack below to fix this error.")
            endif()
        else()
            list(GET REQUIRES_STATEMENT 0 REQUIRES_STATEMENT_TEST)
        endif()
        
        # possible statement are WIN32, WIN64, MSVC, APPLE or UNIX
        if(REQUIRES_STATEMENT_TEST STREQUAL "WIN32")
            if(WIN32)
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        elseif(REQUIRES_STATEMENT_TEST STREQUAL "WIN64")
            if(WIN64)
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        elseif(REQUIRES_STATEMENT_TEST STREQUAL "MSVC")
            if(MSVC)
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        elseif(REQUIRES_STATEMENT_TEST STREQUAL "APPLE")
            if(APPLE)
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        elseif(REQUIRES_STATEMENT_TEST STREQUAL "UNIX")
            if(UNIX)
                set(REQUIRES_EVAL TRUE)
            else()
                set(REQUIRES_EVAL FALSE)
            endif()
        endif()
        
        # negate if needed
        if(REQUIRES_STATEMENT_NOT STREQUAL "NOT")
            if(REQUIRES_EVAL STREQUAL "TRUE")
                set(REQUIRES_EVAL FALSE)
            else()
                set(REQUIRES_EVAL TRUE)
            endif()
        endif()
    endif()
    
    # -- disable tests if the requirement is not met
    string(REPLACE ";" " " CAMITK_TESTS_REQUIREMENT_TESTS_STRING "${CAMITK_TESTS_REQUIREMENT_TESTS}")
    if(NOT ${REQUIRES_EVAL} AND BUILD_TESTING)    
        foreach(TEST_NAME ${CAMITK_TESTS_REQUIREMENT_TESTS})
            # CMake < 3.9 did not have the DISABLED test property. Use WILL_FAIL instead
            if(${CMAKE_VERSION} VERSION_LESS "3.9")
                set_tests_properties(${TEST_NAME} PROPERTIES WILL_FAIL true)
            else()
                set_tests_properties(${TEST_NAME} PROPERTIES DISABLED true)
            endif()
        endforeach()
        set(DISABLE_MESSAGE "Test requirement \"${CAMITK_TESTS_REQUIREMENT_REQUIRES}\": Failed\n   Disabled tests: ${CAMITK_TESTS_REQUIREMENT_TESTS_STRING}\n")
        if(${CMAKE_VERSION} VERSION_LESS "3.9")
            set(DISABLE_MESSAGE ${DISABLE_MESSAGE} "   (using WILL_FAIL property because CMake < 3.9)\n")
        endif()
        set(DISABLE_MESSAGE ${DISABLE_MESSAGE} "   Reason: ${CAMITK_TESTS_REQUIREMENT_REASON}\n")
        set(DISABLE_MESSAGE ${DISABLE_MESSAGE} "   Requirement statement unmet: ${CAMITK_TESTS_REQUIREMENT_REQUIRES}")
        message(STATUS ${DISABLE_MESSAGE})
    else()
        message(STATUS "Test requirement \"${CAMITK_TESTS_REQUIREMENT_REQUIRES}\": Passed\n   Enabled tests: ${CAMITK_TESTS_REQUIREMENT_TESTS_STRING}")
    endif()
endmacro()
