cmake_minimum_required(VERSION 3.5)

set(tile_VERSION_MAJOR 0)
set(tile_VERSION_MINOR 1)
set(tile_VERSION_PATCH 2)
set(tile_VERSION_BUILD $ENV{CI_STEP_NUMBER})
if(NOT tile_VERSION_BUILD)
  set(tile_VERSION_BUILD "0")
endif()
set(TILE_VERSION
    "${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}.${tile_VERSION_BUILD}"
)
project(
  tile
  VERSION "${tile_VERSION_MAJOR}.${tile_VERSION_MINOR}.${tile_VERSION_PATCH}"
  LANGUAGES C CXX)

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

set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# add static libgcc and libstdc++ for static linking set(CMAKE_C_FLAGS
# "${CMAKE_C_FLAGS} -static-libgcc -static-libstdc++") set(CMAKE_CXX_FLAGS
# "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")

option(TILE_BUILD_TESTS "Build tests" OFF)
option(TILE_BUILD_BENCHMARKS "Build tests" OFF)
option(TILE_WITH_OPENSSL "Build wih openssl" OFF)
option(TILE_BUILD_SHARED "Build shared library" ON)
option(TILE_WITH_MIMALLOC "Build with mimalloc" OFF)

if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
  if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
    set(TILE_BUILD_TESTS ON)
    set(TILE_BUILD_BENCHMARKS ON)
  endif()
endif()

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE "Release")
endif()

# if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") # set(CMAKE_CXX_FLAGS
# "${CMAKE_CXX_FLAGS} -gz") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") #
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS #
# "${CMAKE_CXX_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} #
# -fsanitize=address ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address #
# ")
#
# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load") # set(NO_WHOLE_ARCHIVE_PREFIX "")
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # set(CMAKE_CXX_FLAGS
# "${CMAKE_CXX_FLAGS} -gz") # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") #
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") set(CMAKE_C_FLAGS #
# "${CMAKE_CXX_FLAGS} -static")
#
# set(WHOLE_ARCHIVE_PREFIX "-Wl,-force_load,") # set(NO_WHOLE_ARCHIVE_PREFIX "")
# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS
# "${CMAKE_CXX_FLAGS} -gz") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -gz") #
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") #
# set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++")
#
# set(STATIC_BINARY_FLAGS "-static-libgcc -static-libstdc++")
# set(WHOLE_ARCHIVE_PREFIX "-Wl,--whole-archive") set(WHOLE_ARCHIVE_SUFFIX
# "-Wl,--no-whole-archive") endif()

# extern int getifaddrs(struct ifaddrs **ifap); extern void freeifaddrs(struct
# ifaddrs *ifa);
include(CheckSymbolExists)
include(cmake/BuildInfo.cmake)

check_symbol_exists("getifaddrs" "ifaddrs.h" TILE_HAVE_GETIFADDRS)
check_symbol_exists("freeifaddrs" "ifaddrs.h" TILE_HAVE_FREEIFADDRS)

# find_package(Git REQUIRED)
#
# get_git_commit_hash(GIT_COMMIT_HASH) get_git_commit_date(GIT_COMMIT_DATE)
# get_git_commit_subject(GIT_COMMIT_SUBJECT)

set(THIRD_PARTY_INCLUDE_DIRS # "third_party/json" "third_party/inja"
    "third_party/sigslot"
    "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
include_directories(${THIRD_PARTY_INCLUDE_DIRS})
add_subdirectory("third_party/zlib")
if(TILE_WITH_MIMALLOC)
  add_subdirectory("third_party/mimalloc")
endif()
add_subdirectory("third_party/jsoncpp")
add_subdirectory("third_party/fmt")
add_subdirectory("third_party/googletest")
add_subdirectory("third_party/gflags")
set(GFLAGS_USE_TARGET_NAMESPACE ON)
set(gflags_DIR "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags")
# add_subdirectory("third_party/context")

set(CURL_DISABLE_TESTS ON)
set(CURL_CA_PATH
    "none"
    CACHE STRING "" FORCE)
set(CURL_ENABLE_SSL
    OFF
    CACHE BOOL "" FORCE)
set(USE_LIBIDN2
    OFF
    CACHE BOOL "" FORCE)
set(CURL_USE_LIBPSL
    OFF
    CACHE BOOL "" FORCE)
set(CURL_USE_LIBSSH
    OFF
    CACHE BOOL "" FORCE)
set(CURL_USE_LIBSSH2
    OFF
    CACHE BOOL "" FORCE)
set(CURL_USE_GSSAPI
    OFF
    CACHE BOOL "" FORCE)
set(CURL_USE_RTMP
    OFF
    CACHE BOOL "" FORCE)
set(USE_OPENSSL_QUIC
    OFF
    CACHE BOOL "" FORCE)
set(USE_MSH3
    OFF
    CACHE BOOL "" FORCE)
set(CURL_DISABLE_LDAP
    ON
    CACHE BOOL "" FORCE)
set(ZLIB_FOUND ON)
set(ZLIB_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/zlib")
set(ZLIB_LIBRARIES zlib)
add_subdirectory("third_party/curl")
# add_subdirectory("third_party/date")
if(TILE_WITH_OPENSSL)
  add_subdirectory("third_party/openssl-cmake")
  list(APPEND TILE_LINK_LIBS ssl crypto)
endif()

configure_file("tile/base/config.h.in"
               "${CMAKE_CURRENT_BINARY_DIR}/tile/base/config.h" @ONLY)
# file(GLOB zlib_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/third_party/zlib/zlib/*.c")
set(TILE_SRCS
    "tile/base/buffer/builtin_buffer_block.cc"
    "tile/base/buffer/compression_output_stream.cc"
    "tile/base/buffer/polymorphic_buffer.cc"
    "tile/base/buffer.cc"
    "tile/base/chrono.cc"
    "tile/base/compression.cc"
    "tile/base/compression/compression.cc"
    "tile/base/compression/gzip.cc"
    "tile/base/compression/util.cc"
    "tile/base/demangle.cc"
    "tile/base/encoding/base64.cc"
    "tile/base/encoding/detail/hex_chars.cc"
    "tile/base/encoding/hex.cc"
    "tile/base/encoding/percent.cc"
    "tile/base/exposed_var.cc"
    "tile/base/internal/background_task_host.cc"
    "tile/base/internal/background_task_host.h"
    "tile/base/internal/case_insensitive_hash_map.h"
    "tile/base/internal/curl.cc"
    "tile/base/internal/curl.h"
    "tile/base/internal/early_init.h"
    "tile/base/internal/index_alloc.cc"
    "tile/base/internal/logging.cc"
    "tile/base/internal/logging.h"
    "tile/base/internal/thread_pool.cc"
    "tile/base/internal/time_keeper.cc"
    "tile/base/internal/time_keeper.h"
    "tile/base/logging/basic_file_sink.cc"
    "tile/base/logging/splitter_sink.cc"
    "tile/base/logging/console_sink.cc"
    "tile/base/net/endpoint.cc"
    "tile/base/object_pool/disabled.cc"
    "tile/base/object_pool/global.cc"
    "tile/base/object_pool/thread_local.cc"
    "tile/base/option.cc"
    "tile/base/option/gflags_provider.cc"
    "tile/base/option/json_parser.cc"
    "tile/base/option/key.cc"
    "tile/base/option/option_provider.cc"
    "tile/base/option/option_service.cc"
    "tile/base/slice.cc"
    "tile/base/status.cc"
    "tile/base/string.cc"
    "tile/base/thread/cond_var.cc"
    "tile/base/thread/latch.cc"
    "tile/base/thread/mutex.cc"
    "tile/base/thread/rw_mutex.cc"
    "tile/base/thread/scoped_lock.cc"
    "tile/base/thread/spinlock.cc"
    "tile/base/memory_barrier.cc"
    # "tile/fiber/detail/fiber.cc"
    "tile/io/detail/eintr_safe.cc"
    "tile/io/native/acceptor.cc"
    "tile/io/descriptor.cc"
    "tile/io/util/rate_limiter.cc"
    "tile/io/event_loop.cc"
    "tile/init.cc"
    "tile/init/on_init.cc"
    "tile/init/override_flag.cc"
    "tile/net/http/http_headers.cc"
    "tile/net/http/http_message.cc"
    "tile/net/http/http_request.cc"
    "tile/net/http/http_response.cc"
    "tile/net/http/http_client.cc"
    "tile/net/http/types.cc"
    "tile/net/internal/http_task.cc"
    "tile/net/internal/http_engine.cc"
    "tile/testing/internal/random_string.cc"
    "tile/rpc/protocol/http/buffer_io.cc"
    "tile/rpc/protocol/message.cc"
    # "tile/rpc/server.cc"
    "tile/base/config/configuration.cc"
    "tile/base/config/ini_file_configuration.cc"
    "tile/base/config/json_configuration.cc"
    "tile/base/config/layered_configuration.cc"
    "tile/base/config/toml_configuration.cc"
    "tile/system/os.cc")

if((NOT TILE_HAVE_GETIFADDRS) OR (NOT TILE_HAVE_FREEIFADDRS))
  list(APPEND TILE_SRCS "tile/base/net/detail/android/ifaddrs.c")
endif()

if(TILE_BUILD_SHARED)
  add_library(tile SHARED ${TILE_SRCS})
else()
  add_library(tile STATIC ${TILE_SRCS})
endif()
set_target_properties(tile PROPERTIES VERSION ${PROJECT_VERSION}
                                      SOVERSION ${PROJECT_VERSION_MAJOR})

target_precompile_headers(
  tile
  PUBLIC
  "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/third_party/header_only/toml.hpp>"
  # ${CMAKE_CURRENT_SOURCE_DIR}/third_party/json/nlohann/json.hpp
  # ${CMAKE_CURRENT_SOURCE_DIR}/third_party/inja/inja/string_view.h
  # ${CMAKE_CURRENT_SOURCE_DIR}/third_party/inja/inja/inja.h
)
# target_sources(tile PRIVATE ${TILE_SRCS})
target_include_directories(
  tile
  PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/third_party/mustache.hpp"
         "${CMAKE_CURRENT_BINARY_DIR}"
         "${CMAKE_CURRENT_SOURCE_DIR}/third_party/fmt/include"
         # "${CMAKE_CURRENT_BINARY_DIR}/third_party/glog"
         # "${CMAKE_CURRENT_SOURCE_DIR}/third_party/glog/src"
         "${CMAKE_CURRENT_SOURCE_DIR}"
         ${THIRD_PARTY_INCLUDE_DIRS}
         RPIVATE
         "${CMAKE_CURRENT_SOURCE_DIR}/third_party/header_only/"
         "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
if(TILE_WITH_MIMALLOC)
  target_link_libraries(tile PUBLIC mimalloc-obj)
endif()

target_link_libraries(
  tile
  PUBLIC # -Wl,--start-group nova_context
         zlib
         gflags::gflags # glog::glog
         jsoncpp_static
         # -Wl,--end-group
         libcurl
         fmt)
if((CMAKE_SYSTEM_PROCESSOR MATCHES "riscv64") OR (CMAKE_SYSTEM_PROCESSOR MATCHES
                                                  "mips*"))
  if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    target_link_libraries(tile PUBLIC atomic)
  endif()
endif()

# set(LIB_NAMES tile)
#
# if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) OR
# ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") OR ("${CMAKE_SYSTEM_NAME}"
# STREQUAL "GNU") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD") OR
# ("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") OR ("${CMAKE_SYSTEM_NAME}"
# STREQUAL "DragonFly") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")) # FIXME:
# It should be "GNU ld # for elf" set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES}
# -Wl,--no-whole-archive) elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
# set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) endif()

add_library(tile::tile ALIAS tile)
# add_library(tile SHARED $<TARGET_OBJECTS:tile>) target_include_directories(
# tile PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/third_party/fmt/include"
# "${CMAKE_CURRENT_BINARY_DIR}/third_party/glog"
# "${CMAKE_CURRENT_SOURCE_DIR}/third_party/glog/src"
# "${CMAKE_CURRENT_SOURCE_DIR}" RPIVATE
# "${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include")
# target_link_libraries( tile PRIVATE ${WHOLE_ARCHIVE_PREFIX} gflags::gflags
# glog::glog libcurl fmt::fmt ${NO_WHOLE_ARCHIVE_PREFIX})

if(TILE_BUILD_TESTS)
  enable_testing()

  add_executable(tile_test_all "tile/testing/main.cc")
  target_link_libraries(tile_test_all PRIVATE gtest gmock tile::tile)

  macro(tile_add_custom_test test_name test_file)
    add_executable(${test_name} ${test_file} "tile/testing/main.cc")
    target_link_libraries(
      ${test_name} PUBLIC gtest gmock ${WHOLE_ARCHIVE_PREFIX} tile::tile
                          ${WHOLE_ARCHIVE_SUFFIX})

    target_sources(${PROJECT_NAME}_test_all PRIVATE ${test_file})
  endmacro()

  macro(tile_add_test test_name test_file)
    tile_add_custom_test(${test_name} ${test_file})
    add_test(NAME ${test_name} COMMAND ${test_name})
  endmacro()

  function(add_test_group prefix group_name)
    file(GLOB_RECURSE TEST_FILES "${prefix}/**/*_test.cc" "${prefix}/*_test.cc")
    foreach(SRC_FILE ${TEST_FILES})
      if(IS_DIRECTORY ${SRC_FILE})
        continue()
      endif()
      # convert to relative path message(STATUS "${prefix} -> ${TEST_FILE}")
      file(RELATIVE_PATH TEST_NAME "${prefix}" "${SRC_FILE}")
      string(REPLACE "/" "_" TEST_NAME "${TEST_NAME}")
      string(REPLACE "_test.cc" "_test" TEST_NAME "${TEST_NAME}")
      # if group_name is not empty, add suffix _
      if(NOT group_name STREQUAL "")
        set(TEST_NAME "${group_name}_${TEST_NAME}")
      endif()

      tile_add_test(${TEST_NAME} ${SRC_FILE})
    endforeach()
  endfunction(add_test_group)

  add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/system system)
  add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/base base)
  add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/init init)
  add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/io io)
  add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/net net)
  add_test_group(${CMAKE_CURRENT_SOURCE_DIR}/tile/rpc rpc)
  tile_add_custom_test("custom_http_client_test" "tests/http_client_test.cc")
endif(TILE_BUILD_TESTS)

if(TILE_BUILD_BENCHMARKS)
  add_subdirectory("third_party/benchmark")

  add_executable(tile_bm_all "tile/testing/bm_main.cc")
  target_link_libraries(tile_bm_all PRIVATE benchmark::benchmark tile::tile)

  macro(tile_add_bm benchmark_name benchmark_file)
    add_executable(${benchmark_name} ${benchmark_file}
                                     "tile/testing/bm_main.cc")
    target_link_libraries(${benchmark_name} PRIVATE benchmark::benchmark
                                                    tile::tile)

    target_sources(tile_bm_all PRIVATE ${benchmark_file})
  endmacro()

  # tile_add_bm(fiber_detail_fiber_benchmark
  # "tile/fiber/detail/fiber_benchmark.cc")
  tile_add_bm(base_buffer_benchmark "tile/base/buffer_benchmark.cc")
  tile_add_bm(base_casting_benchmark "tile/base/casting_benchmark.cc")
  tile_add_bm(base_thread_mutex_benchmark "tile/base/thread/mutex_benchmark.cc")
  tile_add_bm(base_encoding_benchmark "tile/base/encoding_benchmark.cc")
  tile_add_bm(base_internal_time_keeper_benchmark
              "tile/base/internal/time_keeper_benchmark.cc")
  tile_add_bm(base_chrono_benchmark "tile/base/chrono_benchmark.cc")
endif(TILE_BUILD_BENCHMARKS)
