find_package(PostgreSQL REQUIRED)

if(APPLE)
  set (EXTENSION_PREFIX "")
  if(${PostgreSQL_VERSION_STRING} VERSION_GREATER_EQUAL 16.0)
    set (EXTENSION_SUFFIX ".dylib")
    set (EXTENSION_DEST_SUFFIX ".dylib")
  else()
    set (EXTENSION_SUFFIX ".so")
    set (EXTENSION_DEST_SUFFIX ".so")
  endif()
elseif(WIN32)
  set(REGEX_SEPARATOR "\\\\")
  if(MSVC)
    set (EXTENSION_PREFIX "")
    set (EXTENSION_SUFFIX "")
    set (EXTENSION_DEST_SUFFIX ".dll")
  else()
    set(REGEX_SEPARATOR "${REGEX_SEPARATOR}${REGEX_SEPARATOR}")
    set (EXTENSION_PREFIX "lib")
    set (EXTENSION_SUFFIX "")
    set (EXTENSION_DEST_SUFFIX ".dll")
  endif()
else()
  set (EXTENSION_PREFIX "lib")
  set (EXTENSION_SUFFIX "")
  set (EXTENSION_DEST_SUFFIX ".so")
endif()

if(RDK_OPTIMIZE_POPCNT)
  add_definitions(-DRDK_OPTIMIZE_POPCNT)
endif(RDK_OPTIMIZE_POPCNT)

set(EXTENSION rdkit)
include_directories(${Boost_INCLUDE_DIRS})
message("postgres: ${PostgreSQL_INCLUDE_DIRS}")
add_definitions(-DRDK_TOOLKIT_VERSION="${RDKit_RELEASENAME}")
include_directories(${RDKit_CodeDir})
include_directories(${PostgreSQL_INCLUDE_DIRS})
if(WIN32)
  include_directories(${PostgreSQL_TYPE_INCLUDE_DIR}/port/win32)
  if(MSVC)
    include_directories(${PostgreSQL_TYPE_INCLUDE_DIR}/port/win32_msvc)
  endif(MSVC)
endif(WIN32)
if(RDK_BUILD_AVALON_SUPPORT)
  include_directories(${CMAKE_SOURCE_DIR}/External)
  add_definitions(-DRDK_BUILD_AVALON_SUPPORT)
endif(RDK_BUILD_AVALON_SUPPORT)
if(RDK_BUILD_INCHI_SUPPORT)
  include_directories(${CMAKE_SOURCE_DIR}/External)
  add_definitions(-DRDK_BUILD_INCHI_SUPPORT)
endif(RDK_BUILD_INCHI_SUPPORT)
if(RDK_BUILD_MOLINTERCHANGE_SUPPORT)
  add_definitions(-DRDK_BUILD_MOLINTERCHANGE_SUPPORT)
endif(RDK_BUILD_MOLINTERCHANGE_SUPPORT)

add_definitions(-DRDKITVER="007700")
if(NOT DEFINED PostgreSQL_ROOT)
  if(DEFINED ENV{PostgreSQL_ROOT})
    set(PostgreSQL_ROOT "$ENV{PostgreSQL_ROOT}")
  endif(DEFINED ENV{PostgreSQL_ROOT})
endif(NOT DEFINED PostgreSQL_ROOT)
if(NOT DEFINED PostgreSQL_ROOT)
  set(PostgreSQL_ROOT "${PostgreSQL_LIBRARY_DIRS}/..")
endif(NOT DEFINED PostgreSQL_ROOT)
if(NOT DEFINED PostgreSQL_CONFIG_DIR)
  set(PostgreSQL_CONFIG_DIR "${PostgreSQL_ROOT}/bin")
endif(NOT DEFINED PostgreSQL_CONFIG_DIR)
if(NOT DEFINED PostgreSQL_CONFIG)
  set(PostgreSQL_CONFIG "${PostgreSQL_CONFIG_DIR}/pg_config")
endif(NOT DEFINED PostgreSQL_CONFIG)
macro (run_pg_config arg var)
  execute_process(COMMAND ${PostgreSQL_CONFIG} ${arg}
                  RESULT_VARIABLE pgsql_config_result
                  OUTPUT_VARIABLE ${var}
                  OUTPUT_STRIP_TRAILING_WHITESPACE)
  if(NOT ${pgsql_config_result} EQUAL 0 OR NOT ${var})
    message(FATAL_ERROR "${PostgreSQL_CONFIG} ${arg} failed")
  endif()
endmacro ()
run_pg_config (--bindir PG_BINDIR)
run_pg_config (--sharedir PG_SHAREDIR)
run_pg_config (--pkglibdir PG_PKGLIBDIR)
set(PG_PKGLIBDIR "${PG_PKGLIBDIR}/")
set(PG_EXTENSIONDIR "${PG_SHAREDIR}/extension")
if(WIN32)
  add_definitions(-DWIN32)
endif()
if(MSVC)
  add_definitions(-DBUILDING_MODULE -DNOMINMAX)
  if(NOT (MSVC_VERSION LESS 1700))
    add_definitions(-DHAVE_RINT)
  endif()
endif(MSVC)
if(APPLE)
  set(CMAKE_EXE_LINKER_FLAGS "-bundle -multiply_defined suppress"
              "-Wl,-dead_strip_dylibs -bundle_loader ${PG_BINDIR}/postgres")
  add_executable("${EXTENSION}${EXTENSION_SUFFIX}"
              adapter.cpp bfp_op.c cache.c guc.c low_gist.c mol_op.c
              rdkit_gist.c bfp_gist.c bfp_gin.c bitstring.c rdkit_io.c rxn_op.c sfp_op.c)
else(APPLE)
  link_directories(${PostgreSQL_LIBRARY_DIRS})
  link_directories(${RDKit_LibDir})
  add_library(${EXTENSION} SHARED
              adapter.cpp bfp_op.c cache.c guc.c low_gist.c mol_op.c
              rdkit_gist.c bfp_gist.c bfp_gin.c bitstring.c rdkit_io.c rxn_op.c sfp_op.c)
  target_link_libraries(${EXTENSION} ${PostgreSQL_LIBRARIES})
  if(WIN32)
    target_link_libraries(${EXTENSION} postgres)
  endif(WIN32)
endif(APPLE)
set(avalonRegress "")
set(inchiRegress "")
set(jsonRegress "")
set(pgRDKitLibSuffix "")
if(RDK_PGSQL_STATIC AND ((NOT MSVC) OR (MSVC AND RDK_INSTALL_DLLS_MSVC)))
  set(pgRDKitLibSuffix "_static")
endif()
set(pgRDKitLibList "")
if(RDK_BUILD_AVALON_SUPPORT)
  set(avalonRegress "avalon")
  set(pgRDKitLibList "${pgRDKitLibList}AvalonLib;avalon_clib;")
endif(RDK_BUILD_AVALON_SUPPORT)
if(RDK_BUILD_INCHI_SUPPORT)
  if(NOT DEFINED INCHI_LIBRARIES)
    set(CMAKE_MODULE_PATH
        ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/Modules/")
    if(NOT DEFINED RDKit_RELEASENAME)
      include(RDKitUtils)
    endif(NOT DEFINED RDKit_RELEASENAME)
    find_package(Inchi)
  endif(NOT DEFINED INCHI_LIBRARIES)
  set(inchiRegress "inchi")
  set(pgRDKitLibList "${pgRDKitLibList}RDInchiLib;${INCHI_LIBRARIES};")
endif(RDK_BUILD_INCHI_SUPPORT)
if(RDK_BUILD_MOLINTERCHANGE_SUPPORT)
  set(jsonRegress "json")
  set(pgRDKitLibList "${pgRDKitLibList}MolInterchange;")
endif(RDK_BUILD_MOLINTERCHANGE_SUPPORT)
set(pgRDKitLibList "${pgRDKitLibList}"
    "GeneralizedSubstruct;MolEnumerator;TautomerQuery;MolDraw2D;MolTransforms;MolHash;FMCS;ChemReactions;ChemTransforms;"
    "FileParsers;SmilesParse;Fingerprints;Subgraphs;Descriptors;"
    "PartialCharges;SubstructMatch;GraphMol;EigenSolvers;DataStructs;Depictor;"
    "RDGeometryLib;RDGeneral")
if(RDK_USE_URF)
  set(pgRDKitLibList "${pgRDKitLibList};${RDK_URF_LIBS}")
endif(RDK_USE_URF)
set (pgRDKitLibs "")
foreach(pgRDKitLib ${pgRDKitLibList})
  if (NOT pgRDKitLibs STREQUAL "")
    set(pgRDKitLibs "${pgRDKitLibs};")
  endif()
  set(pgRDKitLibs "${pgRDKitLibs}${pgRDKitLib}${pgRDKitLibSuffix}")
endforeach()
target_link_libraries(${EXTENSION}${EXTENSION_SUFFIX} ${pgRDKitLibs} rdkit_base)
if(RDK_BUILD_THREADSAFE_SSS)
  target_link_libraries(${EXTENSION}${EXTENSION_SUFFIX} ${RDKit_THREAD_LIBS})
endif(RDK_BUILD_THREADSAFE_SSS)

if(MSVC)
  set(PGREGRESS_BINARY "${PG_BINDIR}/pg_regress")
  string(REGEX REPLACE "/" ${REGEX_SEPARATOR} PGREGRESS_BINARY ${PGREGRESS_BINARY})
  string(REGEX REPLACE "/" ${REGEX_SEPARATOR} PG_EXTENSIONDIR ${PG_EXTENSIONDIR})
else()
  run_pg_config (--pgxs PG_MAKEFILE)
  get_filename_component(PG_MAKEFILESDIR ${PG_MAKEFILE} PATH)
  set(PGREGRESS_BINARY "${PG_MAKEFILESDIR}/../test/regress/pg_regress")
endif()
if(WIN32)
  set(PGREGRESS_BINARY "${PGREGRESS_BINARY}.exe")
endif()
if(NOT EXISTS ${PGREGRESS_BINARY})
  message(FATAL_ERROR "${PGREGRESS_BINARY} does not exist")
endif()

macro (run_pgregress arg var)
  execute_process(COMMAND ${PGREGRESS_BINARY} ${arg}
                  OUTPUT_VARIABLE ${var}
                  ERROR_VARIABLE ${var}
                  OUTPUT_STRIP_TRAILING_WHITESPACE
                  ERROR_STRIP_TRAILING_WHITESPACE)
endmacro ()
set(PGREGRESS_BINDIR_SWITCH "")
foreach(opt "bindir" "psqldir")
  run_pgregress("--${opt}" err)
  if (${err} MATCHES "option requires an argument")
    set(PGREGRESS_BINDIR_SWITCH ${opt})
    break()
  endif()
endforeach()
if (NOT ${PGREGRESS_BINDIR_SWITCH} STREQUAL "")
  set(PGREGRESS_BINDIR_SWITCH "--${PGREGRESS_BINDIR_SWITCH}=\"${PG_BINDIR}\"")
endif()

set(testPgSQLName "${CMAKE_CURRENT_BINARY_DIR}/pgsql_regress")
set(installPgSQLName "${CMAKE_CURRENT_BINARY_DIR}/pgsql_install")
set(PG_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/")
set(PG_CURRENT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/")
set(PG_RDKIT_LIB_SRC "${EXTENSION_PREFIX}${EXTENSION}${EXTENSION_DEST_SUFFIX}")
set(PG_RDKIT_LIB_DEST "${EXTENSION}${EXTENSION_DEST_SUFFIX}")
if(MSVC)
  set(testPgSQLCommand "")
  set(testPgSQLName "${testPgSQLName}.bat")
  set(installPgSQLName "${installPgSQLName}.bat")
  set(installPgSQLCopyCommand "copy /Y")
  set(installPgSQLSeparator "\\")
  set(BAT_QUOTE "\"")
  set(SH_QUOTE "")
  set(PG_CPACK_STAGING_DIR "")
  set(installPgSQLBody "")
  string(REGEX REPLACE "/" ${REGEX_SEPARATOR} PG_CURRENT_SOURCE_DIR ${PG_CURRENT_SOURCE_DIR})
  string(REGEX REPLACE "/" ${REGEX_SEPARATOR} PG_CURRENT_BINARY_DIR ${PG_CURRENT_BINARY_DIR})
  string(REGEX REPLACE "/" ${REGEX_SEPARATOR} PG_PKGLIBDIR ${PG_PKGLIBDIR})
  string(REGEX REPLACE "/" ${REGEX_SEPARATOR}${REGEX_SEPARATOR} installPgSQLNameBS ${installPgSQLName})
else()
  set(testPgSQLCommand "sh")
  set(testPgSQLName "${testPgSQLName}.sh")
  set(installPgSQLName "${installPgSQLName}.sh")
  set(installPgSQLCopyCommand "cp")
  set(installPgSQLSeparator "/")
  set(BAT_QUOTE "")
  set(SH_QUOTE "\"")
  set(PG_CPACK_STAGING_DIR "\$DESTDIR")
  set(installPgSQLBody
    "set -x\n"
    "test -n \"${PG_CPACK_STAGING_DIR}\" && \\\n"
    "  mkdir -p \"${PG_CPACK_STAGING_DIR}${PG_EXTENSIONDIR}\" && \\\n"
    "  mkdir -p \"${PG_CPACK_STAGING_DIR}${PG_PKGLIBDIR}\"\n"
  )
endif()
set(testPgSQLBody "cd \"${PG_CURRENT_BINARY_DIR}\"\n"
    "\"${PGREGRESS_BINARY}\" --inputdir=sql "
    "${PGREGRESS_BINDIR_SWITCH} rdkit-91 "
    "props btree molgist bfpgist-91 sfpgist slfpgist fps reaction fmcs query xqm"
    " ${avalonRegress} ${inchiRegress} ${jsonRegress}\n")
file(STRINGS ${PG_CURRENT_SOURCE_DIR}${EXTENSION}.control
    PG_EXTVERSION LIMIT_COUNT 1 REGEX default_version)
string(REPLACE " " ";" tokList ${PG_EXTVERSION})
list(GET tokList -1 PG_EXTVERSION)
string(REGEX REPLACE "'" "" PG_EXTVERSION ${PG_EXTVERSION})
if(MSVC)
  if(${CMAKE_GENERATOR} STREQUAL "NMake Makefiles")
    set(PG_RDKIT_LIB_SRC "${PG_RDKIT_LIB_SRC}")
  else(${CMAKE_GENERATOR} STREQUAL "NMake Makefiles")
    set(PG_RDKIT_LIB_SRC "${CMAKE_BUILD_TYPE}\\${PG_RDKIT_LIB_SRC}")
  endif(${CMAKE_GENERATOR} STREQUAL "NMake Makefiles")
endif(MSVC)

if(RDK_PGSQL_MOL_GIST_SORTSUPPORT AND ${PostgreSQL_VERSION_STRING} VERSION_GREATER_EQUAL 14.0)
  set(RDKIT_PG_MOL_GIST_SORTSUPPORT "FUNCTION    11   gmol_sortsupport (internal),")
else()
  set(RDKIT_PG_MOL_GIST_SORTSUPPORT "")
endif()

if(RDK_PGSQL_QMOL_GIST_SORTSUPPORT AND ${PostgreSQL_VERSION_STRING} VERSION_GREATER_EQUAL 14.0)
  set(RDKIT_PG_QMOL_GIST_SORTSUPPORT "FUNCTION    11   gmol_sortsupport (internal),")
else()
  set(RDKIT_PG_QMOL_GIST_SORTSUPPORT "")
endif()

if(RDK_PGSQL_BFP_GIST_SORTSUPPORT AND ${PostgreSQL_VERSION_STRING} VERSION_GREATER_EQUAL 14.0)
  set(RDKIT_PG_BFP_GIST_SORTSUPPORT "FUNCTION    11   gbfp_sortsupport (internal),")
else()
  set(RDKIT_PG_BFP_GIST_SORTSUPPORT "")
endif()

if (${PostgreSQL_VERSION_STRING} VERSION_GREATER_EQUAL 17.0)
  set(RDKIT_PG_ALTER_OPERATOR_ATEQ_MOL "ALTER OPERATOR @= (mol, mol) SET (
    COMMUTATOR = '@=',
    NEGATOR = '@<>'
);")
  set(RDKIT_PG_ALTER_OPERATOR_ATEQ_REACTION "ALTER OPERATOR @= (reaction, reaction) SET (
    COMMUTATOR = '@=',
    NEGATOR = '@<>'
);")
endif()

configure_file("${PG_CURRENT_SOURCE_DIR}${EXTENSION}.sql.in"
               "${PG_CURRENT_BINARY_DIR}${EXTENSION}--${PG_EXTVERSION}.sql")
file(GLOB files "update_sql/*.in")
foreach(file ${files})
  string(REPLACE ".in" "" output_name ${file})
  if(MSVC)
    string(REGEX REPLACE "/" ${REGEX_SEPARATOR} output_name ${output_name})
  endif()
  string(REPLACE "${PG_CURRENT_SOURCE_DIR}" "${PG_CURRENT_BINARY_DIR}" output_name ${output_name})
  configure_file(${file} ${output_name})
endforeach()
set(installPgSQLBody "${installPgSQLBody}"
  "${installPgSQLCopyCommand} \"${PG_CURRENT_BINARY_DIR}${EXTENSION}--${PG_EXTVERSION}.sql\" \"${PG_CPACK_STAGING_DIR}${PG_EXTENSIONDIR}\"\n"
  "${installPgSQLCopyCommand} \"${PG_CURRENT_SOURCE_DIR}${EXTENSION}.control\" \"${PG_CPACK_STAGING_DIR}${PG_EXTENSIONDIR}\"\n"
  "${installPgSQLCopyCommand} \"${PG_CURRENT_BINARY_DIR}update_sql${installPgSQLSeparator}${SH_QUOTE}*.sql${BAT_QUOTE} \"${PG_CPACK_STAGING_DIR}${PG_EXTENSIONDIR}\"\n"
  "${installPgSQLCopyCommand} \"${PG_CURRENT_BINARY_DIR}${PG_RDKIT_LIB_SRC}\" \"${PG_CPACK_STAGING_DIR}${PG_PKGLIBDIR}${PG_RDKIT_LIB_DEST}\"\n"
)
file(WRITE ${testPgSQLName} ${testPgSQLBody})
file(WRITE ${installPgSQLName} ${installPgSQLBody})
foreach(testDir sql data expected)
  add_custom_command(TARGET ${EXTENSION}${EXTENSION_SUFFIX} PRE_BUILD
      COMMAND "${CMAKE_COMMAND}" -E copy_directory
      "${PG_CURRENT_SOURCE_DIR}${testDir}" "${PG_CURRENT_BINARY_DIR}${testDir}"
      POST_BUILD)
endforeach()
configure_file(
  "${CMAKE_CURRENT_SOURCE_DIR}/InstallPgSql.cmake.in"
  "${CMAKE_CURRENT_BINARY_DIR}/InstallPgSql.cmake" @ONLY)
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/InstallPgSql.cmake" COMPONENT pgsql)
add_test(testPgSQL ${testPgSQLCommand} ${testPgSQLName})
