# CMakeLists.txt for mbpoll - ModBus Master Simulator
# Originally generated by CodeLite IDE, modernized for CMake 3.13+

cmake_minimum_required(VERSION 3.13)

project(mbpoll
  VERSION 1.5.3
  DESCRIPTION "ModBus Master Simulator"
  LANGUAGES C)

# Standard install directories (man, bin, lib, etc.)
include(GNUInstallDirs)

# Export compile commands for IDE support
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Set default build type if not specified
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  message(STATUS "Setting build type to 'Release' as none was specified.")
  set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" "MinSizeRel")
endif()

# Set output directories
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# Option for custom RTS GPIO support
set(MBPOLL_CUSTOM_RTS_GPIO ON CACHE BOOL "Enable custom RTS GPIO (if libpiduino found, only on ARM)")

# Check for system getopt (use bundled version on Windows)
include(CheckSymbolExists)
set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
check_symbol_exists(getopt "getopt.h" HAVE_GETOPT)
if(NOT HAVE_GETOPT)
  set(GETOPT_SOURCES ${CMAKE_SOURCE_DIR}/3rdparty/getopt/getopt.c)
  set(GETOPT_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/3rdparty/getopt)
  # Disable pedantic warnings for 3rdparty getopt (uses deprecated 'register' keyword)
  if(NOT MSVC)
    set_source_files_properties(${GETOPT_SOURCES} PROPERTIES COMPILE_FLAGS "-Wno-pedantic")
  endif()
endif()

# Append cmake/ to module path
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# Find dependencies
if(WIN32)
  # On Windows, build libmodbus from bundled sources (no pkg-config needed)
  set(LIBMODBUS_INCLUDE_DIRS
    ${CMAKE_SOURCE_DIR}/libmodbus/src
    ${CMAKE_SOURCE_DIR}/libmodbus/src/win32)
  file(GLOB_RECURSE LIBMODBUS_SRCS RELATIVE ${CMAKE_SOURCE_DIR} "libmodbus/src/*.c")
  find_program(CSCRIPT_PATH NAMES cscript REQUIRED)
else()
  # On Unix, use system libmodbus via pkg-config
  # Note: 3.1.7+ required for quirks support (-Q/-X options)
  find_package(PkgConfig REQUIRED)
  pkg_check_modules(LIBMODBUS REQUIRED libmodbus>=3.1.7)
endif()

# Search for piduino (optional, for GPIO-based RS-485 RTS control)
find_package(PIDUINO QUIET)
if(PIDUINO_FOUND AND PIDUINO_WITH_GPIO AND MBPOLL_CUSTOM_RTS_GPIO)
  message(STATUS "PiDuino ${PIDUINO_VERSION} found - enabling GPIO RTS support")
  set(MBPOLL_GPIO_RTS ON)
  set(PROGRAM_PERMISSIONS
    OWNER_WRITE OWNER_READ OWNER_EXECUTE
    GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE
    SETUID)
else()
  if(MBPOLL_CUSTOM_RTS_GPIO AND NOT PIDUINO_FOUND)
    message(STATUS "PiDuino not found - GPIO RTS support disabled")
  endif()
  set(MBPOLL_GPIO_RTS OFF)
  set(PROGRAM_PERMISSIONS
    OWNER_WRITE OWNER_READ OWNER_EXECUTE
    GROUP_READ GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE)
endif()

# Get version from git tags
include(GitVersion)
GetGitVersion(MBPOLL_VERSION)
WriteGitVersionFile(${CMAKE_CURRENT_BINARY_DIR}/version-git.h)
set(MBPOLL_VERSION_STRING
  ${MBPOLL_VERSION_MAJOR}.${MBPOLL_VERSION_MINOR}.${MBPOLL_VERSION_PATCH})

# Define source files
set(MBPOLL_SOURCES
  ${CMAKE_SOURCE_DIR}/src/mbpoll.c
  ${CMAKE_SOURCE_DIR}/src/custom-rts.c
  ${CMAKE_SOURCE_DIR}/src/serial.c
  ${CMAKE_SOURCE_DIR}/src/chipio.c
  ${CMAKE_SOURCE_DIR}/src/utils.c
  ${LIBMODBUS_SRCS}
  ${GETOPT_SOURCES})

# Windows resource file
if(WIN32)
  enable_language(RC)
  set(CMAKE_RC_COMPILE_OBJECT
    "<CMAKE_RC_COMPILER> ${RC_OPTIONS} -O coff -i <SOURCE> -o <OBJECT>")
  list(APPEND MBPOLL_SOURCES ${CMAKE_SOURCE_DIR}/libmodbus/src/win32/modbus.rc)
endif()

# Create the executable
add_executable(mbpoll ${MBPOLL_SOURCES})

# Include directories
target_include_directories(mbpoll PRIVATE
  ${CMAKE_SOURCE_DIR}
  ${CMAKE_SOURCE_DIR}/src
  ${CMAKE_CURRENT_BINARY_DIR}
  ${LIBMODBUS_INCLUDE_DIRS}
  ${GETOPT_INCLUDE_DIR})

# Compiler-specific options
if(MSVC)
  target_compile_definitions(mbpoll PRIVATE _CRT_SECURE_NO_WARNINGS)
else()
  # Common warning flags
  target_compile_options(mbpoll PRIVATE
    -Wall
    -Wextra
    -Wpedantic
    -Wformat-security
    -Wno-unused-parameter
    -Wno-unused-function
    -Wno-unused-const-variable)

  # Security hardening (Release builds)
  target_compile_options(mbpoll PRIVATE
    $<$<CONFIG:Release>:-fstack-protector-strong>
    $<$<CONFIG:RelWithDebInfo>:-fstack-protector-strong>)

  target_compile_definitions(mbpoll PRIVATE
    $<$<CONFIG:Release>:_FORTIFY_SOURCE=2>
    $<$<CONFIG:Release>:NDEBUG>
    $<$<CONFIG:RelWithDebInfo>:_FORTIFY_SOURCE=2>)

  # Debug build with Address Sanitizer support
  target_compile_options(mbpoll PRIVATE
    $<$<CONFIG:Debug>:-g>
    $<$<CONFIG:Debug>:-O0>
    $<$<CONFIG:Debug>:-fno-omit-frame-pointer>)

  # Optional: Enable sanitizers in debug mode (uncomment to use)
  # target_compile_options(mbpoll PRIVATE $<$<CONFIG:Debug>:-fsanitize=address,undefined>)
  # target_link_options(mbpoll PRIVATE $<$<CONFIG:Debug>:-fsanitize=address,undefined>)
endif()

# Compile definitions
target_compile_definitions(mbpoll PRIVATE _GNU_SOURCE)
target_compile_definitions(mbpoll PRIVATE SKIP_FAILURE_EXIT)

if(MBPOLL_GPIO_RTS)
  target_compile_definitions(mbpoll PRIVATE MBPOLL_GPIO_RTS)
  target_include_directories(mbpoll PRIVATE ${PIDUINO_INCLUDE_DIRS})
endif()

# Link libraries
if(WIN32)
  target_link_libraries(mbpoll PRIVATE wsock32 ws2_32)
else()
  target_link_libraries(mbpoll PRIVATE ${LIBMODBUS_LIBRARIES})
  target_link_directories(mbpoll PRIVATE ${LIBMODBUS_LIBRARY_DIRS})
  target_compile_options(mbpoll PRIVATE ${LIBMODBUS_CFLAGS_OTHER})

  if(MBPOLL_GPIO_RTS)
    target_link_libraries(mbpoll PRIVATE ${PIDUINO_LIBRARIES})
    target_link_directories(mbpoll PRIVATE ${PIDUINO_LIBRARY_DIRS})
  endif()
endif()

# Windows pre-build step for libmodbus configuration
if(WIN32)
  add_custom_command(
    TARGET mbpoll PRE_BUILD
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/libmodbus/src/win32
    COMMAND ${CSCRIPT_PATH} ${CMAKE_SOURCE_DIR}/libmodbus/src/win32/configure.js
    VERBATIM)
endif()

# Install target
install(TARGETS mbpoll
  RUNTIME DESTINATION bin
  PERMISSIONS ${PROGRAM_PERMISSIONS})

# Install man page (Unix only)
if(UNIX)
  install(FILES ${CMAKE_SOURCE_DIR}/doc/mbpoll.1
    DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif()

# Uninstall target
configure_file(
  "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
  "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
  IMMEDIATE @ONLY)
add_custom_target(uninstall
  COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)

# Testing support
option(BUILD_TESTING "Build unit tests" ON)
if(BUILD_TESTING)
  enable_testing()
  add_subdirectory(tests)
endif()

# CPack configuration for Debian packages
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION "${MBPOLL_VERSION_MAJOR}.${MBPOLL_VERSION_MINOR}.${MBPOLL_VERSION_PATCH}")
set(CPACK_PACKAGE_VERSION_MAJOR "${MBPOLL_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${MBPOLL_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${MBPOLL_VERSION_PATCH}")
set(CPACK_PACKAGE_CONTACT "Pascal JEAN <pascal.jean@piduino.org>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME} - ${PROJECT_DESCRIPTION}")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/package_description.txt")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/COPYING")
set(CPACK_GENERATOR "DEB")

if(CPACK_GENERATOR STREQUAL "DEB")
  set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/epsilonrt/mbpoll")

  find_program(dpkg_cmd NAMES dpkg)
  if(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE AND dpkg_cmd)
    execute_process(
      COMMAND ${dpkg_cmd} --print-architecture
      RESULT_VARIABLE ret
      OUTPUT_VARIABLE arch
      OUTPUT_STRIP_TRAILING_WHITESPACE)
    if(ret EQUAL 0)
      set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${arch}")
    else()
      message(WARNING "Unable to get debian architecture!")
    endif()
  endif()

  message(STATUS "Debian architecture: ${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")

  set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${CPACK_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
  message(STATUS "Debian package name: ${CPACK_PACKAGE_FILE_NAME}.deb")

  if(MBPOLL_GPIO_RTS)
    set(CPACK_DEBIAN_PACKAGE_DEPENDS "libmodbus5 (>= 3.1.7), libpiduino (>= ${PIDUINO_VERSION})")
  else()
    set(CPACK_DEBIAN_PACKAGE_DEPENDS "libmodbus5 (>= 3.1.7)")
  endif()
  set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
endif()

include(CPack)

# Print configuration summary
message(STATUS "")
message(STATUS "mbpoll configuration summary:")
message(STATUS "  Version: ${MBPOLL_VERSION_STRING}")
message(STATUS "  Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "  GPIO RTS support: ${MBPOLL_GPIO_RTS}")
message(STATUS "  Install prefix: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "")
