########################################################################################
# Compiler options for building DAMASK
cmake_minimum_required (VERSION 3.10.0 FATAL_ERROR)

#---------------------------------------------------------------------------------------
# Find PETSc from system environment
set(PETSC_DIR $ENV{PETSC_DIR})
if (PETSC_DIR STREQUAL "")
    message (FATAL_ERROR "PETSc location (PETSC_DIR) is not defined")
endif ()

set (petsc_conf_variables "${PETSC_DIR}/lib/petsc/conf/variables")
set (petsc_conf_rules     "${PETSC_DIR}/lib/petsc/conf/rules"    )

# Use existing variables from PETSc
# https://github.com/jedbrown/cmake-modules/blob/master/FindPETSc.cmake

# Generate a temporary makefile to probe the PETSc configuration
# This file will be deleted once the settings from PETSc are parsed into CMake
exec_program (mktemp ARGS -d OUTPUT_VARIABLE TEMPDIR)
set (petsc_config_makefile "${TEMPDIR}/Makefile.petsc")
file (WRITE
"${petsc_config_makefile}"
"## This file was auto generated by CMake
# PETSC_DIR  = ${PETSC_DIR}
SHELL = /bin/sh
include ${petsc_conf_rules}
include ${petsc_conf_variables}
INCLUDE_DIRS   := \${PETSC_FC_INCLUDES}
LIBRARIES      := \${PETSC_WITH_EXTERNAL_LIB}
COMPILERF      := \${FC}
COMPILERC      := \${CC}
LINKERNAME     := \${FLINKER}
includes:
\t@echo \${INCLUDE_DIRS}
extlibs:
\t@echo \${LIBRARIES}
compilerf:
\t@echo \${COMPILERF}
compilerc:
\t@echo \${COMPILERC}
linker:
\t@echo \${LINKERNAME}
")

# CMake will execute each target in the ${petsc_config_makefile}
# to acquire corresponding PETSc Variables.
find_program (MAKE_EXECUTABLE NAMES gmake make)
# Find the PETSc includes directory settings
execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} "includes"
                 RESULT_VARIABLE PETSC_INCLUDES_RETURN
                 OUTPUT_VARIABLE petsc_includes
                 OUTPUT_STRIP_TRAILING_WHITESPACE)
# Find the PETSc external linking directory settings
# required for final linking, must be appended after the executable
execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} "extlibs"
                 RESULT_VARIABLE PETSC_EXTERNAL_LIB_RETURN
                 OUTPUT_VARIABLE petsc_external_lib
                 OUTPUT_STRIP_TRAILING_WHITESPACE)
# PETSc specified fortran compiler
execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} "compilerf"
                 RESULT_VARIABLE PETSC_MPIFC_RETURN
                 OUTPUT_VARIABLE PETSC_MPIFC
                 OUTPUT_STRIP_TRAILING_WHITESPACE)
# PETSc specified C compiler
execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} "compilerc"
                 RESULT_VARIABLE PETSC_MPICC_RETURN
                 OUTPUT_VARIABLE PETSC_MPICC
                 OUTPUT_STRIP_TRAILING_WHITESPACE)
# PETSc specified linker (Fortran compiler + PETSc linking flags)
execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} "linker"
                 RESULT_VARIABLE PETSC_LINKER_RETURN
                 OUTPUT_VARIABLE PETSC_LINKER
                 OUTPUT_STRIP_TRAILING_WHITESPACE)
# Remove temporary makefile, no need to keep it anymore.
file (REMOVE_RECURSE ${TEMPDIR})

# Remove duplicate compiler and linker flags
string (REGEX MATCHALL "-I([^\" ]+)" TMP_LIST "${petsc_includes}")
list (REMOVE_DUPLICATES TMP_LIST)
foreach (dir ${TMP_LIST})
    set (PETSC_INCLUDES "${PETSC_INCLUDES} ${dir}")
endforeach (dir)
string (REGEX MATCHALL "-[lLW]([^\" ]+)" TMP_LIST "${petsc_external_lib}")
list (REMOVE_DUPLICATES TMP_LIST)
foreach (exlib ${TMP_LIST})
    set (PETSC_EXTERNAL_LIB "${PETSC_EXTERNAL_LIB} ${exlib}")
endforeach (exlib)

message ("Found PETSC_DIR:\n${PETSC_DIR}\n"                  )
message ("Found PETSC_INCLUDES:\n${PETSC_INCLUDES}\n"        )
message ("Found PETSC_EXTERNAL_LIB:\n${PETSC_EXTERNAL_LIB}\n")
message ("Found PETSC_LINKER:\n${PETSC_LINKER}\n"            )
message ("Found MPI Fortran Compiler:\n${PETSC_MPIFC}\n"     )
message ("Found MPI C Compiler:\n${PETSC_MPICC}\n"           )

# set compiler commands to match PETSc (needs to be done before defining the project)
# https://cmake.org/Wiki/CMake_FAQ#How_do_I_use_a_different_compiler.3F
set (CMAKE_Fortran_COMPILER "${PETSC_MPIFC}")
set (CMAKE_C_COMPILER       "${PETSC_MPICC}")

#---------------------------------------------------------------------------------------
# Now start to care about DAMASK

# DAMASK solver defines project to build
string(TOLOWER ${DAMASK_SOLVER} DAMASK_SOLVER)
if (DAMASK_SOLVER STREQUAL "grid")
    project (damask-grid Fortran C)
    add_definitions (-DGrid)
    message ("Building Grid Solver\n")
elseif (DAMASK_SOLVER STREQUAL "fem" OR DAMASK_SOLVER STREQUAL "mesh")
    project (damask-mesh Fortran C)
    add_definitions (-DFEM)
    message ("Building Mesh Solver\n")
else ()
    message (FATAL_ERROR "Build target (DAMASK_SOLVER) is not defined")
endif ()
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

if (CMAKE_BUILD_TYPE STREQUAL "")
    set (CMAKE_BUILD_TYPE "RELEASE")
endif ()

# Predefined sets for OPTIMIZATION/OPENMP based on BUILD_TYPE
if (CMAKE_BUILD_TYPE STREQUAL "DEBUG" OR CMAKE_BUILD_TYPE STREQUAL "SYNTAXONLY")
    set (DEBUG_FLAGS "${DEBUG_FLAGS} -DDEBUG")
    set (PARALLEL "OFF")
    set (OPTI "OFF")
elseif (CMAKE_BUILD_TYPE STREQUAL "RELEASE")
    set (PARALLEL "ON")
    set (OPTI "DEFENSIVE")
elseif (CMAKE_BUILD_TYPE STREQUAL "PERFORMANCE")
    set (PARALLEL "ON")
    set (OPTI "AGGRESSIVE")
endif ()

# $OPTIMIZATION takes precedence over $BUILD_TYPE defaults
if (OPTIMIZATION STREQUAL "" OR NOT DEFINED OPTIMIZATION)
    set (OPTIMIZATION "${OPTI}")
else ()
    set (OPTIMIZATION "${OPTIMIZATION}")
endif ()

# $OPENMP takes precedence over $BUILD_TYPE defaults
if (OPENMP STREQUAL "" OR NOT DEFINED OPENMP)
    set (OPENMP "${PARALLEL}")
else ()
    set(OPENMP "${OPENMP}")
endif ()

# syntax check only (mainly for pre-receive hook)
if (CMAKE_BUILD_TYPE STREQUAL "SYNTAXONLY")
    set (BUILDCMD_POST "${BUILDCMD_POST} -fsyntax-only")
endif ()

# Parse DAMASK version from VERSION file
find_program (CAT_EXECUTABLE NAMES cat)
execute_process (COMMAND ${CAT_EXECUTABLE} ${PROJECT_SOURCE_DIR}/VERSION
                 RESULT_VARIABLE DAMASK_VERSION_RETURN
                 OUTPUT_VARIABLE DAMASK_V
                 OUTPUT_STRIP_TRAILING_WHITESPACE)
add_definitions (-DDAMASKVERSION="${DAMASK_V}")

# definition of other macros
add_definitions (-DPETSc)

if    (CMAKE_Fortran_COMPILER_ID STREQUAL "Intel")
    include (Compiler-Intel)
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
    include (Compiler-GNU)
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "PGI")
    include (Compiler-PGI)
else ()
    message (FATAL_ERROR "Compiler type (CMAKE_Fortran_COMPILER_ID) not recognized")
endif ()


set (CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE} "${BUILDCMD_PRE} ${OPENMP_FLAGS} ${STANDARD_CHECK} ${OPTIMIZATION_FLAGS} ${COMPILE_FLAGS} ${PRECISION_FLAGS}")
set (CMAKE_Fortran_LINK_EXECUTABLE           "${BUILDCMD_PRE} ${PETSC_LINKER} ${OPENMP_FLAGS}   ${OPTIMIZATION_FLAGS} ${LINKER_FLAGS}")

if (CMAKE_BUILD_TYPE STREQUAL "DEBUG")
    set (CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE} "${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}} ${DEBUG_FLAGS}")
    set (CMAKE_Fortran_LINK_EXECUTABLE           "${CMAKE_Fortran_LINK_EXECUTABLE} ${DEBUG_FLAGS}")
endif ()

set (CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}   "${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}} ${PETSC_INCLUDES} ${BUILDCMD_POST}")
set (CMAKE_Fortran_LINK_EXECUTABLE             "${CMAKE_Fortran_LINK_EXECUTABLE} <OBJECTS> -o <TARGET> <LINK_LIBRARIES> ${PETSC_EXTERNAL_LIB} ${BUILDCMD_POST}")

message ("Fortran Compiler Flags:\n${CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE}}\n")
message ("C Compiler Flags:\n${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE}}\n")
message ("Fortran Linker Command:\n${CMAKE_Fortran_LINK_EXECUTABLE}\n")

# location of code
add_subdirectory (src)