cmake_minimum_required(VERSION 3.11...3.16)

include(${CMAKE_GENODE_TOOLS_DIR}/genode-tool.cmake)

define_property(TARGET PROPERTY repos_dir
        BRIEF_DOCS "Repository path."
        FULL_DOCS  "Repository path relative to the Genode dir.")

define_property(TARGET PROPERTY shared_include_dir
        BRIEF_DOCS "Include to be shared with others."
        FULL_DOCS  "Include to be shared with others.")

define_property(TARGET PROPERTY repos_src_dir
        BRIEF_DOCS "Repository source path."
        FULL_DOCS  "Repository source path.")


function(get_sources_from_path)
    set(_OPTIONS_ARGS GLOB_RECURSE)
    set(_ONE_VALUE_ARGS FROM)
    set(_MULTI_VALUE_ARGS LOCATION)

    cmake_parse_arguments(_GET_SOURCES_FROM_PATH "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})

    if(NOT _GET_SOURCES_FROM_PATH_LOCATION)
        message(FATAL_ERROR "add_project_test: missing argument(s)" )
    endif()

    foreach(path ${_GET_SOURCES_FROM_PATH_LOCATION})
        if(_GET_SOURCES_FROM_PATH_GLOB_RECURSE)
            file(GLOB_RECURSE files ${path}/*.cc ${path}/*.c ${path}/*.h)
        else()
            file(GLOB files ${path}/*.cc ${path}/*.c ${path}/*.h)
        endif()
        list(APPEND sources ${files})
    endforeach()
    set(RESULT_SOURCES ${sources} PARENT_SCOPE)
endfunction()


function(genode_component_include_directories)
    set(_OPTIONS_ARGS)
    set(_ONE_VALUE_ARGS TARGET)
    set(_MULTI_VALUE_ARGS INCLUDE_DEPENDENCIES INCLUDE_DIR)

    cmake_parse_arguments(_GENODE_COMPONENT_INCLUDE_DIRECTORIES "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})

    if(NOT _GENODE_COMPONENT_INCLUDE_DIRECTORIES_TARGET OR NOT _GENODE_COMPONENT_INCLUDE_DIRECTORIES_INCLUDE_DEPENDENCIES)
        message(FATAL_ERROR "genode_component_include_directories: missing argument(s)")
    endif()

    foreach(repos_target ${_GENODE_COMPONENT_INCLUDE_DIRECTORIES_INCLUDE_DEPENDENCIES})
        get_property(repos_include_dir TARGET ${repos_target} PROPERTY shared_include_dir)
        list(APPEND INCLUDE_DIRS ${repos_include_dir})
    endforeach()
    if(_GENODE_COMPONENT_INCLUDE_DIRECTORIES_INCLUDE_DIR)
        list(APPEND INCLUDE_DIRS ${_GENODE_COMPONENT_INCLUDE_DIRECTORIES_INCLUDE_DIR})
    endif()
    target_include_directories(${_GENODE_COMPONENT_INCLUDE_DIRECTORIES_TARGET} PUBLIC ${INCLUDE_DIRS})
endfunction()


function(genode_add_component)
    set( _OPTIONS_ARGS NO_RECURSIVE_SRCS)
    set( _ONE_VALUE_ARGS TARGET FROM)
    set( _MULTI_VALUE_ARGS SRCS_LOCATION SHARED_INCLUDE_DIR DEPENDS INCLUDE_DIR)

    cmake_parse_arguments(
            _GENODE_ADD_COMPONENT
            "${_OPTIONS_ARGS}"
            "${_ONE_VALUE_ARGS}"
            "${_MULTI_VALUE_ARGS}"
            ${ARGN}
    )

    if(NOT _GENODE_ADD_COMPONENT_TARGET OR NOT _GENODE_ADD_COMPONENT_FROM)
        message( FATAL_ERROR "genode_add_component: missing argument(s)")
    endif()

    list(
            APPEND TARGET_DEPENDENCIES
            ${_GENODE_ADD_COMPONENT_FROM}
            ${_GENODE_ADD_COMPONENT_DEPENDS}
    )

    # Determinate path for target sources
    get_property(repos_dir TARGET ${_GENODE_ADD_COMPONENT_FROM} PROPERTY repos_dir)
    foreach(path ${_GENODE_ADD_COMPONENT_SRCS_LOCATION})
        list(APPEND COMPONENT_SRCS_LOCATIONS ${repos_dir}/${path})
    endforeach()
    if (_GENODE_ADD_COMPONENT_NO_RECURSIVE_SRCS)
        # out: RESULT_SOURCES
        get_sources_from_path(LOCATION ${COMPONENT_SRCS_LOCATIONS})
    else()
        # out: RESULT_SOURCES
        get_sources_from_path(
                GLOB_RECURSE
                LOCATION ${COMPONENT_SRCS_LOCATIONS}
        )
    endif()

    # extend shared include dir to full path
    foreach(inc_rel_path ${_GENODE_ADD_COMPONENT_SHARED_INCLUDE_DIR})
        list(APPEND TARGET_SHARED_INCLUDE_DIR ${repos_dir}/${inc_rel_path})
    endforeach()

    # create target
    set(TARGET_NAME "component-${_GENODE_ADD_COMPONENT_TARGET}")
    add_library(${TARGET_NAME} ${RESULT_SOURCES})
    set_property(TARGET ${TARGET_NAME} PROPERTY EXCLUDE_FROM_ALL   true)
    set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER             GenodeComponents)
    set_property(
            TARGET ${TARGET_NAME}
            PROPERTY shared_include_dir ${TARGET_SHARED_INCLUDE_DIR}
    )

    # target include dirs
    genode_component_include_directories(
            TARGET               ${TARGET_NAME}
            INCLUDE_DIR          ${COMPONENT_SRCS_LOCATIONS}
            INCLUDE_DEPENDENCIES ${TARGET_DEPENDENCIES}
    )
endfunction()


function(genode_add_repos PATH)
    get_filename_component(REPO_NAME ${PATH} NAME)
    set(NAME repos-${REPO_NAME})
    add_library(${NAME} INTERFACE)

    set_property(TARGET ${NAME} PROPERTY repos_dir ${PATH})
    set_property(TARGET ${NAME} PROPERTY repos_src_dir ${PATH}/src)

    list(APPEND INCLUDE_DIRS "${PATH}/include")
    foreach(SPEC ${SPECS})
        list(APPEND INCLUDE_DIRS "${PATH}/include/spec/${SPEC}")
    endforeach()
    set_property(TARGET ${NAME} PROPERTY shared_include_dir ${INCLUDE_DIRS})
    set_property(TARGET ${NAME} PROPERTY FOLDER GenodeRepos)
    include_directories(${INCLUDE_DIRS})
endfunction()


function(genode_add_port)
    set( _OPTIONS_ARGS NO_RECURSIVE_SRCS)
    set( _ONE_VALUE_ARGS PORT_NAME)
    set( _MULTI_VALUE_ARGS EXTRACT_SRCS DEPENDS INCLUDE_DIR)

    cmake_parse_arguments(_GENODE_ADD_PORT "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})

    if(NOT _GENODE_ADD_PORT_PORT_NAME OR NOT _GENODE_ADD_PORT_EXTRACT_SRCS)
        message( FATAL_ERROR "genode_add_contrib: missing argument(s)")
    endif()

    # out: RESULT_PORT_PATH
    genode_current_port(${_GENODE_ADD_PORT_PORT_NAME})

    foreach(path ${_GENODE_ADD_PORT_EXTRACT_SRCS})
        list(APPEND PORT_SRCS_LOCATIONS "${RESULT_PORT_PATH}/${path}")
    endforeach()

    foreach(path ${_GENODE_ADD_PORT_INCLUDE_DIR})
        list(APPEND PORT_INCLUDE_DIR "${RESULT_PORT_PATH}/${path}")
    endforeach()

    # out: RESULT_SOURCES
    get_sources_from_path(GLOB_RECURSE LOCATION ${PORT_SRCS_LOCATIONS})

    set(TARGET_NAME port-${_GENODE_ADD_PORT_PORT_NAME})
    add_library(${TARGET_NAME} ${RESULT_SOURCES})
    set_property(TARGET ${TARGET_NAME} PROPERTY EXCLUDE_FROM_ALL true)
    set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER GenodePorts)
    set_property(TARGET ${TARGET_NAME} PROPERTY shared_include_dir ${PORT_INCLUDE_DIR})

    target_include_directories(${TARGET_NAME} PUBLIC ${PORT_INCLUDE_DIR})
endfunction()


function(genode_load_repository LOAD_FILE)
    if(EXISTS ${CMAKE_GENODE_TOOLS_DIR}/load/${LOAD_FILE})
        set(LOAD_PATH ${CMAKE_GENODE_TOOLS_DIR}/load/${LOAD_FILE})
        include(${LOAD_PATH})
    else()
        get_filename_component(filename ${LOAD_FILE} NAME_WE)
        if (EXISTS ${GENODE_REPOS_DIR}/${filename}/tools/cmake/${LOAD_FILE})
            include(${GENODE_REPOS_DIR}/${filename}/tools/cmake/${LOAD_FILE})
        else()
            message(FATA_ERROR "${LOAD_FILE} must be one of the default one, or located in tools/cmake/ under your repository.")
        endif()
    endif()
endfunction()


function(genode_add_run_script)
    set( _OPTIONS_ARGS)
    set( _ONE_VALUE_ARGS NAME PLATFORM TARGET BOARD KERNEL)
    set( _MULTI_VALUE_ARGS ENV)

    cmake_parse_arguments(_GENODE_ADD_RUN_SCRIPT "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN})

    if (_GENODE_ADD_RUN_SCRIPT_BOARD)
        set(BOARD "BOARD=${_GENODE_ADD_RUN_SCRIPT_BOARD}")
    endif()
    if (_GENODE_ADD_RUN_SCRIPT_KERNEL)
        set(KERNEL "KERNEL=${_GENODE_ADD_RUN_SCRIPT_KERNEL}")
    endif()

    add_custom_target(${_GENODE_ADD_RUN_SCRIPT_NAME}
            COMMAND ${_GENODE_ADD_RUN_SCRIPT_ENV} ${BOARD} ${KERNEL} make -C build/${_GENODE_ADD_RUN_SCRIPT_PLATFORM} ${_GENODE_ADD_RUN_SCRIPT_TARGET}
            WORKING_DIRECTORY ${GENODE_DIR})
    set_property(TARGET ${_GENODE_ADD_RUN_SCRIPT_NAME} PROPERTY FOLDER Run)
endfunction()
