cmake_minimum_required (VERSION 3.23)

#--- v1.0.0 ---
#------------------------------------------------
#--- Setup compiler settings ---
#------------------------------------------------
# Set C language standard
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)                 # Only standard features, no compiler-specific extensions

# Set C++ language standard
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)               # Only standard features, no compiler-specific extensions

# Options
set(APP_WIN32 OFF CACHE BOOL "Build app as a Windows GUI application" FORCE)

#------------------------------------------------
#--- Project configuration --- 
#------------------------------------------------
# Define the project settings
project("volaTile")

# Source directory
set(SRC_DIR 
    "src"
    "quokka_gfx"
)

# Resource files (add resource files here if needed)
set(RESOURCE_FILES "")

#------------------------------------------------
#--- Sanity checks ---
#------------------------------------------------
# Ensure build type is set (Debug, Release, etc.)
if(NOT CMAKE_BUILD_TYPE)
  message(FATAL_ERROR "CMAKE_BUILD_TYPE must be set")
endif()

# Ensure DEV_LIB environment variable is defined (used for external libraries)
if(NOT DEFINED ENV{DEV_LIB})
  message(FATAL_ERROR "DEV_LIB environment variable must be defined")
endif() 
set(DEV_LIB $ENV{DEV_LIB})


#------------------------------------------------
#--- Include directories ---
#------------------------------------------------
# General include directories (add your common include paths here)
set(GENERAL_INCLUDE_DIRS
    "src"
    "quokka_gfx"
    "sdi_toolBox_2.x.x/toolBox"
    "${DEV_LIB}/SDL3-3.4.10/include"
    "${DEV_LIB}/boost_1_87_0"
    "${DEV_LIB}/libremidi-5.4.3/include"
    "${DEV_LIB}/choc_1.0.1"
)

# Additional include directories for Debug configuration
set(DEBUG_INCLUDE_DIRS
    # "path/to/debug/include"
)

# Additional include directories for Release configuration
set(RELEASE_INCLUDE_DIRS
    # "path/to/debug/include"
)


#------------------------------------------------
#--- Library directories ---
#------------------------------------------------
# General library directories (add your common library paths here)
set(GENERAL_LIBRARY_DIRS
    ${CMAKE_BINARY_DIR}
    "${DEV_LIB}/boost_1_87_0/stage/lib"
    "${DEV_LIB}/SDL3-3.4.10/lib"
    "${DEV_LIB}/libremidi-5.4.3/lib"
)

# Additional library directories for Debug configuration
set(DEBUG_LIBRARY_DIRS
    # "path/to/debug/lib"
)

# Additional library directories for Release configuration
set(RELEASE_LIBRARY_DIRS
    # "path/to/debug/lib"
)

# Platform libraries required by libremidi on Windows
if (WIN32 AND MSVC)
  set(PLATFORM_LIBS
    winmm               # midiIn*/midiOut* API
    windowsapp          # C++/WinRT runtime (RoGetActivationFactory, etc.)
    ole32               # COM runtime (CoCreateInstance, etc.)
    oleaut32            # Automation (IDispatch, VARIANT, etc.)
  )
else()
  set(PLATFORM_LIBS "")
endif()


#------------------------------------------------
#--- Preprocessor definitions ---
#------------------------------------------------
# General preprocessor definitions (add your common defines here)
set(GENERAL_PREPROCESSOR_DEFINITIONS
    "_CRT_SECURE_NO_DEPRECATE"
    "_CRT_NONSTDC_NO_DEPRECATE"
    "_UNICODE"
    "_WINDOWS"
    "NOMINMAX"
    "UNICODE"
    "WIN32"

    "WIN32_LEAN_AND_MEAN"               # Exclude Windows shitty headers

    "SDL_STATIC_LIB"                    # Define for static linking of SDL3
)

# Additional preprocessor definitions for Debug configuration
set(DEBUG_PREPROCESSOR_DEFINITIONS
    "_DEBUG"
    "DEBUG"
)

# Additional preprocessor definitions for Release configuration
set(RELEASE_PREPROCESSOR_DEFINITIONS
    "NDEBUG"
)


#------------------------------------------------
#--- Libraries and DLLs ---
#------------------------------------------------
# General libraries to link (add your common libraries here)
set(GENERAL_LIB
    "winmm"                             # Required for Windows multimedia functions
    "imm32"                             # Required for Windows Input Method Manager functions
    "setupapi.lib"                      # Required for Windows setup API functions
    "cfgmgr32.lib"                      # Required for Windows configuration manager functions
    "version.lib"                       # Required for Windows version functions
    
    "SDL3-static.lib"                   # Required for SDL3 static linking
)

# Additional libraries for Debug configuration
set(DEBUG_LIB
    "libremidid.lib"
)

# Additional libraries for Release configuration
set(RELEASE_LIB
    "libremidi.lib"
)

# General DLLs to copy after build (add your common DLLs here)
set(GENERAL_BIN
#    "${DEV_LIB}/SDL2/SDL2-2.30.4/lib/x64/SDL2.dll"
)

# Additional DLLs for Debug configuration
set(DEBUG_BIN
    # "path/to/debug/dll"
)

# Additional DLLs for Release configuration
set(RELEASE_BIN
    # "path/to/release/dll"
)


#------------------------------------------------
#--- Set compiler flags based on build type ---
#------------------------------------------------
set(APP_WIN32 ON CACHE BOOL "Build app as a Windows GUI application" FORCE)

set(GENERAL_C_FLAGS
    /MP                 # Enable multi-processor compilation
)
set(GENERAL_CXX_FLAGS
    /MP                 # Enable multi-processor compilation
    /EHsc               # Enable C++ exception handling
)

set(DEBUG_C_FLAGS
    /Od                 # Disable optimization for debugging
    /Zi                 # Generate complete debugging information
    /RTC1               # Enable runtime error checks
    /Ob0                # Disable inline expansion
    /W3                 # Enable standard warnings
#    /W4                 # Enable high-level warnings
)
set(DEBUG_CXX_FLAGS
    /Od                 # Disable optimization for debugging
    /Zi                 # Generate complete debugging information
    /RTC1               # Enable runtime error checks
    /Ob0                # Disable inline expansion
    /W3                 # Enable standard warnings
#    /W4                 # Enable high-level warnings
)

set(RELEASE_C_FLAGS
    /O2                 # Optimize for speed
    /Ot                 # Favor fast code
    /Oi                 # Generate intrinsic functions
    /GL                 # Enable whole program optimization
    /Ob2                # Enable inline expansion
    /W3                 # Enable standard warnings
#    /W4                 # Enable high-level warnings
)
set(RELEASE_CXX_FLAGS
    /O2                 # Optimize for speed
    /Ot                 # Favor fast code
    /Oi                 # Generate intrinsic functions
    /GL                 # Enable whole program optimization
    /Ob2                # Enable inline expansion
    /W3                 # Enable standard warnings
#    /W4                 # Enable high-level warnings
)


#------------------------------------------------
#--- MSVC Debug Information Format Policy ---
#------------------------------------------------
# Ensure CMake policy CMP0141 is set to NEW to control the MSVC debug information format.
# This sets CMAKE_MSVC_DEBUG_INFORMATION_FORMAT to "EditAndContinue" for Debug and RelWithDebInfo configurations,
# and to "ProgramDatabase" for other configurations, but only when using the MSVC compiler.
if (POLICY CMP0141)
  cmake_policy(SET CMP0141 NEW)
  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()


#------------------------------------------------
#--- Source files gathering ---
#------------------------------------------------
# Collect all C and C++ source files from each directory in SRC_DIR
set(SOURCES_C "")
set(SOURCES_CPP "")

foreach(DIR IN LISTS SRC_DIR)
    file(GLOB_RECURSE DIR_SOURCES_C "${DIR}/*.c")
    file(GLOB_RECURSE DIR_SOURCES_CPP "${DIR}/*.cpp")
    list(APPEND SOURCES_C ${DIR_SOURCES_C})
    list(APPEND SOURCES_CPP ${DIR_SOURCES_CPP})
endforeach()


#------------------------------------------------
#--- Target definition ---
#------------------------------------------------
# Define the main executable target
message(STATUS "APP_WIN32='${APP_WIN32}'")
if(APP_WIN32)
    add_executable(${PROJECT_NAME} WIN32
        ${SOURCES_C}
        ${SOURCES_CPP}
        ${RESOURCE_FILES}
    )
else()
    add_executable(${PROJECT_NAME} 
        ${SOURCES_C}
        ${SOURCES_CPP}
        ${RESOURCE_FILES}
    )
endif()


#------------------------------------------------
#--- Target properties setup ---
#------------------------------------------------
# Setup include directories
target_include_directories(${PROJECT_NAME} PRIVATE
    ${GENERAL_INCLUDE_DIRS}
    $<$<CONFIG:Debug>:${DEBUG_INCLUDE_DIRS}>
    $<$<CONFIG:Release>:${RELEASE_INCLUDE_DIRS}>
)

# Setup library directories
target_link_directories(${PROJECT_NAME} PRIVATE
    ${GENERAL_LIBRARY_DIRS}
    $<$<CONFIG:Debug>:${DEBUG_LIBRARY_DIRS}>
    $<$<CONFIG:Release>:${RELEASE_LIBRARY_DIRS}>
)

# Setup preprocessor definitions
target_compile_definitions(${PROJECT_NAME} PRIVATE
    ${GENERAL_PREPROCESSOR_DEFINITIONS}
    $<$<CONFIG:Debug>:${DEBUG_PREPROCESSOR_DEFINITIONS}>
    $<$<CONFIG:Release>:${RELEASE_PREPROCESSOR_DEFINITIONS}>
)

# Setup linked libraries
target_link_libraries(${PROJECT_NAME} PRIVATE
    ${GENERAL_LIB}
    ${PLATFORM_LIBS}
    $<$<CONFIG:Debug>:${DEBUG_LIB}>
    $<$<CONFIG:Release>:${RELEASE_LIB}>
)

# Setup compiler and linker options (MSVC specific)
if(MSVC)
  target_compile_options(${PROJECT_NAME} PRIVATE /MP)
  target_link_options(${PROJECT_NAME} PRIVATE "/ignore:4099" "/PROFILE" "/ENTRY:mainCRTStartup")
endif()


#------------------------------------------------
#--- Post-build: Copy DLLs ---
#------------------------------------------------
# Copy DLLs to the output directory after build
if(GENERAL_BIN OR DEBUG_BIN OR RELEASE_BIN)
    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
        COMMAND ${CMAKE_COMMAND} -E copy_if_different
        ${GENERAL_BIN}
        $<$<CONFIG:Debug>:${DEBUG_BIN}>
        $<$<CONFIG:Release>:${RELEASE_BIN}>
        ${CMAKE_BINARY_DIR}
    )
endif()


#------------------------------------------------
#--- Project compilation log ---
#------------------------------------------------
# Print project and environment information for diagnostics
message(STATUS "---------------------------------------------------")
message(STATUS "---------------------------------------------------")
message(STATUS "---------------------------------------------------")
message(STATUS "--- Platform Information --------------------------")
message(STATUS "System Name:     ${CMAKE_SYSTEM_NAME}")
message(STATUS "Processor:       ${CMAKE_SYSTEM_PROCESSOR}")
message(STATUS "CMake Generator: ${CMAKE_GENERATOR}")
message(STATUS "Install Prefix:  ${CMAKE_INSTALL_PREFIX}")
message(STATUS "---------------------------------------------------")
message(STATUS "--- Compiler/Language Settings --------------------")
message(STATUS "C++ Standard:           ${CMAKE_CXX_STANDARD}")
message(STATUS "Compiler ID:            ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "Build Type:             ${CMAKE_BUILD_TYPE}")
message(STATUS "Common Compiler Flags:  ${CMAKE_CXX_FLAGS}")
message(STATUS "Debug Compiler Flags:   ${CMAKE_CXX_FLAGS_DEBUG}")
message(STATUS "Release Compiler Flags: ${CMAKE_CXX_FLAGS_RELEASE}")
message(STATUS "Common Linker Flags:    ${CMAKE_EXE_LINKER_FLAGS}")
message(STATUS "Debug Linker Flags:     ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
message(STATUS "Release Linker Flags:   ${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
message(STATUS "---------------------------------------------------")
message(STATUS "--- Path Information ------------------------------")
message(STATUS "Source Dir: ${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "Binary Dir: ${CMAKE_CURRENT_BINARY_DIR}")
if(MSVC)
  message(STATUS "---------------------------------------------------")
  message(STATUS "--- Microsoft Visual C++ (MSVC) Information -------")
  # Check the major compiler version
  if(MSVC_VERSION GREATER_EQUAL 1950)
    message(STATUS "MSVC Compiler Version: ${MSVC_VERSION} (Visual Studio 2026 or newer)")
  elseif(MSVC_VERSION GREATER_EQUAL 1930)
    message(STATUS "MSVC Compiler Version: ${MSVC_VERSION} (Visual Studio 2022)")
  endif()
message(STATUS "MSVC Toolset Version:  ${MSVC_TOOLSET_VERSION}")
message(STATUS "CPP Compiler Version:  ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "C Compiler Version:    ${CMAKE_C_COMPILER_VERSION}")
endif()
message(STATUS "---------------------------------------------------")
message(STATUS "--- Project Information ---------------------------")
message(STATUS "Project Name: ${PROJECT_NAME}")
message(STATUS "Preset Name:  ${PRESET_NAME}")
message(STATUS "---------------------------------------------------")
message(STATUS "---------------------------------------------------")
message(STATUS "---------------------------------------------------")

