cmake_minimum_required(VERSION 3.16)

# set project name and version
project(QFitsView VERSION 4.2)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

if (WIN32)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj") 
endif()

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)

# control where the static and shared libraries are built so that on windows
# we don't need to tinker with the path to run the executable
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")

# FLAGS
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)

option(LBT "LBT" OFF)
option(HAS_VTK "has VTK" ON)
option(HAS_GDL "has GDL" OFF)
option(HAS_PYTHON "has Python" ON)
option(HAS_PLPLOT "has PLPlot" OFF)
option(HAS_PGPLOT "has PGPlot" ON)
option(HAS_DPPGPLOT "has DPPGPlot" ON)
option(HAS_PG2PLPLOT "has PG2PLPlot" OFF)

add_definitions(-DDPQT)
add_definitions(-DNO_READLINE)
# dpuser definitions are unused
set(DPUSER_DEFINITIONS -o QT=ON)

if(HAS_VTK)
  add_definitions(-DHAS_VTK)
  set(DPUSER_DEFINITIONS ${DPUSER_DEFINITIONS} -o HAS_VTK=ON)
endif()
if(HAS_GDL)
  add_definitions(-DHAS_GDL)
  include_directories("../include/gdl/")
  set(DPUSER_DEFINITIONS ${DPUSER_DEFINITIONS} -o HAS_GDL=ON)
endif()
if(HAS_PYTHON)
  add_definitions(-DHAS_PYTHON)
  set(DPUSER_DEFINITIONS ${DPUSER_DEFINITIONS} -o HAS_PYTHON=ON)
  if (WIN32)
    include_directories("../include/WIN/python/")
  elseif(APPLE)
    include_directories("../include/MACOSX/python/")
  else()
    include_directories("../include/LINUX/python/")
  endif()
endif()
if(HAS_PLPLOT)
  add_definitions(-DHAS_PLPLOT)
  set(DPUSER_DEFINITIONS ${DPUSER_DEFINITIONS} -o HAS_PLPOT=ON)
endif()
if(HAS_PGPLOT)
  add_definitions(-DHAS_PGPLOT)
  set(DPUSER_DEFINITIONS ${DPUSER_DEFINITIONS} -o HAS_PGPLOT=ON)
endif()
if(HAS_DPPGPLOT)
  add_definitions(-DHAS_DPPGPLOT)
  set(DPUSER_DEFINITIONS ${DPUSER_DEFINITIONS} -o HAS_DPPGPLOT=ON)
endif()
if(HAS_PG2PLPLOT)
  add_definitions(-DHAS_PG2PLPLOT)
  set(DPUSER_DEFINITIONS ${DPUSER_DEFINITIONS} -o HAS_P2PLPLOT=ON)
endif()

if (HAS_PGPLOT AND HAS_PG2PLPLOT)
  message("Both HAS_PGPLOT and HAS_PG2PLPLOT defined, thus DPPGPLOT must be undefined.")
  unset(HAS_DPPGPLOT)
  remove_definitions(-DHAS_DPPGPLOT)
endif()

if(WIN32)
  add_definitions(-DHAVE_MINGW32)
  add_definitions(-DWIN)
endif()

# Qt modules
if (NOT QT_PATH)
  if(WIN32)
    message("win32")
    get_filename_component(QT_PATH "/Qt/static" ABSOLUTE)
  elseif(APPLE)
    message("apple")
    get_filename_component(QT_PATH "/Users/ott/Qt/6.5.3/macos/lib/cmake/Qt6" ABSOLUTE)
  else()
    get_filename_component(QT_PATH "/home/ott/Qt/6.5.3/gcc_64/lib/cmake/Qt6" ABSOLUTE)
  endif()
  message("Looking for QT in ${QT_PATH}.")
endif()

set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_PATH})

find_package(Qt6 COMPONENTS Network PrintSupport Svg DataVisualization REQUIRED)

# extra subdirectory
include_directories("extra/")

# QFitsBuffers subdirectory
include_directories("QFitsBuffers/")

# QFitsViews subdirectory
include_directories("QFitsViews/")

# QFitsWidgets subdirectory
include_directories("QFitsWidgets/")

# QCustomPlot subdirectory
include_directories("QCustomPlot/")

# Add dpuser as external project
#@include(ExternalProject)

get_filename_component(DPUSER_PATH "../dpuser/" ABSOLUTE)

# TODO: use dpuser as external project, do NOT compile it here
#ExternalProject_Add(dpuser
#                   SOURCE_DIR ${DPUSER_PATH}
#                   BINARY_DIR ${DPUSER_PATH}
#                   PREFIX ${DPUSER_PATH}
#                   CONFIGURE_COMMAND "${DPUSER_PATH}/setup" -b "${DPUSER_PATH}" -i "${DPUSER_PATH}/install-dpuser/" # ${DPUSER_DEFINITIONS}
#                   )

#@add_custom_command(
#@                  WORKING_DIRECTORY ${DPUSER_PATH}
#@                  COMMENT "Building dpuser..."
#@                  OUTPUT ${DPUSER_PATH}/doc/helpmap.cpp ${DPUSER_PATH}/mpfitScanner.cpp ${DPUSER_PATH}/mpfitParser.cpp #@${DPUSER_PATH}/astScanner.cpp ${DPUSER_PATH}/astParser.cpp
#@                  COMMAND ./setup
#@                  COMMAND cmake --build . --parallel # ${DPUSER_DEFINITIONS}
#@                  USES_TERMINAL
#@                  )

# BISON & FLEX - uncomment if modifying the parsers
find_package(BISON 2.6)
find_package(FLEX 2.6.4)

# mpfit subdirectory
IF(BISON_FOUND AND FLEX_FOUND)
  BISON_TARGET(mpfitParser ${DPUSER_PATH}/mpfit/mpfit.y "${CMAKE_CURRENT_BINARY_DIR}/mpfitParser.cpp" DEFINES_FILE "${CMAKE_CURRENT_BINARY_DIR}/mpfitParser.h")

  ADD_CUSTOM_TARGET(
     patchMpfitParser
     COMMAND ${CMAKE_COMMAND} -P "${DPUSER_PATH}/mpfit/patch_mpfit_parser.cmake"
     DEPENDS ${BISON_mpfitParser_OUTPUTS}
  )

  FLEX_TARGET(mpfitScanner ${DPUSER_PATH}/mpfit/mpfit.l "${CMAKE_CURRENT_BINARY_DIR}/mpfitScanner.cpp" COMPILE_FLAGS "-l")

  ADD_CUSTOM_TARGET(
     patchMpfitScanner
     COMMAND ${CMAKE_COMMAND} -P "${DPUSER_PATH}/mpfit/patch_mpfit_scanner.cmake"
     DEPENDS ${FLEX_mpfitScanner_OUTPUTS}
  )

  ADD_FLEX_BISON_DEPENDENCY(mpfitScanner mpfitParser)



# parser subdirectory
  BISON_TARGET(astParser ${DPUSER_PATH}/parser/ast.y "${CMAKE_CURRENT_BINARY_DIR}/astParser.cpp" DEFINES_FILE "${CMAKE_CURRENT_BINARY_DIR}/astParser.h")
FLEX_TARGET(astScanner ${DPUSER_PATH}/parser/ast.l "${CMAKE_CURRENT_BINARY_DIR}/astScanner.cpp"  COMPILE_FLAGS "-l")
ADD_FLEX_BISON_DEPENDENCY(astScanner astParser)
ENDIF()

# include dpuser codepath
include_directories("${DPUSER_PATH}")
include_directories("${DPUSER_PATH}/parser/")
include_directories("${DPUSER_PATH}/mpfit/")

set(DPUSER
   ${DPUSER_PATH}/arithmetics.cpp
   ${BISON_astParser_OUTPUTS}
   ${FLEX_astScanner_OUTPUTS}
   ${DPUSER_PATH}/boolean.cpp
   ${DPUSER_PATH}/dpuser.yacchelper.cpp
   ${DPUSER_PATH}/dpuser.input.cpp
   ${DPUSER_PATH}/dpuser_utils.cpp
   ${DPUSER_PATH}/dpuser.procs.cpp
   ${DPUSER_PATH}/dpuserType.cpp
   ${DPUSER_PATH}/dpuserAST.cpp
   ${DPUSER_PATH}/functions.cpp
   ${DPUSER_PATH}/gdl_dpuser.cpp
   ${DPUSER_PATH}/procedures.cpp
   ${DPUSER_PATH}/python_dpuser.cpp
   ${DPUSER_PATH}/doc/helpmap.cpp
   ${DPUSER_PATH}/mpfit/mpfitAST.cpp
   )

IF(BISON_FOUND AND FLEX_FOUND)
set(DPUSER
   ${DPUSER}
   ${BISON_astParser_OUTPUTS}
   ${FLEX_astScanner_OUTPUTS}
   ${BISON_mpfitParser_OUTPUTS}
   ${FLEX_mpfitScanner_OUTPUTS}
)
ELSE()
set(DPUSER
   ${DPUSER}
   ${DPUSER_PATH}/parser/astScanner.cpp
   ${DPUSER_PATH}/parser/astParser.cpp
   ${DPUSER_PATH}/mpfit/mpfitScanner.cpp
   ${DPUSER_PATH}/mpfit/mpfitParser.cpp
)
ENDIF()


# other dependencies to include: libfits
include_directories("../libfits/")

set(LIBFITS
   ../libfits/3d_stuff.cpp
   ../libfits/cube.c
   ../libfits/dpheader.cpp
   ../libfits/fits.cpp
   ../libfits/fits_cube.cpp
   ../libfits/fits_dpl.cpp
   ../libfits/fits_exc.cpp
   ../libfits/fits_file.cpp
   ../libfits/fits_filters.cpp
   ../libfits/fits_funcs.cpp
   ../libfits/fits_mem.cpp
   ../libfits/fits_ops.cpp
   ../libfits/fits_procs.cpp
   ../libfits/fits_range.cpp
   ../libfits/fits_red.cpp
   ../libfits/JulianDay.cpp
   ../libfits/math_utils.cpp
   ../libfits/dpComplex.cpp
   ../libfits/fitting.cpp
   ../libfits/voronoi.cpp
   ../libfits/fits_logic.cpp
   ../libfits/astrolib.cpp
   )

# other dependencies to include: utils
include_directories("../utils/")
include_directories("../utils/kabsch/")
include_directories("../utils/cmpfit/")
include_directories("../utils/regex/")

set(UTILS
   ../utils/dpstring.cpp
   ../utils/dpstringlist.cpp
   ../utils/cpgplot.cpp
   ../utils/regex/regex_sr.cpp
   ../utils/cmpfit/mpfit.cpp
   ../utils/kabsch/kabsch2d.cpp
   )

if (HAS_PGPLOT AND HAS_DPPGPLOT)
  set(UTILS
     ${UTILS}
     ../utils/cpg3d.c
     )
endif()

# Additional settings and files
if (HAS_VTK)
  set(EXTRAS
     ${EXTRAS}
     "QFitsWidgets/QFitsWidget3D.cpp"
     "QFitsViews/QFitsView3D.cpp"
     )
endif()

if (LBT)
  set(EXTRAS
     ${EXTRAS}
     "lbt.cpp"
     )
endif()

foreach(file ${EXTRAS})
  message(STATUS "${file}")
endforeach()

# other dependencies to include: include
include_directories("../include/")

# build the executable: QFitsView
add_executable(QFitsView WIN32
              dialogs.cpp
              events.cpp
              galfit_control.ui
              galfit_gaussian.ui
              galfit_hidden.ui
              galfit_sersic.ui
              galfit_sky.ui
              galfit.cpp
              guitools.cpp
              highlighter.cpp
              imred.cpp
              lut.cpp
              main.cpp
              qt_mainwindow.cpp
              qt_mdichild.cpp
              qtdpuser.cpp
              QPgplot.cpp
              QFitsCubeSpectrum.cpp
              QFitsGlobal.cpp
              QFitsHeaderView.cpp
              QFitsMainWindow.cpp
              QFitsMainView.cpp
              QFitsMarkers.cpp
              QFitsPreferences.cpp
              QFitsToolBar.cpp
              QFitsTools.cpp
              QFitsViewingTools.cpp
              QFitsWedge.cpp
              RGBDialog.cpp
              # QFitsBuffers
              QFitsBuffers/QFitsBaseBuffer.cpp
              QFitsBuffers/QFitsSingleBuffer.cpp
              QFitsBuffers/QFitsMultiBuffer.cpp
              QFitsScroller.cpp
              # QFitsWidgets
              QFitsWidgets/QFitsBaseWidget.cpp
              QFitsWidgets/QFitsWidget1D.cpp
              QFitsWidgets/QFitsWidget2D.cpp
              QFitsWidgets/QFitsWidgetWiregrid.cpp
              QFitsWidgets/QFitsWidgetContour.cpp
              QFitsWidgets/QFitsWidgetTable.cpp
              # QFitsViews
              QFitsViews/QFitsBaseView.cpp
              QFitsViews/QFitsView1D.cpp
              QFitsViews/QFitsView2D.cpp
              # QCustomPlot
              QCustomPlot/qcustomplot.cpp
              # extra
              extra/tetrixboard.cpp
              extra/tetrixpiece.cpp
              extra/tetrixwindow.cpp
              # other dependencies
              ${DPUSER}
              ${LIBFITS}
              ${UTILS}
              # extras
              ${EXTRAS}
              # qrc
              doc/QFitsViewDoc.qrc
              resources/QFitsViewRes.qrc
	      QFitsView.rc
              )

IF(BISON_FOUND AND FLEX_FOUND)
add_dependencies(QFitsView patchMpfitParser)
add_dependencies(QFitsView patchMpfitScanner)
ENDIF()

# DEPENDENCIES: QT (SHARED | STATIC)
if (NOT BUILD_SHARED_LIBS)
  message("Statically linking to QT. Please note that a manual static compilation of Qt is required, otherwise dynamic libraries are used. Looking in ${QT_PATH}.")
endif()

set(QT_LIBRARIES PrintSupport Svg DataVisualization Network)

message("Included Qt libraries (please make sure that they are installed):")
foreach(library ${QT_LIBRARIES})
  message("* Qt${library}")
  target_link_libraries(QFitsView Qt::${library})
endforeach()

# DEPENDENCIES: SYSTEM LIBRARIES (SHARED | STATIC)

if(UNIX AND NOT APPLE)
    set(LINUX TRUE)
endif()

if (NOT LIB_PATH)
  if (WIN32)
    GET_FILENAME_COMPONENT(LIB_PATH "../lib/WIN64" ABSOLUTE)
	endif()
  if (APPLE)
    GET_FILENAME_COMPONENT(LIB_PATH "../lib/MACOS" ABSOLUTE)
	endif()
  if (LINUX)
    GET_FILENAME_COMPONENT(LIB_PATH "../lib/LINUX64" ABSOLUTE)
  endif()
endif()

if (NOT BUILD_SHARED_LIBS)
#  if (NOT LIB_PATH)
#    set(LIB_PATH "../lib/")
#  endif()

  message("Statically linking system libraries. Please note: Manual compilation of system libraries is required. Looking in ${LIB_PATH}.")
  target_link_libraries(QFitsView -static-libgcc -static-libstdc++ -static)
#  target_link_libraries(QFitsView -static)
#  set(CMAKE_EXE_LINKER_FLAGS "-static")
endif()

if (WIN32)
  target_link_libraries(QFitsView -static-libgcc -static-libstdc++ -static)
endif()

if (APPLE)
   set(app_icon_macos "${CMAKE_CURRENT_SOURCE_DIR}/resources/telescope.icns")
   set_source_files_properties(${app_icon_macos} PROPERTIES
           MACOSX_PACKAGE_LOCATION "Resources")
   set_target_properties(QFitsView PROPERTIES
      MACOSX_BUNDLE TRUE
      MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist"
      MACOSX_BUNDLE_ICON_FILE telescope.icns
   )
endif()

# BASE
set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} xpa fftw3 readline)

if(LINUX)
  set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ncurses X11 dl xcb pthread Xdmcp Xau)
endif()

# PGPLOT
if (HAS_PGPLOT AND NOT HAS_PG2PLPLOT)
  if(WIN32)
    set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} cpgplot pgplot f2c)
  elseif(APPLE)
    set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} cpgplot pgplot_qf png f2c)
  else()
    set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} cpgplot pgplot png f2c)
  endif()
endif()

# PLPLOT
if ((HAS_PGPLOT AND HAS_PG2PLPLOT) OR HAS_PLPLOT OR HAS_GDL)
  set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} plplotcxx fftw3f plplot qsastime)
endif()

# GDL
if (HAS_GDL)
  set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} gdl)
endif()

# LINUX
if(LINUX)
  set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} Xt mng)
endif()

set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} gsl gslcblas cfitsio z)

# Include libraries
message("Included libraries (please make sure that they are installed):")
foreach(library ${SYSTEM_LIBRARIES})
  message("* lib${library}")

  find_library(dynlib${library}
              "lib${library}.so"
              )
  if (BUILD_SHARED_LIBS AND dynlib${library})
    target_link_libraries(QFitsView -l${library})
    message(${dynlib${library}})
  else()
    find_library(libpath${library}
                "lib${library}.a"
                HINTS "${LIB_PATH}"
                )
    message(${libpath${library}})

    add_library(lib${library} STATIC IMPORTED)
    set_property(TARGET lib${library} PROPERTY IMPORTED_LOCATION ${libpath${library}})
    target_link_libraries(QFitsView lib${library})
  endif()
endforeach()

# Include directories
target_include_directories(QFitsView PUBLIC
                          "${PROJECT_BINARY_DIR}"
                          "${PROJECT_SOURCE_DIR}"
                          "${PROJECT_SOURCE_DIR}/extra"
                          "${PROJECT_SOURCE_DIR}/QCustomPlot"
                          "${PROJECT_SOURCE_DIR}/QFitsBuffers"
                          "${PROJECT_SOURCE_DIR}/QFitsViews"
                          "${PROJECT_SOURCE_DIR}/QFitsWidgets"
                          "${PROJECT_SOURCE_DIR}/QFitsWidgets"
                          "${QT_PATH}lib"
                          )

# for installation
install(TARGETS QFitsView DESTINATION bin)
