#!
#! @ingroup group_sdk_cmake_camitk
#!
#! Macro camitk_extension_generator is called in camitk_extension macro.
#! In turns it calls the camitk-extensiongenerator application to generate
#! the source code in the build directory from the given .camitk file.
#!
#! It calls camitk-extensiongenerator a first time for a first generation,
#! list the files that are generated and set up a custom command to 
#! re-generate theses files everytime the .camitk is modified.
#!
#! \note
#! When a new action is added in the .camitk, the user MUST restart the CMake configuration stage
#! as the list of generated source files will have to be updated so that they will be updated
#! whenever the .camitk is modified.
#! Also the moc process will also be generating its own source code from the generated files
#! and won't be able to find them after regeneration.
#!  
#!  usage:
#! \code
#!   camitk_extension_generator(CAMITK_FILE file)
#! \endcode
#!
#! \param CAMITK_FILE      REQUIRED   The .camitk file used to generate the source code
#
macro(camitk_extension_generator)
    set(oneValueArgs CAMITK_FILE)
    cmake_parse_arguments(EXTENSION_GENERATOR "" "${oneValueArgs}" "$" ${ARGN} )

    if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${EXTENSION_GENERATOR_CAMITK_FILE}")
        # first generation the CamiTK file is indeed in the source directory
        set(CAMITK_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${EXTENSION_GENERATOR_CAMITK_FILE}")
    else()
        # during build:
        # as extensions CMakeLists are in a actions/components/viewers subdirectory (at least for Standard extension),
        # the full path to the CamiTK file is in the directory above the current source code
        set(CAMITK_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../${EXTENSION_GENERATOR_CAMITK_FILE}")        
    endif()
    
    # generated directory is relative to current binary dir (build/actions/generated, build/components/generated..)
    set(GENERATED_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")

    if (EXISTS "${CAMITK_FILE}")
        # Try to find camitk-extensiongenerator binary in usual/recommended path/variable/user directories
        find_program(CAMITK_EXTENSION_GENERATOR_EXECUTABLE
                    NAMES camitk-extensiongenerator-debug camitk-extensiongenerator
                    PATH_SUFFIXES "bin"
                    PATHS
                        #-- 0. from CAMITK_DIR environment or cache variable
                        "${CAMITK_DIR}"
                        "$ENV{CAMITK_DIR}"

                        #-- 1. Build path
                        # Read from the CMakeSetup registry entries.  It is likely that
                        # CamiTK will have been recently built.
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild1]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild2]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild3]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild4]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild5]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild6]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild7]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild8]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild9]
                        [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild10]

                        # For camitk community edition compilation
                        "${CMAKE_BINARY_DIR}"

                        #-- 2. installed path
                        # Unix/MacOS
                        "/usr"
                        "/usr/local"
                        "/opt/CamiTK"
                        "$ENV{HOME}/Dev/CamiTK"
                        "$ENV{HOME}/Dev/camitk"
                        
                        # Windows
                        "C:/Programs "
                        "C:/Programs/CamiTK"
                        "C:/Program Files/CamiTK"
                        "C:/Programs/camitk"
                        "C:/Program Files/camitk"
                        "C:/Dev"
                        "C:/Dev/CamiTK"
                        "C:/Dev/camitk"

                        # From last installation
                        "${FIND_CAMITK_USER_DIR_LAST_INSTALL}"
        )

        if (NOT CAMITK_EXTENSION_GENERATOR_EXECUTABLE)
            # camitk-extensiongenerator is not in an obvious place, ask the user to update its PATH env. variable, or define CAMITK_DIR
            set(CAMITK_EXTENSION_GENERATOR_EXECUTABLE_NOT_FOUND_MESSAGE "CamiTK extension generator executable not found.\n   To solve this problem, you can (in order of preference):\n   a) make sure CamiTK SDK (version above but not including 5.2) is installed and\n      camitk-extensiongenerator is in a directory included\n      in the environment command path ($PATH or %PATH%).\n   b) or specify the CMake cache variable (e.g. modify the value directly in the cmake GUI\n      or run cmake from the command line with -DCAMITK_DIR:PATH=...)\n   c) or specify a CAMITK_DIR environment variable (system variable)")
            message(FATAL_ERROR "${CAMITK_EXTENSION_GENERATOR_EXECUTABLE_NOT_FOUND_MESSAGE}")
        else()
            # list source files before generation
            file(GLOB EXISTING_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
            file(GLOB EXISTING_GENERATED_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
            file(GLOB EXISTING_GENERATED_SOURCES "${GENERATED_DIRECTORY}/*.cpp")
            set(EXISTING_FILES ${EXISTING_SOURCES} ${EXISTING_GENERATED_HEADERS} ${EXISTING_GENERATED_SOURCES})
            
            # Get newest file timestamp in generated source directory 
            if(EXISTING_GENERATED_SOURCES)
                list(GET EXISTING_GENERATED_SOURCES 0 NEWEST_GENERATED_FILE)
                file(TIMESTAMP ${NEWEST_GENERATED_FILE} NEWEST_GENERATED_FILE_TIME "%s")
            else()
                # no file → generation is needed
                set(NEWEST_GENERATED_FILE_TIME 0)
            endif()
            # get the camitk file last modification time
            file(TIMESTAMP ${CAMITK_FILE} CAMITK_FILE_TIME "%s")
            get_filename_component(CAMITK_FILE_FILENAME ${CAMITK_FILE} NAME)

            # execute extension generator only if the .camitk is newer than the last generated file
            if(CAMITK_FILE_TIME GREATER NEWEST_GENERATED_FILE_TIME)
                
                # On windows the path to the extensiongenerator library is unknown and requires to be added
                if (MSVC AND CAMITK_COMMUNITY_EDITION_BUILD)
                    # message(STATUS "current shell: $ENV{ComSpec}")
                    # use CMAKE_COMMAND directly, see https://cmake.org/pipermail/cmake/2006-March/008522.html
                    # string(REGEX REPLACE "/" "\\\\" PATH_TO_PUBLIC_DLL "${CAMITK_BUILD_BIN_DIR}") 
                    set(EXTENSIONGENERATOR_COMMAND "In ${CMAKE_BINARY_DIR}\n${CMAKE_COMMAND} -E env PATH=$ENV{PATH};${CAMITK_BUILD_BIN_DIR} ${CAMITK_EXTENSION_GENERATOR_EXECUTABLE} --file ${CAMITK_FILE} --directory ${GENERATED_DIRECTORY}")
                    execute_process(COMMAND ${CMAKE_COMMAND} -E env "PATH=$ENV{PATH};${PATH_TO_PUBLIC_DLL}" "${CAMITK_EXTENSION_GENERATOR_EXECUTABLE}" "--file" "${CAMITK_FILE}" "--directory" "${GENERATED_DIRECTORY}"
                                    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
                                    RESULT_VARIABLE CAMITK_EXTENSION_GENERATOR_EXECUTABLE_RETURN_CODE
                                    OUTPUT_VARIABLE CAMITK_EXTENSION_GENERATOR_EXECUTABLE_OUTPUT
                                    ERROR_VARIABLE  CAMITK_EXTENSION_GENERATOR_EXECUTABLE_OUTPUT
                                    OUTPUT_STRIP_TRAILING_WHITESPACE
                                    ERROR_STRIP_TRAILING_WHITESPACE
                    )                
                else()            
                    # first generation during CMake configure stage: the path is in the current directory
                    set(EXTENSIONGENERATOR_COMMAND "{CAMITK_EXTENSION_GENERATOR_EXECUTABLE} --file ${CAMITK_FILE} --directory ${GENERATED_DIRECTORY}")
                    execute_process(COMMAND "${CAMITK_EXTENSION_GENERATOR_EXECUTABLE}" "--file" "${CAMITK_FILE}" "--directory" "${GENERATED_DIRECTORY}"
                                    RESULT_VARIABLE CAMITK_EXTENSION_GENERATOR_EXECUTABLE_RETURN_CODE
                                    OUTPUT_VARIABLE CAMITK_EXTENSION_GENERATOR_EXECUTABLE_OUTPUT
                                    ERROR_VARIABLE  CAMITK_EXTENSION_GENERATOR_EXECUTABLE_OUTPUT
                                    OUTPUT_STRIP_TRAILING_WHITESPACE
                                    ERROR_STRIP_TRAILING_WHITESPACE
                    )
                endif()
            else()
                message(STATUS "No generation required as ${CAMITK_FILE_FILENAME} is older than generated files")
                set(CAMITK_EXTENSION_GENERATOR_EXECUTABLE_RETURN_CODE 0)
            endif() # generation time stamp check


            if(CAMITK_EXTENSION_GENERATOR_EXECUTABLE_RETURN_CODE)
                message(FATAL_ERROR "Failed to run camitk-extensiongenerator:\ncommand:\n${EXTENSIONGENERATOR_COMMAND}\nreturn code:\n${CAMITK_EXTENSION_GENERATOR_EXECUTABLE_RETURN_CODE}\noutput:\n${CAMITK_EXTENSION_GENERATOR_EXECUTABLE_OUTPUT}")
            else()
                # list files after the first generation
                file(GLOB GENERATED_USER_ACTION_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
                file(GLOB GENERATED_HEADERS "${GENERATED_DIRECTORY}/*.h")
                file(GLOB GENERATED_SOURCES "${GENERATED_DIRECTORY}/*.cpp")
                set(GENERATED_FILES ${GENERATED_USER_ACTION_SOURCES} ${GENERATED_HEADERS} ${GENERATED_SOURCES})
                # Remove previously existing files to get the list of generated files
                list(REMOVE_ITEM GENERATED_FILES ${EXISTING_FILES})
                message(STATUS "Generating source code for ${${TYPE_EXTENSION_CMAKE}_TARGET_NAME} from \"${EXTENSION_GENERATOR_CAMITK_FILE}...\"")
                foreach(GENERATED_FILE ${GENERATED_FILES})
                    get_filename_component(FILENAME "${GENERATED_FILE}" NAME)
                    get_filename_component(DIR "${GENERATED_FILE}" DIRECTORY)

                    if("${DIR}" STREQUAL "${GENERATED_DIRECTORY}")
                        message(STATUS "Generated: ${FILENAME}")
                    else()
                        message(STATUS "Generated user action class: ${FILENAME}")
                    endif()
                endforeach()
                set(GENERATED_FILES ${GENERATED_HEADERS} ${GENERATED_SOURCES})

                # command to generate the source code from .camitk whenever it is modified                
                if (MSVC)
                    # setting CMAKE_MSVCIDE_RUN_PATH only works for add_custom_command() or add_custom_target() 
                    # but not for execute_process()
                    set(CMAKE_MSVCIDE_RUN_PATH ${CAMITK_BUILD_BIN_DIR})
                    message(STATUS "${CAMITK_BUILD_BIN_DIR} added to MSVC run path")
                endif()
                
                add_custom_command(OUTPUT ${GENERATED_FILES}
                                COMMAND "${CAMITK_EXTENSION_GENERATOR_EXECUTABLE}" "--file" "${CAMITK_FILE}" "--directory" "${GENERATED_DIRECTORY}"
                                DEPENDS ${CAMITK_FILE}
                                COMMENT "Generating source code for ${${TYPE_EXTENSION_CMAKE}_TARGET_NAME} from \"${EXTENSION_GENERATOR_CAMITK_FILE}\"..."
                )

                include_directories("${GENERATED_DIRECTORY}")
                set(${EXTENSION_NAME_CMAKE}_EXTERNAL_SOURCES ${GENERATED_FILES})
            endif()
        endif() # extension generator was found
    else()
        message(FATAL_ERROR "Cannot generate code from ${CAMITK_FILE}: file does not exist")
    endif()

endmacro()