cmake_minimum_required(VERSION 3.5)
if(CMAKE_VERSION VERSION_LESS 3.12)
  cmake_policy(VERSION ${CMAKE_VERSION})
else()
  cmake_policy(VERSION 3.5.1...3.29.0)
endif()
message(STATUS "Using CMake version ${CMAKE_VERSION}")

set(CDF_VERSION_MAJOR 3)
set(CDF_VERSION_MINOR 9)
set(CDF_VERSION_INCREMENT 2)
#set(CDF_VERSION_SUBINCREMENT 0)
set(CDF_VERSION ${CDF_VERSION_MAJOR}.${CDF_VERSION_MINOR}.${CDF_VERSION_INCREMENT})
set(VERSION ${CDF_VERSION})
set(CDF_FULL_VERSION ${CDF_VERSION})

message(STATUS "CDF_VERSION: ${CDF_VERSION}")

project(CDF VERSION 
${CDF_VERSION} 
LANGUAGES C
HOMEPAGE_URL "https://cdf.gsfc.nasa.gov"
DESCRIPTION "CDF is a set of software libraries and machine-independent data formats that support the creation, access, and sharing of scientific data."
)

set(PACKAGE "CDF" CACHE STRING "")
# as PACKAGE:STRING=CDF in CMakeCache.txt (in alphabatic order)

set(PACKAGE_VERSION ${CDF_VERSION})

set(CMAKE_VERBOSE_MAKEFILE OFF)

if (DEFINED ZLIB)
  set(zlib ${ZLIB})
  string(TOLOWER ${zlib} zlib)
  if(NOT "${zlib}" MATCHES "^(orig|ng)$")
    message(FATAL_ERROR "zlib has an invalid value:${zlib}")
  endif ()
endif ()

if(MSVC)
  set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
endif()

# For CMAKE_INSTALL_LIBDIR
if (NOT CMAKE_INSTALL_PREFIX)
  set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR})
endif()

if(NOT ${CMAKE_INSTALL_PREFIX} MATCHES "/usr/local")
  set(CMAKE_INSTALL_LIBDIR lib)
endif()

include(GNUInstallDirs)
include(CheckCCompilerFlag)

if (APPLE)
  set(CMAKE_MACOSX_RPATH ON)
  set(rbase "@loader_path")
else ()
  set(rbase "$ORIGIN")
endif ()

file(RELATIVE_PATH lib_dir
     "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}"
     "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")

# Honor user overrides, whether at the command line or FetchContent
set(CMAKE_INSTALL_RPATH "${rbase};${rbase}/${lib_dir}"
    CACHE STRING "Install RPATH")

if(DEFINED FORTRAN AND (NOT FORTRAN))
  set(TEST_FORTRAN OFF)
else()
  if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
    include(CheckFortranSourceCompiles)
    include(CheckFortranCompilerFlag)
    include(CheckLanguage)
    check_language(Fortran)
    if(CMAKE_Fortran_COMPILER)
      enable_language(Fortran)
      set(TEST_FORTRAN ON)
    else()
      set(TEST_FORTRAN OFF)
    endif()
  endif()
endif()

if (NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
  string(APPEND CMAKE_C_FLAGS ${CMAKE_C_FLAGS_RELEASE})
endif()

find_program(UNAME NAMES uname)

if(UNAME)
  macro(getuname name flag)
    execute_process(COMMAND "${UNAME}" "${flag}" OUTPUT_VARIABLE "${name}")
  endmacro(getuname)
  getuname(kernelName -s)
  getuname(kernelRel  -r)
  getuname(machineName    -m)
  getuname(osName -o)
else()
  if(MSVC)
    set(kernelName "MSVC")
  endif()
endif()

if(ABI)
  if("${machineName}" MATCHES "arm64.*")
    if("${osName}" MATCHES "Darwin.*" AND NOT ("${ABI}" STREQUAL "x86_64"))
      message(FATAL_ERROR "MAC OS (arm64) does not support ABI:${ABI}")
    endif()
  elseif("${machineName}" MATCHES "x86_64.*")
    if("${kernelName}" MATCHES "Linux.*" OR 
        "${kernelName}" MATCHES "CYGWIN.*" OR
        "${kernelName}" MATCHES "MSYS.*" OR
        "${kernelName}" MATCHES "MINGW.*")
       if(NOT ${ABI} STREQUAL "m32")
         message(FATAL_ERROR "${kernelName} does not support ABI:${ABI}")
       endif()
    endif()
  endif()
endif()

# Define some Platforms
if(${kernelName} MATCHES "Linux.*")
  set(ISLINUX YES CACHE BOOL "a Linux")
elseif(${osName} MATCHES "Darwin.*")
  set(ISDARWIN YES CACHE BOOL "a MAC")
elseif(${kernelName} MATCHES "CYGWIN.*")
  set(ISCYGWIN YES CACHE BOOL "a CYGWIN")
elseif((${kernelName} MATCHES "MINGW.*") OR (${kernelName} MATCHES "MSYS.*"))
  set(ISMINGW YES CACHE BOOL "a MINGW")
  set(MINGW YES)
elseif(MSVC)
	set(ISMSVC YES CACHE BOOL "a MSVC")
  set(CMAKE_BUILD_TYPE Release)
endif()

#####
# ABI
#####

if(ABI) 
  if("${osName}" MATCHES "Darwin.*")
    if(${ABI} STREQUAL "x86_64")
      set(ABI " -arch x86_64")
      string(APPEND CMAKE_C_FLAGS ${ABI})
    endif()
  elseif(NOT MSVC)
    string(APPEND CMAKE_C_FLAGS " -${ABI}")
    if(TEST_FORTRAN)
      string(APPEND CMAKE_Fortran_FLAGS " -${ABI}")
    endif()
  endif()
endif()

###################################
# Compiler and Linker Configuration
###################################

option(BUILD_SHARED_CDF "Whether to build shared CDF library" ON)
option(BUILD_STATIC_CDF "Whether to build static CDF library" ON)
if(NOT BUILD_SHARED_CDF AND NOT BUILD_STATIC_CDF)
  message(FATAL_ERROR "Need to build at leasst one library...")
endif()

if(BUILD_SHARED_CDF)
  set(BUILD_SHARED_LIBS ON)
endif()

######i##################################
# zlib - default to orig if not specified
#####i###################################

if(zlib)
  if(zlib STREQUAL "orig")
    set(TOZLIB-NG NO CACHE BOOL "Use zlib-ng package")
  elseif(zlib STREQUAL "ng")
    set(TOZLIB-NG YES CACHE BOOL "Use zlib-ng package")
  endif()
else()
  set(TOZLIB-NG NO CACHE BOOL "Use zlib-ng package")
endif()

if(TOZLIB-NG)
  include_directories(src/lib/zlib-ng)
  add_subdirectory(src/lib/zlib-ng)
  set(CDF_C_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/zlib-ng CACHE STRING "zlib include" FORCE)
  set(USE_ZLIB-NG yes CACHE BOOL "Use Zlib-ng package" FORCE)
  set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} src/lib/zlib-ng)
else()
  include_directories(src/lib/zlib)
  add_subdirectory(src/lib/zlib)
  set(CDF_C_INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/zlib CACHE STRING "zlib include" FORCE)
  set(USE_ZLIB-NG no CACHE BOOL "Not to use Zlib-ng package" FORCE)
  set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} src/lib/zlib)
endif()

#################
# CDF src/include
#################

add_subdirectory("src/include")

include_directories(src/include)
set(CMAKE_REQUIRED_INCLUDES src/include)

#############
# CDF src/lib
#############

add_subdirectory(src/lib)

#####
# JNI
#####

if((DEFINED JNI AND (NOT JNI)) OR (NOT BUILD_SHARED_CDF))
  set(BUILD_JNI OFF)
else()
  if(DEFINED ENV{JDK_HOME} OR JDK_HOME)
    if(DEFINED ENV{JDK_HOME})
      set(JDK_HOME $ENV{JDK_HOME})
    endif()
    string(TOLOWER ${kernelName} kernelnamel)
    if(kernelnamel MATCHES "linux.*")
      set(add_jnidir1 "include")
      set(add_jnidir2 "linux")
    elseif(kernelnamel MATCHES "darwin.*")
      set(add_jnidir1 "Contents/Home/include")
      set(add_jnidir2 "darwin")
    elseif(kernelnamel MATCHES "msvc.*" OR kernelnamel MATCHES "cygwin.*" OR
           kernelnamel MATCHES "mingw.*" OR kernelnamel MATCHES "msys.*")
      set(add_jnidir1 "include")
      set(add_jnidir2 "win32")
    endif()
    if(EXISTS ${JDK_HOME}/${add_jnidir1}/jni.h AND
        EXISTS ${JDK_HOME}/${add_jnidir1}/${add_jnidir2}/jni_md.h)
      set(BUILD_JNI ON)
    else()
      if(DEFINED JDK_HOME)
        message(STATUS "JDK_HOME defined, but needed files are missing... JNI not built")
        set(BUILD_JNI OFF)
      endif()
    endif()
  else()
    set(BUILD_JNI OFF)
  endif()
endif()

set(BUILD_Csharp OFF)
if(Csharp AND MSVC)
  set(BUILD_Csharp ON)
endif()

if(BUILD_JNI)
  add_subdirectory(cdfjava/jni)
endif()

########
# csharp
########

if(BUILD_Csharp)
  add_subdirectory(win32/csharp)
endif()

################################
# Define Utility Macros
################################

# Macro to append files to the EXTRA_DIST files.
# Note: can only be used in subdirectories because of the use of PARENT_SCOPE
set(EXTRA_DIST "")
macro(ADD_EXTRA_DIST files)
  foreach(F ${files})
    set(EXTRA_DIST ${EXTRA_DIST} ${CMAKE_CURRENT_SOURCE_DIR}/${F})
    set(EXTRA_DIST ${EXTRA_DIST} PARENT_SCOPE)
  endforeach()
endmacro()

# A cmake script to print out information at the end of the configuration step.
macro(print_conf_summary)
  message("")
  message("")
  message("Configuration Summary:")
  message("")
  if(TOZLIB-NG)
    message(STATUS "Using zlib-ng...")
  else()
    message(STATUS "Using original zlib...")
  endif()
  message(STATUS "Building Shared CDF Library:     ${BUILD_SHARED_CDF}")
  message(STATUS "Building Static CDF Library:     ${BUILD_STATIC_CDF}")
  message(STATUS "Building CDF JNI Library:     ${BUILD_JNI}")
  message(STATUS "Building Utilities:            ${BUILD_UTILITIES}")
  if(CMAKE_PREFIX_PATH)
    message(STATUS "CMake Prefix Path:             ${CMAKE_PREFIX_PATH}")
  endif()
  message("")

  message("Tests Enabled:              ${ENABLE_TESTS}")
  if(ENABLE_TESTS)
    message(STATUS "Unit Tests:        ${ENABLE_UNIT_TESTS}")
  endif()

  message("")
  message("Compiler:")
  message("")
  message(STATUS "Build Type:           ${CMAKE_BUILD_TYPE}")
  message(STATUS "CMAKE_C_COMPILER:     ${CMAKE_C_COMPILER}")
  message(STATUS "CMAKE_C_FLAGS:        ${CMAKE_C_FLAGS}")
  if(TEST_FORTRAN)
    message(STATUS "CMAKE_FORTRAN_COMPILER:     ${CMAKE_Fortran_COMPILER}")
    message(STATUS "CMAKE_FORTRAN_FLAGS:  ${CMAKE_Fortran_FLAGS}")
  endif()
  if(BUILD_JNI)
    message(STATUS "Build CDF-JAVA JNI...")
  endif()
  if(BUILD_Csharp)
    message(STATUS "Build CDF-C#...")
  endif()
  if(DEFINED ${CMAKE_BUILD_TYPE})
    string(TOLOWER ${CMAKE_BUILD_TYPE} BUILD_TYPE)
    if("${BUILD_TYPE}" STREQUAL "release")
      message(STATUS "CMAKE_C_FLAGS_RELEASE:   ${CMAKE_C_FLAGS_RELEASE}")
    endif()
  endif()

  message(STATUS "Linking against:      ${ALL_TLL_LIBS}")
  message(STATUS "Installation:      ${CMAKE_INSTALL_PREFIX}")

  message("")
endmacro()

add_subdirectory(liblib)

#
# Options parsing
#
option(_LARGEFILE64_SOURCE "Compile with large file" ON)
option(_LARGEFILE_SOURCE "Compile with large file" ON)
option(__USE_POSIX "A new stream that refers to an existing system file descriptor" ON)
option(_FILE_OFFSET_BITS "Build 64-bit option" ON)
option(WITH_OPTIM "Build with optimization" ON)
option(WITH_UNALIGNED "Support unaligned reads on platforms that support it" ON)

# Option to build utilities
option(BUILD_UTILITIES "Build cdf tools,e.g., cdfedit, cdfcompare, cdfconvert, etc." ON)
if(BUILD_UTILITIES)
  include_directories(src/include)
  add_subdirectory(src/tools)
endif()

# Option to build test programs
option(BUILD_TESTS "Build cdf tests programs, e.g., qst2ic,qst2c, qst2icTT2000,etc." ON)

enable_testing()
add_subdirectory(cdf_tests)

if(NOT WITH_UNALIGNED)
  add_definitions(-DNO_UNALIGNED)
  message(STATUS "Unaligned reads manually disabled")
endif()

# Apply warning compiler flags
if(WITH_MAINTAINER_WARNINGS)
  add_compile_options(${WARNFLAGS} ${WARNFLAGS_MAINTAINER} ${WARNFLAGS_DISABLE})
else()
  add_compile_options(${WARNFLAGS} ${WARNFLAGS_DISABLE})
endif()

#################################
# Print the configuration summary
#################################
print_conf_summary()

