# Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-443271.
#
# This file is part of the GLVis visualization tool and library. For more
# information and source code availability see https://glvis.org.
#
# GLVis is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.

# CMake v3.10 is where "Imported Targets" like "Freetype::Freetype" are
# introduced.
cmake_minimum_required(VERSION 3.10)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Prohibit in-source builds
if ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
  message(FATAL_ERROR "In-source builds are prohibited.")
endif ("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")

project(glvis NONE)

# Import MFEM. The following variables can be used to help CMake find MFEM:
#  * MFEM_DIR - absolute path to the MFEM build or install prefix.
#  * mfem_DIR - absolute path to where MFEMConfig.cmake is.
message(STATUS "Looking for mfem ...")
set(MFEM_DIR "" CACHE PATH "Path to the MFEM build or install prefix.")
if (MFEM_DIR)
   find_package(mfem REQUIRED NAMES MFEM HINTS "${MFEM_DIR}"
                "${MFEM_DIR}/lib/cmake/mfem" NO_DEFAULT_PATH)
else()
   find_package(mfem REQUIRED NAMES MFEM)
endif()
message(STATUS "Found mfem config in: ${mfem_DIR} (version ${MFEM_VERSION})")
# Use the same C++ compiler as MFEM. This is needed when MFEM was built using
# an MPI wrapper and we do not have explicitly the MPI compile and link flags.
if (NOT CMAKE_CXX_COMPILER AND MFEM_CXX_COMPILER)
  set(CMAKE_CXX_COMPILER "${MFEM_CXX_COMPILER}")
endif()

enable_language(C)
enable_language(CXX)

# Default options match the Makefile
if (NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE "Release")
endif()

option(GLVIS_USE_LIBTIFF
  "Use libtiff for taking screenshots internally"
  OFF)

option(GLVIS_USE_LIBPNG
  "Use libpng for taking screenshots internally"
  ON)

#
# Handle a few other definitions
#

# Default multisampling mode
if (NOT GLVIS_MULTISAMPLE)
  set(GLVIS_MULTISAMPLE 4)
endif (NOT GLVIS_MULTISAMPLE)

# Default multisampling line-width
if (NOT GLVIS_MS_LINEWIDTH)
  if (NOT APPLE)
    set(GLVIS_MS_LINEWIDTH 1.4)
  else()
    # This value seems to work better on Macs
    set(GLVIS_MS_LINEWIDTH 1.0)
  endif()
endif (NOT GLVIS_MS_LINEWIDTH)

# Default font size
if (NOT GLVIS_FONT_SIZE)
  set(GLVIS_FONT_SIZE 12)
endif (NOT GLVIS_FONT_SIZE)

#
# Start finding everything
#

set(_glvis_compile_defs)
set(_glvis_compile_opts)
set(_glvis_include_dirs)
set(_glvis_libraries)

list(APPEND _glvis_compile_defs "GLVIS_MULTISAMPLE=${GLVIS_MULTISAMPLE}")
list(APPEND _glvis_compile_defs "GLVIS_MS_LINEWIDTH=${GLVIS_MS_LINEWIDTH}")
list(APPEND _glvis_compile_defs "GLVIS_FONT_SIZE=${GLVIS_FONT_SIZE}")
if (NOT WIN32)
  list(APPEND _glvis_compile_defs "GLVIS_USE_LOGO")
else()
  list(APPEND _glvis_compile_defs "_USE_MATH_DEFINES")
endif()

if(NOT WIN32)
  find_program(XXD_FOUND xxd)
  if(NOT XXD_FOUND)
    message(FATAL_ERROR "Required tool not found: xxd.")
  endif()
endif()

if (CMAKE_BUILD_TYPE MATCHES "Debug|debug|DEBUG")
  list(APPEND _glvis_compile_defs "GLVIS_DEBUG")
  list(APPEND _glvis_compile_opts "-Wall")
endif()

# Include paths and libraries needed by MFEM
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MFEM_CXX_FLAGS}")
list(APPEND _glvis_include_dirs "${MFEM_INCLUDE_DIRS}")
list(APPEND _glvis_libraries "${MFEM_LIBRARIES}")

if (NOT EMSCRIPTEN)

  # Find OpenGL
  find_package(OpenGL REQUIRED)
  list(APPEND _glvis_libraries OpenGL::GL)

  # Find 'SDL2' which is not part of the CMake standard modules
  list(APPEND SDL2_DIR "../SDL2")
  set(SDL2_DIR_SAVE ${SDL2_DIR})
  find_package(SDL2 QUIET HINTS ${SDL2_DIR})
  # The SDL2 target 'SDL2::SDL2' is not always defined, so instead we use
  # directly 'SDL2_INCLUDE_DIRS' and 'SDL2_LIBRARIES'.
  if (TARGET SDL2::SDL2)
    list(APPEND _glvis_libraries SDL2::SDL2)
  elseif (SDL2_INCLUDE_DIRS AND SDL2_LIBRARIES)
    # SDL2_INCLUDE_DIRS is defined with 'SDL2' at the end of the path we need to
    # strip that.
    set(new_list_)
    foreach (path IN LISTS SDL2_INCLUDE_DIRS)
      string(REGEX REPLACE "/include/SDL2$" "/include" new_path_ "${path}")
      list(APPEND new_list_ "${new_path_}")
    endforeach()
    set(SDL2_INCLUDE_DIRS ${new_list_})
    list(APPEND _glvis_include_dirs "${SDL2_INCLUDE_DIRS}")
    list(APPEND _glvis_libraries "${SDL2_LIBRARIES}")
  else()
    # find_package() did not work; try more direct approach.
    set(SDL2_DIR ${SDL2_DIR_SAVE})
    find_path(SDL2_INCLUDE_DIRS "SDL2/SDL.h"
      HINTS ${SDL2_DIR} ENV SDL2_DIR
      PATH_SUFFIXES "include"
      DOC "SDL2 include path.")
    find_library(SDL2_LIBRARIES "SDL2"
      HINTS ${SDL2_DIR} ENV SDL2_DIR
      PATH_SUFFIXES "lib"
      DOC "SDL2 library.")
    if (NOT (SDL2_INCLUDE_DIRS AND SDL2_LIBRARIES))
      message(FATAL_ERROR "SDL2 library not found. Please set SDL2_DIR.")
    endif()
    list(APPEND _glvis_include_dirs "${SDL2_INCLUDE_DIRS}")
    list(APPEND _glvis_libraries "${SDL2_LIBRARIES}")
  endif()
  message(STATUS "SDL2 found: ${SDL2_LIBRARIES}")
  message(STATUS "SDL2_INCLUDE_DIRS = ${SDL2_INCLUDE_DIRS}")

  # Find GLEW
  # GLEW search path can be set using CMAKE_PREFIX_PATH
  list(APPEND CMAKE_PREFIX_PATH "../glew")
  find_package(GLEW REQUIRED)
  list(APPEND _glvis_libraries GLEW::GLEW)

  # Find 'glm' which is not part of the CMake standard modules
  list(APPEND GLM_DIR "../glm")
  find_package(glm QUIET HINTS ${GLM_DIR})
  if (NOT GLM_INCLUDE_DIRS)
    find_path(GLM_INCLUDE_DIRS "glm/glm.hpp"
      HINTS ${GLM_DIR} ENV GLM_DIR
      PATH_SUFFIXES "include"
      DOC "GLM include path.")
    if (NOT GLM_INCLUDE_DIRS)
      message(FATAL_ERROR "GLM headers not found. Please set GLM_DIR.")
    endif()
  endif()
  message(STATUS "GLM headers found: ${GLM_INCLUDE_DIRS}")
  list(APPEND _glvis_include_dirs "${GLM_INCLUDE_DIRS}")

  # Find TIFF
  if (GLVIS_USE_LIBTIFF)
    find_package(TIFF)
    if (TIFF_FOUND)
      list(APPEND _glvis_compile_defs "GLVIS_USE_LIBTIFF")
      list(APPEND _glvis_libraries TIFF::TIFF)
    else()
      message(WARNING "TIFF library not found. TIFF disabled.")
      set(GLVIS_USE_LIBTIFF OFF)
    endif (TIFF_FOUND)
  endif (GLVIS_USE_LIBTIFF)

  # Find PNG
  if (GLVIS_USE_LIBPNG)
    find_package(PNG)
    if (PNG_FOUND)
      list(APPEND _glvis_compile_defs "GLVIS_USE_LIBPNG")
      list(APPEND _glvis_libraries PNG::PNG)
    else()
      message(WARNING "PNG library not found. PNG disabled.")
      set(GLVIS_USE_LIBPNG OFF)
    endif (PNG_FOUND)
  endif (GLVIS_USE_LIBPNG)

  # Find FreeType.
  find_package(Freetype REQUIRED)
  # Find FontConfig (FindFontconfig.cmake was added in CMake 3.14)
  find_library(FONTCONFIG_LIBRARY fontconfig
    HINTS ${FONTCONFIG_DIR} $ENV{FONTCONFIG_DIR}
    DOC "The fontconfig library for use with FreeType."
    NO_DEFAULT_PATH)
  find_library(FONTCONFIG_LIBRARY fontconfig)
  if (FONTCONFIG_LIBRARY)
    list(APPEND _glvis_libraries Freetype::Freetype)
    # Need FONTCONFIG_INCLUDE_DIRS?
    list(APPEND _glvis_libraries "${FONTCONFIG_LIBRARY}")
    message(STATUS "Found Fontconfig: ${FONTCONFIG_LIBRARY}")
  else()
    message(STATUS "Fontconfig not found. Please set FONTCONFIG_DIR.")
    message(FATAL_ERROR "Fontconfig not found.")
  endif (FONTCONFIG_LIBRARY)

  # Find threading library
  find_package(Threads REQUIRED)
  list(APPEND _glvis_libraries "${CMAKE_THREAD_LIBS_INIT}")
  if (CMAKE_USE_PTHREADS_INIT)
    message(STATUS "System threading library: pthreads")
  elseif (CMAKE_USE_WIN32_THREADS_INIT)
    message(STATUS "System threading library: Win32 threads")
  elseif (Threads_FOUND)
    message(STATUS "System threading library: other")
  endif()

else(NOT EMSCRIPTEN)

  find_package(glm REQUIRED)
  list(APPEND _glvis_include_dirs "${GLM_INCLUDE_DIRS}")
  set(_emscripten_opts)
  # Enable embind
  list(APPEND _emscripten_opts "--bind")

  # OpenGL, SDL2, and GLEW are provided by the Emscripten runtime
  # Enable SDL2
  list(APPEND _emscripten_opts "-s USE_SDL=2")
  list(APPEND _emscripten_opts
      "-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0")

  # Set WebGL options
  list(APPEND _emscripten_opts
      "-s GL_ASSERTIONS=1 -s GL_DEBUG=1 -s MAX_WEBGL_VERSION=2")

  # Enable Freetype port
  list(APPEND _emscripten_opts "-s USE_FREETYPE=1")

  # Module export options
  list(APPEND _emscripten_opts "-s ENVIRONMENT=web")
  list(APPEND _emscripten_opts "-s MODULARIZE=1")
  list(APPEND _emscripten_opts "-s EXPORT_NAME=glvis")

  # Other emscripten options
  list(APPEND _emscripten_opts "-s ALLOW_MEMORY_GROWTH=1")
  list(APPEND _emscripten_opts "-s SINGLE_FILE=1")
  list(APPEND _emscripten_opts "--no-heap-copy")

  # Since we don't have access to fontconfig, embed a font
  if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/OpenSans.ttf")
    message(FATAL_ERROR "Preload font file \"OpenSans.ttf\" not found.")
  endif()
  list(APPEND _emscripten_opts
       "--embed-file ${CMAKE_CURRENT_SOURCE_DIR}/OpenSans.ttf@OpenSans.ttf")

endif(NOT EMSCRIPTEN)

message(STATUS "GLVis build type: CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}")
message(STATUS "GLVis defines: ${_glvis_compile_defs}")
# message(STATUS "GLVis opts: ${_glvis_compile_opts}")
# message(STATUS "GLVis include dirs: ${_glvis_include_dirs}")
# message(STATUS "GLVis libraries: ${_glvis_libraries}")
message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}")


#
# Setup the GLVis library target
#

add_subdirectory(lib)
add_subdirectory(share)

if(NOT EMSCRIPTEN)

  # Setup the GLVis executable
  set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON CACHE BOOL "")
  set(CMAKE_INSTALL_RPATH "${MFEM_LIBRARY_DIR}" CACHE PATH "")

  add_executable(glvis-exe glvis.cpp)
  set_target_properties(glvis-exe PROPERTIES OUTPUT_NAME glvis)

  target_link_libraries(glvis-exe PRIVATE glvis)

  if (WIN32)
    target_sources(glvis-exe
      PRIVATE
        "share/windows.manifest"
        "${CMAKE_CURRENT_BINARY_DIR}/share/resource.rc")
  else()
    target_link_libraries(glvis-exe PRIVATE glvis_logo)
  endif (WIN32)

  # Install the executable
  install(TARGETS glvis-exe RUNTIME DESTINATION bin)

  # Install the gnutls helper script
  if (MFEM_USE_GNUTLS)
    install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/glvis-keygen.sh
      DESTINATION bin
      PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
        GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
  endif (MFEM_USE_GNUTLS)

  if (APPLE)
    set(GLVIS_APP_ICON ${CMAKE_CURRENT_SOURCE_DIR}/share/GLVis.icns)
    set(GLVIS_APP_CREDITS ${CMAKE_CURRENT_SOURCE_DIR}/share/Credits.rtf)
    set_source_files_properties(
      ${GLVIS_APP_ICON} ${GLVIS_APP_CREDITS}
      PROPERTIES
        MACOSX_PACKAGE_LOCATION "Resources")

    add_executable(app MACOSX_BUNDLE glvis.cpp
      ${GLVIS_APP_ICON} ${GLVIS_APP_CREDITS})
    set_target_properties(app
      PROPERTIES
        EXCLUDE_FROM_ALL TRUE
        OUTPUT_NAME GLVis
        MACOSX_BUNDLE_INFO_PLIST
          ${CMAKE_CURRENT_SOURCE_DIR}/share/Info.cmake.plist.in)
    target_link_libraries(app PRIVATE glvis glvis_logo)
    install(TARGETS app
      RUNTIME DESTINATION .
      BUNDLE DESTINATION .)
    install(CODE [[
      include (BundleUtilities)
      fixup_bundle("${CMAKE_INSTALL_PREFIX}/GLVis.app" "" "")
      file(GLOB LIBS_TO_SIGN
          "${CMAKE_INSTALL_PREFIX}/GLVis.app/Contents/Frameworks/*.dylib")
      foreach(LIB ${LIBS_TO_SIGN})
        if (NOT IS_SYMLINK ${LIB})
          execute_process(COMMAND codesign --force --sign - ${LIB})
        endif()
      endforeach()
      execute_process(COMMAND codesign --force --sign - ${CMAKE_INSTALL_PREFIX}/GLVis.app)
    ]] COMPONENT RUNTIME)
  endif(APPLE)

endif(NOT EMSCRIPTEN)

if(ENABLE_TESTS)
  enable_testing()
  add_subdirectory(tests)
endif(ENABLE_TESTS)
