#!
#! @ingroup group_sdk_cmake_camitk_test
#!
#! camitk_add_integration_test is a macro to add a new test to the CTest infrastructure
#! It encapsulates CMake add_test and use the action state machine to run a set of actions.
#! The set of actions are described in a CamiTK SCXML document. This macro is used to
#! add integration tests.
#!
#! Details on the runned test can be found in directory 
#! ${CMAKE_BINARY_DIR}/Testing/Temporary/${TYPE_EXTENSION}-${EXTENSION_NAME}-integration-test
#!
#! It is typically usued inside the camitk_extension(..) macro and expects the following variable
#! to be set beforehand:
#! - TYPE_EXTENSION     action or component)
#! - EXTENSION_NAME     name of the current action or component
#! 
#! It does add a test if and only if:
#! - ${CMAKE_CURRENT_SOURCE_DIR}/integration-testdata exists
#! - ${CMAKE_CURRENT_SOURCE_DIR}/integration-testdata contains a file called "asm-input.scxml"
#! - ${CMAKE_CURRENT_SOURCE_DIR}/integration-testdata contains at least one file name "output-*.*"
#!
#! Usage:
#! \code
#! camitk_add_integration_test()
#! \endcode
#!
macro(camitk_add_integration_test)

    #-- set the name of the current test and other parameters
    set(CAMITK_INTEGRATION_TEST_NAME ${TYPE_EXTENSION}-${EXTENSION_NAME}-integration-test)
    
    #-- check integration data availability
    set(CAMITK_INTEGRATION_TESTDATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/integration-testdata)
    if (NOT EXISTS ${CAMITK_INTEGRATION_TESTDATA_DIR})
        message(FATAL_ERROR "Can not add test ${CAMITK_INTEGRATION_TEST_NAME}: \"integration-testdata\" subdirectory not found." )
    endif()
    
    set(CAMITK_INTEGRATION_SCXML asm-input.scxml)
    if (NOT EXISTS ${CAMITK_INTEGRATION_TESTDATA_DIR})
        message(FATAL_ERROR "Can not add test ${CAMITK_INTEGRATION_TEST_NAME}: CamiTK SCXML document not found." )
    endif()

    # look for all the files that called "output-*"
    file(GLOB CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES RELATIVE ${CAMITK_INTEGRATION_TESTDATA_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/integration-testdata/output-*.*)
    list(LENGTH CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES NUMBER_OF_EXPECTED_OUTPUT_FILES)
    if (NUMBER_OF_EXPECTED_OUTPUT_FILES EQUAL 0)
        message(FATAL_ERROR "Can not add test ${CAMITK_INTEGRATION_TEST_NAME}: no expected output file found." )
    endif()
    
    #-- clean/create test output directory
    set(CAMITK_TEST_OUTPUT_DIR "${CMAKE_BINARY_DIR}/Testing/Temporary/${CAMITK_INTEGRATION_TEST_NAME}")
    file(REMOVE_RECURSE ${CAMITK_TEST_OUTPUT_DIR})
    file(MAKE_DIRECTORY ${CAMITK_TEST_OUTPUT_DIR})

    #-- replace the input file names
    # Read the file in a variable
    file(READ ${CAMITK_INTEGRATION_TESTDATA_DIR}/${CAMITK_INTEGRATION_SCXML} ASM_INPUT_FILE_AS_STRING)
    # get all the lines that have a non-empty "File Name" parameter
    string(REGEX MATCHALL "<camitk:parameter name=\"File Name\" value=\".*\" type=\"QString\"/>" FILENAME_LINES "${ASM_INPUT_FILE_AS_STRING}")
    # intialize ASM_INPUT_FILE_AS_STRING_OK
    set(ASM_INPUT_FILE_AS_STRING_OK "${ASM_INPUT_FILE_AS_STRING}")
    # for each lines that have a non-empty "File Name" parameter
    foreach(CURRENT_LINE ${FILENAME_LINES})
        # first replace the file name so that the file can be copied to the temp directory
        string(REGEX REPLACE "value=\"(.*)\"" "value=\"${CAMITK_TEST_OUTPUT_DIR}/\\1\"" CURRENT_LINE_OK ${CURRENT_LINE})
        # second replace the line in ASM_INPUT_FILE_AS_STRING_OK
        string(REPLACE "${CURRENT_LINE}" "${CURRENT_LINE_OK}" ASM_INPUT_FILE_AS_STRING_OK "${ASM_INPUT_FILE_AS_STRING_OK}")
        # extract the File Name
        string(REGEX REPLACE ".*value=\"(.*)\" .*" "\\1" INPUT_FILE_NAME ${CURRENT_LINE})
        # copy the corresponding file to the temp directory
        file(COPY ${CAMITK_INTEGRATION_TESTDATA_DIR}/${INPUT_FILE_NAME}
             DESTINATION ${CAMITK_TEST_OUTPUT_DIR})
    endforeach()

    # Finally write the file in the temp directory
    # Beware/Note: you need to add the quote around the ASM_INPUT_FILE_AS_STRING_OK value otherwise ";" will be
    # interprated as list/argument separator and will be transformed to " " (for XML files that means that each 
    # substitution will get damaged: &lt; -> &lt ... not good...)
    file(WRITE ${CAMITK_TEST_OUTPUT_DIR}/${CAMITK_INTEGRATION_SCXML} "${ASM_INPUT_FILE_AS_STRING_OK}")

    # And copy the expected output files
    foreach(EXPECTED_OUTPUT_FILE ${CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES})
        file(COPY ${CAMITK_INTEGRATION_TESTDATA_DIR}/${EXPECTED_OUTPUT_FILE}
             DESTINATION ${CAMITK_TEST_OUTPUT_DIR})
    endforeach()

    #-- run actionstatemachine
    set(CAMITK_TEST_EXECUTABLE_ARG "-f ${CAMITK_TEST_OUTPUT_DIR}/${CAMITK_INTEGRATION_SCXML} -o ${CAMITK_TEST_OUTPUT_DIR} -a")

    #-- determine cmake macro path
    if (NOT EXISTS SDK_TOP_LEVEL_SOURCE_DIR)
        # this macro is called outside the sdk 
        set(CAMITK_CMAKE_MACRO_PATH ${CAMITK_CMAKE_DIR}/modules/macros/camitk/test)
        if(NOT IS_DIRECTORY ${CAMITK_CMAKE_MACRO_PATH})
            # inside opensource but not in sdk (modeling or imaging)
            set(CAMITK_CMAKE_MACRO_PATH ${CMAKE_SOURCE_DIR}/sdk/cmake/modules/macros/camitk/test)
        endif()
    else()
        # directly use the macro source dir
        set(CAMITK_CMAKE_MACRO_PATH ${SDK_TOP_LEVEL_SOURCE_DIR}/cmake/modules/macros/camitk/test)
    endif()
    
    #-- check if debug postfix is needed
    set(APP_NAME camitk-actionstatemachine)
    if(NOT CAMITK_COMMUNITY_EDITION_BUILD)
        # this macro is called outside CamiTK CE
        
        # find the complete path to the test programs (removing any previous attempt to find a test application
        # as this could be another application)
        unset(CAMITK_INTEGRATION_TEST_EXECUTABLE CACHE)
        find_program(CAMITK_INTEGRATION_TEST_EXECUTABLE
                    NAMES ${APP_NAME}${CAMITK_DEBUG_POSTFIX} ${APP_NAME}
                    PATH_SUFFIXES "bin"
                    PATHS ${CAMITK_BIN_DIR}
        )
        
        if (NOT CAMITK_INTEGRATION_TEST_EXECUTABLE)
            # Test programs should be installed
            message(FATAL_ERROR "${APP_NAME} not found.\n   This means that action state machine was not installed during CamiTK SDK installation.")
        endif()                                
    else()
        # in CamiTK CE build

        # add debug postfix if needed by MSVC
        unset(APP_SUFFIX)
        # determine which version of the executable to use (debug-suffixed or not)
        if(MSVC)
            if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR (CMAKE_CONFIGURATION_TYPES AND "$<CONFIG>" STREQUAL "Debug"))
                set(APP_SUFFIX "${CAMITK_DEBUG_POSTFIX}")
            endif()
        endif()
        
        # In CamiTK CE build directly use the binary dir version
        set(CAMITK_INTEGRATION_TEST_EXECUTABLE ${CMAKE_BINARY_DIR}/bin/${APP_NAME}${APP_SUFFIX})  
    endif()

    #-- and add the test
    # Construct a specific string for outputfiles to pass them properly to the test command
    unset(CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES_ARG)
    foreach(TEST_EXPECTED_OUTPUT_FILE ${CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES})
        # add specific "::" separator
        if(CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES_ARG)
            string(CONCAT CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES_ARG "${CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES_ARG}::${TEST_EXPECTED_OUTPUT_FILE}")
        else()
            set(CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES_ARG ${TEST_EXPECTED_OUTPUT_FILE})
        endif()
    endforeach()
    add_test(NAME ${CAMITK_INTEGRATION_TEST_NAME}
             COMMAND ${CMAKE_COMMAND} 
                            -DCAMITK_TEST_COMMAND=${CAMITK_INTEGRATION_TEST_EXECUTABLE}
                            -DCAMITK_TEST_COMMAND_ARG=${CAMITK_TEST_EXECUTABLE_ARG}
                            -DCAMITK_TEST_EXPECTED_FILES=${CAMITK_INTEGRATION_TEST_EXPECTED_OUTPUT_FILES_ARG}
                            -DCAMITK_TEST_OUTPUT_DIR=${CAMITK_TEST_OUTPUT_DIR}
                            -DCAMITK_TEST_NAME=${CAMITK_INTEGRATION_TEST_NAME}
                            -P ${CAMITK_CMAKE_MACRO_PATH}/CamiTKTestActionStateMachine.cmake
            WORKING_DIRECTORY ${CMAKE_BINARY_DIR} # needs to be at the top of the CamiTK module repository
    )

    
    # set the label for tests / associate tests to a project name in CDash 
    if( CAMITK_ADD_TEST_PROJECT_NAME )
        set_tests_properties( ${CAMITK_INTEGRATION_TEST_NAME} PROPERTIES LABELS "${CAMITK_ADD_TEST_PROJECT_NAME}" )#
    else()
        set_tests_properties( ${CAMITK_INTEGRATION_TEST_NAME} PROPERTIES LABELS "${CAMITK_INTEGRATION_TEST_NAME}" )
    endif()

endmacro()
