cmake_minimum_required(VERSION 3.5)
project(Spark.Admin.WebApp)

include(GNUInstallDirs)
include(${CMAKE_SOURCE_DIR}/cmake/safe_dep_glob.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/SymLink.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/ProtobufGenerateJs.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/ProtobufGenerateFs.cmake)

file(
  GLOB_RECURSE ${PROJECT_NAME}_PROTO_SOURCES
  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
  ../lib_spark/spark/api/*.proto
  ../lib_spark/spark/api/services/*.proto
  ../lib_spark/spark/domain_model/*.proto
  ../lib_spark/spark/sparkproto/*.proto
  ../lib_spark/spark/md/protos/*.proto
  ../../externals/linux/include/google/protobuf/descriptor.proto
  ../../externals/linux/include/google/protobuf/timestamp.proto
)

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
  COMMAND npm install --include=dev
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/package.json
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

set(_tools ${CMAKE_SOURCE_DIR}/tools)
set(_quiet_exec ${_tools}/quiet_exec.sh)
set(_venv_exec ${_tools}/venv_exec.sh)
set(_tools_py ${CMAKE_SOURCE_DIR}/tools/py)

set(_tsconfig_base "${CMAKE_CURRENT_SOURCE_DIR}/tsconfig.$<IF:$<CONFIG:Debug>,debug,release>.json")
set(_tsconfig "${CMAKE_CURRENT_BINARY_DIR}/tsconfig.json")

set(_venv_python ${CMAKE_SOURCE_DIR}/.venv/bin/python)
set(_gen_tsconfig_py ${_tools_py}/gen_tsconfig.py)
set(_build_admin_web_app_py ${_tools_py}/build_admin_web_app.py)
set(_build_admin_web_app_deps
  ${_quiet_exec}
  ${_venv_exec}
  ${_venv_python}
  ${_build_admin_web_app_py}
  ${CMAKE_CURRENT_SOURCE_DIR}/src/Spark.Admin.WebApp.fsproj
  ${CMAKE_CURRENT_SOURCE_DIR}/webpack.config.js
  ${CMAKE_SOURCE_DIR}/src/lib_spark/spark/api/service_compiler/templates/fs/fable.fs
  ${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
  ${${PROJECT_NAME}_PROTO_SOURCES}
  ${_api_ts_SOURCES}
  ${_ui_fs_SOURCES}
  ${_tsconfig})

update_deps_file("${_build_admin_web_app_deps}")

add_custom_command(
  OUTPUT ${_tsconfig}
  COMMAND ${_venv_exec}
  ARGS
    ${_venv_python}
    ${_gen_tsconfig_py}
    --config-base ${_tsconfig_base}
    --config-out ${_tsconfig}
    --build-out ${CMAKE_CURRENT_BINARY_DIR}
  DEPENDS
    ${_venv_exec}
    ${_venv_python}
    ${_gen_tsconfig_py}
    ${_tsconfig_base}
    ${CMAKE_CURRENT_SOURCE_DIR}/package-lock.json
)

protobuf_generate_js(
  TARGET_NAME generate_js_protos
  PROTO_INCLUDE_DIRS
    ../lib_spark
    ../../externals/linux/include
  PROTO_SOURCES ${${PROJECT_NAME}_PROTO_SOURCES}
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/proto.js
)

protobuf_generate_fs(
  TARGET_NAME generate_fs_protos
  PROTO_INCLUDE_DIRS
    ../lib_spark
    ../../externals/linux/include
  PROTO_SOURCES ${${PROJECT_NAME}_PROTO_SOURCES}
  OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/fable
)

# Each input file maps to multiple output files with different extensions
# (js, js.map, d.ts, fs). Transpile ts -> d.ts (and js), then d.ts -> fs.
# Who knew front-end development could be so complicated? `\_(=_=)_/`
file(
  GLOB_RECURSE ${PROJECT_NAME}_TS_SOURCES
  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
  src/*.ts
)
set(${PROJECT_NAME}_JS_PROTOS)
set(${PROJECT_NAME}_TS_PROTOS)
set(${PROJECT_NAME}_FS_PROTOS)
foreach(_ts_source ${${PROJECT_NAME}_TS_SOURCES})
  get_filename_component(_ts_source_we ${_ts_source} NAME_WE)
  set(_prefix ${CMAKE_CURRENT_BINARY_DIR}/${_ts_source_we})
  list(APPEND ${PROJECT_NAME}_JS_PROTOS ${_prefix}.js ${_prefix}.js.map)
  list(APPEND ${PROJECT_NAME}_TS_PROTOS ${_prefix}.d.ts ${_prefix}.d.ts.map)
  list(APPEND ${PROJECT_NAME}_FS_PROTOS ${_prefix}.fs)
  add_custom_command(
    OUTPUT ${_prefix}.fs
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    COMMAND npx --no-install ts2fable ${_prefix}.d.ts ${_prefix}.fs
    DEPENDS ${_prefix}.d.ts
  )
endforeach()

add_custom_command(
  OUTPUT
    ${${PROJECT_NAME}_JS_PROTOS}
    ${${PROJECT_NAME}_TS_PROTOS}
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMAND npx
    --no-install tsc
    -p ${_tsconfig}
  DEPENDS
    ${_tsconfig}
    ${CMAKE_CURRENT_BINARY_DIR}/proto.js
)
add_custom_target(
  transpile_type_script
  ALL
  DEPENDS
    ${${PROJECT_NAME}_JS_PROTOS}
    ${${PROJECT_NAME}_TS_PROTOS}
)
add_custom_target(generate_fsts_stubs ALL DEPENDS ${${PROJECT_NAME}_FS_PROTOS})

sym_link_dir(
  TARGET_NAME link_webapp_public
  SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/public
  DEST ${CMAKE_CURRENT_BINARY_DIR}/public
)
sym_link_dir(
  TARGET_NAME link_webapp_root
  SOURCE ${CMAKE_CURRENT_BINARY_DIR}
  DEST ${CMAKE_SOURCE_DIR}/build/Spark.Admin.WebApp
)

file(
  GLOB_RECURSE ${PROJECT_NAME}_FS_SOURCES
  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
  src/*.fs
)
add_custom_command(
  OUTPUT
    ${CMAKE_CURRENT_BINARY_DIR}/bundle.js
    $<$<CONFIG:DEBUG>:${CMAKE_CURRENT_BINARY_DIR}/bundle.js.map>
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src
  COMMAND dotnet restore
  COMMAND dotnet tool restore
  COMMAND export SPARK_ADMIN_WEB_APP_OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR}
  COMMAND dotnet fable -o ${CMAKE_CURRENT_BINARY_DIR} --verbose
  COMMAND npm run build -- --mode $<IF:$<CONFIG:Debug>,"development","production">
  DEPENDS
    ${${PROJECT_NAME}_FS_SOURCES}
    ${${PROJECT_NAME}_FS_PROTOS}
    ${CMAKE_CURRENT_SOURCE_DIR}/src/Spark.Admin.WebApp.fsproj
)
add_custom_target(
  bundle_spark_admin_webapp ALL
  DEPENDS
    ${CMAKE_CURRENT_BINARY_DIR}/bundle.js
    $<$<CONFIG:DEBUG>:${CMAKE_CURRENT_BINARY_DIR}/bundle.js.map>
  COMMENT "Bundling Spark.Admin.WebApp"
)

# Exclude 'public' dir because it is a symlink
install(
  DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
  DESTINATION "${CMAKE_INSTALL_BINDIR}"
  PATTERN "public" EXCLUDE
)
# Install actual (non-symlinked) 'public' dir
install(
  DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/public"
  DESTINATION "${CMAKE_INSTALL_BINDIR}/${PROJECT_NAME}"
)
