# Copyright (C) 2020-2021  CZ.NIC, z. s. p. o.
#
# This file is part of FRED.
#
# FRED is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# FRED is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with FRED.  If not, see <https://www.gnu.org/licenses/>.

cmake_minimum_required(VERSION 3.5)

set(LIBPG_VERSION "2.0.0")

set(PACKAGE_NAME      "libpg")
set(PACKAGE_VERSION   ${LIBPG_VERSION})
set(PACKAGE_STRING    "${PACKAGE_NAME} ${PACKAGE_VERSION}")
string(TOLOWER "${PACKAGE_NAME}-${PACKAGE_VERSION}" PACKAGE_TARNAME)
set(PACKAGE_BUGREPORT "https://gitlab.office.nic.cz/fred/libpg/")

# Options
option(LibPg_BUILD_TESTS "Build tests" OFF)

project(LibPg
    VERSION ${LIBPG_VERSION}
    LANGUAGES CXX)

message(STATUS "${PROJECT_NAME} version: ${PROJECT_VERSION}")

get_directory_property(HAS_PARENT PARENT_DIRECTORY)
if(HAS_PARENT)
    set(LIBPG_VERSION ${LIBPG_VERSION} PARENT_SCOPE)
endif()

##############################################
# Declare dependencies
set(PostgreSQL_ADDITIONAL_VERSIONS "9.6")
find_package(PostgreSQL ${PostgreSQL_ADDITIONAL_VERSIONS} REQUIRED)
find_package(Boost 1.53.0
    COMPONENTS
        system
        date_time
    REQUIRED)
find_package(PkgConfig REQUIRED)

if(NOT TARBALL_FILE_NAME)
    set(TARBALL_FILE_NAME ${PACKAGE_TARNAME})
endif()

if(NOT LIBSTRONG_DIR)
    set(LIBSTRONG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/lib/libstrong)
    if(NOT EXISTS ${LIBSTRONG_DIR}/CMakeLists.txt)
        message(FATAL_ERROR "no ${LIBSTRONG_DIR}/CMakeLists.txt file found; use -DLIBSTRONG_DIR=<path> "
                            "to specify a LibStrong location")
    endif()
else()
    if(NOT IS_ABSOLUTE ${LIBSTRONG_DIR})
        set(LIBSTRONG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${LIBSTRONG_DIR})
    endif()
    if(NOT EXISTS ${LIBSTRONG_DIR}/CMakeLists.txt)
        message(FATAL_ERROR "no ${LIBSTRONG_DIR}/CMakeLists.txt file found; LIBSTRONG_DIR "
                            "does not refer a LibStrong location")
    endif()
endif()
if(NOT TARGET LibStrong::library)
    add_subdirectory(${LIBSTRONG_DIR} ${CMAKE_BINARY_DIR}/external/lib/libstrong EXCLUDE_FROM_ALL)
endif()

##############################################
# Create target and set properties
add_library(pg STATIC
    src/extendable_query_arguments.cc
    src/libpq_layer.cc
    src/pg_connection.cc
    src/pg_result.cc
    src/pg_ro_transaction.cc
    src/pg_rw_transaction.cc
    src/pg_transaction.cc
    src/prepared_transaction.cc
    src/psql_type.cc
    src/query.cc
    src/query_parameter.cc
    src/time_zone.cc
    src/detail/libpq_layer_interface.cc
    src/detail/libpq_layer_impl.cc
    src/detail/unwrapper.cc
    src/detail/wrapper.cc
    src/sql_state/show_code.cc)

#Add an alias so that library can be used inside the build tree, e.g. when testing
add_library(LibPg::library ALIAS pg)

#Set target properties
target_include_directories(pg
    PUBLIC
        $<INSTALL_INTERFACE:include>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        ${PostgreSQL_INCLUDE_DIRS}
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}
)

target_compile_features(pg PRIVATE cxx_decltype_auto)
target_compile_options(pg PRIVATE $<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -ggdb -grecord-gcc-switches>)
set_target_properties(pg PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

target_link_libraries(pg
    PUBLIC
        Boost::system
        Boost::date_time
        LibStrong::library
    PRIVATE
        ${PostgreSQL_LIBRARIES}
)

if(LibPg_BUILD_TESTS)
    add_subdirectory(test)
endif()

find_program(GIT_PROGRAM git)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git AND GIT_PROGRAM AND TARBALL_FILE_NAME)
    add_custom_target(dist_libpg
        COMMAND ${GIT_PROGRAM} archive --format=tar --prefix=${TARBALL_FILE_NAME}/external/lib/${PACKAGE_NAME}/ -o ${CMAKE_BINARY_DIR}/.${PACKAGE_NAME}.tar HEAD
        COMMAND [ ! -f ${CMAKE_BINARY_DIR}/.libstrong.tar ] || (tar --concatenate --file=${CMAKE_BINARY_DIR}/.${PACKAGE_NAME}.tar ${CMAKE_BINARY_DIR}/.libstrong.tar && rm ${CMAKE_BINARY_DIR}/.libstrong.tar)
        DEPENDS dist_libstrong
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
get_directory_property(LIBPG_HAS_PARENT PARENT_DIRECTORY)
if("${LIBPG_HAS_PARENT}" STREQUAL "")
    include(GNUInstallDirs)

    install(TARGETS pg
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
    )
    add_custom_target(uninstall
        COMMAND xargs -L10 rm -v < ${CMAKE_BINARY_DIR}/install_manifest.txt
        COMMAND cat ${CMAKE_BINARY_DIR}/install_manifest.txt | xargs -L1 dirname | sort | uniq | xargs -L10 rmdir -p --ignore-fail-on-non-empty)

    if(EXISTS ${CMAKE_SOURCE_DIR}/.git AND GIT_PROGRAM)
        if(NOT TARGET dist)
            add_custom_target(make_dist_tarball
                COMMAND ${GIT_PROGRAM} archive --format=tar --prefix=${TARBALL_FILE_NAME}/ -o ${CMAKE_BINARY_DIR}/.${TARBALL_FILE_NAME}.tar HEAD
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
            add_custom_target(add_dist_libstrong
                COMMAND tar --concatenate --file=${CMAKE_BINARY_DIR}/.${TARBALL_FILE_NAME}.tar ${CMAKE_BINARY_DIR}/.libstrong.tar
                COMMAND rm ${CMAKE_BINARY_DIR}/.libstrong.tar
                DEPENDS make_dist_tarball dist_libstrong
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
            add_custom_target(dist
                COMMAND cat ${CMAKE_BINARY_DIR}/.${TARBALL_FILE_NAME}.tar | gzip > ${CMAKE_BINARY_DIR}/${TARBALL_FILE_NAME}.tar.gz && rm ${CMAKE_BINARY_DIR}/.${TARBALL_FILE_NAME}.tar
                DEPENDS add_dist_libstrong
                WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
        endif()

        if(DEFINED ENV{DISTCHECK_CMAKE_FLAGS})
            set(distcheck_flags $ENV{DISTCHECK_CMAKE_FLAGS})
        endif()

        if(NOT TARGET distcheck)
            set(DISTCHECK_BUILD_DIR "_build")
            set(DISTCHECK_INSTALL_DIR "_inst")
            if(${CMAKE_GENERATOR} STREQUAL "Ninja")
                set(DISTCHECK_BUILD_CMD "ninja")
            else()
                set(DISTCHECK_BUILD_CMD "$(MAKE)")
            endif()
            add_custom_target(clean_before_distcheck
                COMMAND rm -rf ${DISTCHECK_BUILD_DIR} ${DISTCHECK_INSTALL_DIR} ${TARBALL_FILE_NAME}
                COMMENT "cleaning before distcheck start")
            add_custom_target(distcheck_make_dirs
                COMMAND mkdir ${DISTCHECK_BUILD_DIR} ${DISTCHECK_INSTALL_DIR}
                COMMENT "make distcheck build and install directories"
                DEPENDS clean_before_distcheck)
            add_custom_target(distcheck_unpack_archive
                COMMAND tar xzf ${TARBALL_FILE_NAME}.tar.gz
                COMMENT "Checking the distribution archive..."
                DEPENDS dist distcheck_make_dirs)
            add_custom_target(distcheck_configure
                COMMAND ${CMAKE_COMMAND} -G${CMAKE_GENERATOR} ${distcheck_flags} -DCMAKE_INSTALL_PREFIX=../${DISTCHECK_INSTALL_DIR} ../${TARBALL_FILE_NAME}
                COMMENT "configure distcheck"
                DEPENDS distcheck_unpack_archive
                WORKING_DIRECTORY ${DISTCHECK_BUILD_DIR})
            add_custom_target(distcheck_make
                COMMAND ${DISTCHECK_BUILD_CMD}
                COMMENT "make"
                DEPENDS distcheck_configure
                WORKING_DIRECTORY ${DISTCHECK_BUILD_DIR})
            add_custom_target(distcheck_install
                COMMAND ${DISTCHECK_BUILD_CMD} install
                COMMENT "make install"
                DEPENDS distcheck_make
                WORKING_DIRECTORY ${DISTCHECK_BUILD_DIR})
            add_custom_target(distcheck_uninstall
                COMMAND ${DISTCHECK_BUILD_CMD} uninstall
                COMMENT "make uninstall"
                DEPENDS distcheck_install
                WORKING_DIRECTORY ${DISTCHECK_BUILD_DIR})
            add_custom_target(distcheck_clean
                COMMAND ${DISTCHECK_BUILD_CMD} clean
                COMMENT "make clean"
                DEPENDS distcheck_uninstall
                WORKING_DIRECTORY ${DISTCHECK_BUILD_DIR})
            add_custom_target(distcheck
                COMMAND rm -rf ${DISTCHECK_BUILD_DIR} ${DISTCHECK_INSTALL_DIR} ${TARBALL_FILE_NAME}
                COMMENT "PASS: '${TARBALL_FILE_NAME}.tar.gz' is ready for distribution."
                DEPENDS distcheck_clean)
        endif()
    else()
        message(STATUS "no git -- not including dist and distcheck targets")
    endif()
endif()
