# cmake/vcpkg.cmake
#
# Shared vcpkg integration for HiGHS-related projects.
#
# Usage:
#   cmake_minimum_required(VERSION 3.15)
#   list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake")
#   include(vcpkg)        # MUST be before the first project() call
#   project(my_project LANGUAGES C CXX)
#
# Behavior:
#   * Enabled only when HIGHS_VCPKG is ON (default OFF)
#   * If $ENV{VCPKG_ROOT} is set and CMAKE_TOOLCHAIN_FILE is not, point
#     CMAKE_TOOLCHAIN_FILE at vcpkg's toolchain so manifest mode kicks in.
#   * Auto-pick a sensible VCPKG_TARGET_TRIPLET unless the caller (or env
#     var VCPKG_DEFAULT_TRIPLET) already set one. Windows defaults to the
#     *-windows-static triplet so wheels stay self-contained.
#
# Notes on cross-compilation:
#   * Visual Studio generators expose the target arch via
#     CMAKE_GENERATOR_PLATFORM (-A Win32/x64/ARM64) — handled below.
#   * For Ninja/Makefile generators we cannot tell host vs. target before
#     project(); we fall back to the host arch (queried via
#     cmake_host_system_information OS_PLATFORM, which works pre-project()
#     unlike CMAKE_HOST_SYSTEM_PROCESSOR which is empty until project()
#     runs on CMake < 3.25). When cross-compiling with such a generator,
#     set VCPKG_TARGET_TRIPLET (or the VCPKG_DEFAULT_TRIPLET env var)
#     explicitly, e.g. -DVCPKG_TARGET_TRIPLET=arm64-windows-static.
#
# Skip when consumed as a vcpkg port:
#   When HiGHS is built as a port (e.g. installed via `vcpkg install highs`),
#   the outer vcpkg invocation already owns the toolchain, triplet, and
#   manifest settings. Detecting that situation and bailing out early
#   prevents this module from forcing a *-windows-static triplet, hijacking
#   VCPKG_MANIFEST_DIR to extern/vcpkg.json, or otherwise interfering with
#   the port build. Indicators that we're inside a vcpkg-driven build:
#     * CMAKE_TOOLCHAIN_FILE is already set (any toolchain in effect)
#     * Z_VCPKG_ROOT_DIR exported by vcpkg's portfile

include_guard(GLOBAL)

if((NOT HIGHS_VCPKG) OR DEFINED CMAKE_TOOLCHAIN_FILE
   OR DEFINED ENV{Z_VCPKG_ROOT_DIR})
  return()
endif()

if (DEFINED ENV{VCPKG_INSTALLATION_ROOT} AND NOT DEFINED ENV{VCPKG_ROOT})
  set(ENV{VCPKG_ROOT} $ENV{VCPKG_INSTALLATION_ROOT})
endif()

# Treat an empty VCPKG_ROOT the same as undefined (allows overrides to
# disable vcpkg by setting VCPKG_ROOT="").
if(DEFINED ENV{VCPKG_ROOT} AND NOT "$ENV{VCPKG_ROOT}" STREQUAL "")
  message(STATUS "Using VCPKG_ROOT: $ENV{VCPKG_ROOT}")
  file(TO_CMAKE_PATH "$ENV{VCPKG_ROOT}" VCPKG_ROOT_CMAKE)

  set(CMAKE_TOOLCHAIN_FILE
    "${VCPKG_ROOT_CMAKE}/scripts/buildsystems/vcpkg.cmake"
    CACHE FILEPATH "vcpkg toolchain file")

  # The manifest lives in extern/ alongside the code that consumes the
  # dependencies. vcpkg only auto-discovers vcpkg.json in CMAKE_SOURCE_DIR,
  # so point VCPKG_MANIFEST_DIR at extern/ explicitly. This makes manifest
  # mode work whether the top-level project is highspy-extras/, extern/
  # itself, or any future consumer.
  if(NOT DEFINED VCPKG_MANIFEST_DIR
     AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/../extern/vcpkg.json")
    get_filename_component(VCPKG_MANIFEST_DIR
      "${CMAKE_CURRENT_LIST_DIR}/../extern" ABSOLUTE)
    set(VCPKG_MANIFEST_DIR "${VCPKG_MANIFEST_DIR}"
      CACHE PATH "vcpkg manifest directory")
  endif()

  if(NOT DEFINED VCPKG_TARGET_TRIPLET AND NOT DEFINED ENV{VCPKG_DEFAULT_TRIPLET})
    # CMAKE_HOST_SYSTEM_PROCESSOR isn't reliably populated before project()
    # on CMake < 3.25, so query the host arch explicitly.
    if(NOT _vcpkg_host_arch)
      cmake_host_system_information(RESULT _vcpkg_host_arch
        QUERY OS_PLATFORM)
    endif()

    if(CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
      set(_vcpkg_arch x86)
    elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
      set(_vcpkg_arch arm64)
    elseif(CMAKE_GENERATOR_PLATFORM STREQUAL "x64")
      set(_vcpkg_arch x64)
    elseif(_vcpkg_host_arch MATCHES "^(x86_64|AMD64|x64)$")
      set(_vcpkg_arch x64)
    elseif(_vcpkg_host_arch MATCHES "^(aarch64|arm64|ARM64)$")
      set(_vcpkg_arch arm64)
    elseif(_vcpkg_host_arch MATCHES "^(i.86|x86|X86)$")
      set(_vcpkg_arch x86)
    endif()

    message(STATUS "Host arch (OS_PLATFORM): ${_vcpkg_host_arch}")
    message(STATUS "Auto-detected vcpkg architecture: ${_vcpkg_arch}")

    if(WIN32 AND DEFINED _vcpkg_arch)
      set(VCPKG_TARGET_TRIPLET "${_vcpkg_arch}-windows-static"
        CACHE STRING "vcpkg triplet")
      set(VCPKG_HOST_TRIPLET "${_vcpkg_arch}-windows-static"
        CACHE STRING "vcpkg host triplet")

      # Match the static CRT used by the *-windows-static triplet to
      # avoid LNK4098 "defaultlib 'LIBCMT' conflicts with ..." warnings.
      set(CMAKE_MSVC_RUNTIME_LIBRARY
        "MultiThreaded$<$<CONFIG:Debug>:Debug>"
        CACHE STRING "MSVC runtime library")

    elseif(APPLE AND DEFINED _vcpkg_arch)
      set(VCPKG_TARGET_TRIPLET "${_vcpkg_arch}-osx"
        CACHE STRING "vcpkg triplet")
    elseif(UNIX AND DEFINED _vcpkg_arch)
      set(VCPKG_TARGET_TRIPLET "${_vcpkg_arch}-linux"
        CACHE STRING "vcpkg triplet")
    endif()

    unset(_vcpkg_arch)
    unset(_vcpkg_host_arch)
  endif()

  # Fall back to the env-var default if nothing else picked a triplet.
  if(NOT DEFINED VCPKG_TARGET_TRIPLET AND DEFINED ENV{VCPKG_DEFAULT_TRIPLET})
    set(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}"
      CACHE STRING "vcpkg triplet")
  endif()

  if(DEFINED VCPKG_TARGET_TRIPLET)
    message(STATUS "Using vcpkg triplet: ${VCPKG_TARGET_TRIPLET}")
  else()
    message(WARNING
      "VCPKG_TARGET_TRIPLET not set; vcpkg will use its built-in default. "
      "Set -DVCPKG_TARGET_TRIPLET=... when cross-compiling.")
  endif()
endif()