###############################################################################
# Description       : CMake build script for libSBML
# Original author(s): Frank Bergmann <fbergman@caltech.edu>
# Organization      : California Institute of Technology
#
# This file is part of libSBML.  Please visit http://sbml.org for more
# information about SBML, and the latest version of libSBML.
#
# Copyright (C) 2013-2018 jointly by the following organizations:
#     1. California Institute of Technology, Pasadena, CA, USA
#     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
#     3. University of Heidelberg, Heidelberg, Germany
#
# Copyright (C) 2009-2013 jointly by the following organizations:
#     1. California Institute of Technology, Pasadena, CA, USA
#     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
#
# Copyright (C) 2006-2008 by the California Institute of Technology,
#     Pasadena, CA, USA
#
# Copyright (C) 2002-2005 jointly by the following organizations:
#     1. California Institute of Technology, Pasadena, CA, USA
#     2. Japan Science and Technology Agency, Japan
#
# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation.  A copy of the license agreement is provided
# in the file named "LICENSE.txt" included with this software distribution
# and also available online as http://sbml.org/software/libsbml/license.html
#
###############################################################################

# used to create CMake config files for projects using this library
if (${CMAKE_VERSION} VERSION_GREATER 2.8.7)
include(CMakePackageConfigHelpers)
endif()

###############################################################################
#
# Initialize SBML sources
#
set(LIBSBML_SOURCES)

###############################################################################
#
# Include all packages
#
file(GLOB PACKAGE_OPTIONS "*package.cmake")
foreach(package ${PACKAGE_OPTIONS})

    include(${package})

endforeach()

###############################################################################
#
# utility macro for copying files only if they have changed, for this it is
# assumed that the source file is always created. Then the macro is called.
# If the target file will only be overwritten if it does not yet exist, or
# its content is different. At the end the source file will be removed.
#
macro(copy_if_different_and_remove source target)
# message(
# "
# copy_if_different_and_remove called with:
#     source = ${source}
#     target = ${target}
# "
# )
file(READ ${source} CONTENT)

if (EXISTS ${target})
    # message("target exists")
    file(READ ${target} CURRENT_CONTENT)
    string(COMPARE EQUAL ${CURRENT_CONTENT} ${CONTENT} IS_SAME)
    if (NOT ${IS_SAME})
        #message("content is different")
        file(WRITE ${target} ${CONTENT})
    endif()
else()
    #message("target does not exist")
    file(WRITE ${target} ${CONTENT})
endif()

file(REMOVE ${source})

endmacro(copy_if_different_and_remove)

###############################################################################
#
# Register all packagess
#

file(GLOB REGISTER_EXTENSIONS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/ "${CMAKE_CURRENT_SOURCE_DIR}/sbml/packages/*-register.h")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.h.new" "/* This file is autogenerated and will be overwritten by the configuration process */\n\n")
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.h.new" "#include <sbml/extension/SBMLExtension.h>\n")
foreach(package ${REGISTER_EXTENSIONS})
    file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.h.new" "#include <${package}>\n")
endforeach()
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.h.new" "\n")
copy_if_different_and_remove(
  ${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.h.new
  ${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.h
)

file(GLOB REGISTER_EXTENSIONS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/ "${CMAKE_CURRENT_SOURCE_DIR}/sbml/packages/*-register.cxx")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.cxx.new" "/* This file is autogenerated and will be overwritten by the configuration process */\n\n")
foreach(package ${REGISTER_EXTENSIONS})
    file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.cxx.new" "#include <${package}>\n")
endforeach()
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.cxx.new" "\n")

copy_if_different_and_remove(
  ${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.cxx.new
  ${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/RegisterExtensions.cxx
)

# write all package declarations to libsbml-config-packages
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-config-packages.h.new" "/* This file is autogenerated and will be overwritten by the configuration process */\n\n")
if (LIBSBML_PACKAGE_INCLUDES)
  list(REMOVE_DUPLICATES LIBSBML_PACKAGE_INCLUDES)
  foreach(declaration ${LIBSBML_PACKAGE_INCLUDES})
      file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-config-packages.h.new" "#define ${declaration} 1\n")
  endforeach()
endif()
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-config-packages.h.new" "\n")

copy_if_different_and_remove(
  ${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-config-packages.h.new
  ${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-config-packages.h
)

###############################################################################
#
# create libsbml-config-common.h
#
include(CheckIncludeFiles)
check_include_files (check.h HAVE_CHECK_H)
check_include_files (expat.h HAVE_EXPAT_H)
check_include_files (errno.h HAVE_ERRNO_H)
check_include_files (ieeefp.h HAVE_IEEEFP_H)
check_include_files (math.h HAVE_MATH_H)
check_include_files (sys/types.h HAVE_SYS_TYPES_H)
check_include_files (float.h STDC_HEADERS)
check_include_files (stdarg.h STDC_HEADERS)
check_include_files (stdlib.h STDC_HEADERS)
check_include_files (string.h STDC_HEADERS)

set(WORDS_BIGENDIAN)
if (UNIX)
  include (TestBigEndian)
  test_big_endian(WORDS_BIGENDIAN)
else()
  # test_big_endian seems to fail with nmake (VS 2010) on windows
  # however, windows is always little endian, so catch this here
  set(WORDS_BIGENDIAN FALSE)
endif()

# for whatever reason some of the values are not correctly picked up
# so we encode these values here
if (MSVC)
  set(HAVE_ERRNO_H TRUE)
  set(STDC_HEADERS TRUE)
  set(HAVE_MATH_H TRUE)
  set(HAVE_SYS_TYPES_H TRUE)
endif()


include(CheckLibraryExists)
check_library_exists(m sqrt "" HAVE_LIBM)

# generate configuration for this system
configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/sbml/common/libsbml-config-common.h.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-config-common.h
)

# Replace: @PACKAGE_VERSION@ and @LIBSBML_VERSION_NUMERIC@
configure_file (
  "${CMAKE_CURRENT_SOURCE_DIR}/sbml/common/libsbml-version.h.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-version.h"
)

configure_file (
  "${CMAKE_CURRENT_SOURCE_DIR}/sbml/common/libsbml-namespace.h.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-namespace.h"
)

configure_file (
  "${CMAKE_CURRENT_SOURCE_DIR}/sbml/common/libsbml-package.h.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/libsbml-package.h"
)
  
###############################################################################
#
# mark header files for installation
#

foreach(dir annotation common compress conversion extension html2md maddy math units util validator xml  )

    file(GLOB header_files "${CMAKE_CURRENT_SOURCE_DIR}/sbml/${dir}/*.h")
    install(FILES ${header_files} DESTINATION include/sbml/${dir})

endforeach(dir)


file(GLOB files "${CMAKE_CURRENT_SOURCE_DIR}/sbml/*.h")
install(FILES ${files} DESTINATION include/sbml)


###############################################################################
#
# copy build specific header files
#

file(GLOB additional_common "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/*.h")
file(GLOB additional_ext "${CMAKE_CURRENT_BINARY_DIR}/sbml/extension/*.h")

install(FILES ${additional_common} DESTINATION include/sbml/common)
install(FILES ${additional_ext} DESTINATION include/sbml/extension)

###############################################################################
#
# specify include directories
#

include_directories(BEFORE ${LIBSBML_ROOT_SOURCE_DIR}/src/sbml)
include_directories(BEFORE ${LIBSBML_ROOT_SOURCE_DIR}/src)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/src)
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}/src/sbml)
if (EXTRA_INCLUDE_DIRS) 
 include_directories(${EXTRA_INCLUDE_DIRS})
endif(EXTRA_INCLUDE_DIRS)


###############################################################################
#
# Find all sources
#

macro(ADD_FUNCTION directory)

    set(prefix "sbml/")

    if (directory STREQUAL "sbml")
        set(prefix "")
    endif()


    file(GLOB temp
            ${prefix}${directory}/*.cpp
            ${prefix}${directory}/*.cxx
            ${prefix}${directory}/*.c
            ${prefix}${directory}/*.h)

    # Set the constraint files to be 'headers' as they don't have to be compiled
    # since they don't contain symbols but rather are included directly.  Could
    # remove them, but that keeps their content from being found in searches, etc.
    # in IDEs such as VisualStudio.
    foreach(tempFile ${temp})
        if (directory STREQUAL "validator/constraints")
            if (tempFile MATCHES ".*Constraints.cpp")
              set_source_files_properties(
                  ${tempFile}
                  PROPERTIES HEADER_FILE_ONLY true
                  )
#              list(REMOVE_ITEM temp "${tempFile}")
            endif()
        endif()
        if (tempFile MATCHES ".*.cxx$")
          set_source_files_properties(
              ${tempFile}
              PROPERTIES HEADER_FILE_ONLY true
              )
        endif()
    endforeach()

    # create source group for IDEs
    source_group(${directory} FILES ${temp})

    # add to libsbml sources
    set(LIBSBML_SOURCES ${LIBSBML_SOURCES} ${temp})

endmacro(ADD_FUNCTION)

foreach (directory annotation common conversion extension sbml html2md maddy math
                   units util validator validator/constraints)

	ADD_FUNCTION(${directory})
				       
endforeach()

###############################################################################
#
# Find compression sources and adjust include and lib directory
#
set (COMPRESS_SOURCES)
set(COMPRESS_SOURCES ${COMPRESS_SOURCES}
    sbml/compress/CompressCommon.h
    sbml/compress/CompressCommon.cpp
    sbml/compress/InputDecompressor.cpp
    sbml/compress/InputDecompressor.h
    sbml/compress/OutputCompressor.cpp
    sbml/compress/OutputCompressor.h
    )

if(WITH_BZIP2)

  set(COMPRESS_SOURCES ${COMPRESS_SOURCES}
        sbml/compress/bzfstream.h
        sbml/compress/bzfstream.cpp
        )
  set(LIBSBML_LIBS ${LIBSBML_LIBS} BZ2::BZ2)

endif()

if(WITH_ZLIB)

set(COMPRESS_SOURCES ${COMPRESS_SOURCES}
        sbml/compress/zip.c
        sbml/compress/zip.h
        sbml/compress/unzip.h
        sbml/compress/unzip.c
        sbml/compress/ioapi.h
        sbml/compress/ioapi.c
        sbml/compress/ioapi_mem.h
        sbml/compress/ioapi_mem.c
        sbml/compress/zfstream.h
        sbml/compress/zfstream.cpp
        sbml/compress/zipfstream.cpp
        sbml/compress/zipfstream.h
    )

    if (WIN32)
        #
        # compress/iowin32.c contains only symbols on WIN32 if we include
        # it on other platforms a warning is issued by ranlib about
        # no symbols. So it will only be included on windows.
        #
        set(COMPRESS_SOURCES ${COMPRESS_SOURCES}
            sbml/compress/iowin32.c
            sbml/compress/iowin32.h
        )
    endif()

    set(LIBSBML_LIBS ${LIBSBML_LIBS} ZLIB::ZLIB)

endif()

source_group(compress FILES ${COMPRESS_SOURCES})
set(LIBSBML_SOURCES ${LIBSBML_SOURCES} ${COMPRESS_SOURCES})

###############################################################################
#
# Find xml sources and adjust include and lib directory
#

set(XML_SOURCES)
set(XML_SOURCES ${XML_SOURCES}

  sbml/xml/XMLAttributes.cpp
  sbml/xml/XMLBuffer.cpp
  sbml/xml/XMLConstructorException.cpp
  sbml/xml/XMLError.cpp
  sbml/xml/XMLErrorLog.cpp
  sbml/xml/XMLLogOverride.cpp
  sbml/xml/XMLFileBuffer.cpp
  sbml/xml/XMLHandler.cpp
  sbml/xml/XMLInputStream.cpp
  sbml/xml/XMLMemoryBuffer.cpp
  sbml/xml/XMLNamespaces.cpp
  sbml/xml/XMLNode.cpp
  sbml/xml/XMLOutputStream.cpp
  sbml/xml/XMLParser.cpp
  sbml/xml/XMLToken.cpp
  sbml/xml/XMLTokenizer.cpp
  sbml/xml/XMLTriple.cpp
  sbml/xml/XMLAttributes.h
  sbml/xml/XMLBuffer.h
  sbml/xml/XMLConstructorException.h
  sbml/xml/XMLError.h
  sbml/xml/XMLErrorLog.h
  sbml/xml/XMLLogOverride.h
  sbml/xml/XMLFileBuffer.h
  sbml/xml/XMLHandler.h
  sbml/xml/XMLInputStream.h
  sbml/xml/XMLMemoryBuffer.h
  sbml/xml/XMLNamespaces.h
  sbml/xml/XMLNode.h
  sbml/xml/XMLOutputStream.h
  sbml/xml/XMLParser.h
  sbml/xml/XMLToken.h
  sbml/xml/XMLTokenizer.h
  sbml/xml/XMLTriple.h
)

if(WITH_EXPAT)

    set(XML_SOURCES ${XML_SOURCES}
        sbml/xml/ExpatAttributes.cpp
        sbml/xml/ExpatHandler.cpp
        sbml/xml/ExpatParser.cpp
        sbml/xml/ExpatAttributes.h
        sbml/xml/ExpatHandler.h
        sbml/xml/ExpatParser.h
    )
    set(LIBSBML_LIBS ${LIBSBML_LIBS} EXPAT::EXPAT)

endif(WITH_EXPAT)

if(WITH_LIBXML)

    set(XML_SOURCES ${XML_SOURCES}
        sbml/xml/LibXMLAttributes.cpp
        sbml/xml/LibXMLHandler.cpp
        sbml/xml/LibXMLNamespaces.cpp
        sbml/xml/LibXMLParser.cpp
        sbml/xml/LibXMLTranscode.cpp
        sbml/xml/LibXMLAttributes.h
        sbml/xml/LibXMLHandler.h
        sbml/xml/LibXMLNamespaces.h
        sbml/xml/LibXMLParser.h
        sbml/xml/LibXMLTranscode.h
    )
    set(LIBSBML_LIBS ${LIBSBML_LIBS} LIBXML::LIBXML)

endif(WITH_LIBXML)

if(WITH_XERCES)

    set(XML_SOURCES ${XML_SOURCES}
        sbml/xml/XercesAttributes.cpp
        sbml/xml/XercesHandler.cpp
        sbml/xml/XercesNamespaces.cpp
        sbml/xml/XercesParser.cpp
        sbml/xml/XercesTranscode.cpp
        sbml/xml/XercesAttributes.h
        sbml/xml/XercesHandler.h
        sbml/xml/XercesNamespaces.h
        sbml/xml/XercesParser.h
        sbml/xml/XercesTranscode.h
    )

    set(LIBSBML_LIBS ${LIBSBML_LIBS} XERCES::XERCES)


endif(WITH_XERCES)

source_group(xml FILES ${XML_SOURCES})
set(LIBSBML_SOURCES ${LIBSBML_SOURCES} ${XML_SOURCES})

###############################################################################
#
# Build library
#
SET (INCLUDE_DESTINATION)
if (${CMAKE_VERSION} VERSION_GREATER "2.8.11")
  set(INCLUDE_DESTINATION INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif()

if (NOT LIBSBML_SKIP_SHARED_LIBRARY)

add_library (${LIBSBML_LIBRARY} SHARED ${LIBSBML_SOURCES} )

# When we included the 'maddy' library, we needed features added in c++11.
target_compile_features(${LIBSBML_LIBRARY} PUBLIC cxx_std_11)

if (LIBSBML_SHARED_VERSION)
  SET_TARGET_PROPERTIES(${LIBSBML_LIBRARY} PROPERTIES
                        SOVERSION ${LIBSBML_VERSION_MAJOR}
                        VERSION ${LIBSBML_VERSION_MAJOR}.${LIBSBML_VERSION_MINOR}.${LIBSBML_VERSION_PATCH})
endif()

target_link_libraries(${LIBSBML_LIBRARY} ${LIBSBML_LIBS} ${EXTRA_LIBS})

INSTALL(TARGETS ${LIBSBML_LIBRARY} 
        EXPORT ${LIBSBML_LIBRARY}-targets
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ${INCLUDE_DESTINATION}
)

# and install the exported target configuration
INSTALL(EXPORT ${LIBSBML_LIBRARY}-targets
        DESTINATION ${PACKAGE_CONFIG_DIR}
)

if (${CMAKE_VERSION} VERSION_GREATER 2.8.7)
CONFIGURE_PACKAGE_CONFIG_FILE(
  ${CMAKE_CURRENT_LIST_DIR}/libsbml-config.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-config.cmake
  INSTALL_DESTINATION ${PACKAGE_CONFIG_DIR}
)

WRITE_BASIC_PACKAGE_VERSION_FILE(
  ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-config-version.cmake 
  VERSION ${PROJECT_VERSION}
  COMPATIBILITY AnyNewerVersion
)
  
INSTALL(
  FILES 
    ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-config-version.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-config.cmake
  DESTINATION ${PACKAGE_CONFIG_DIR}
)
endif()

  
endif()


if (NOT LIBSBML_SKIP_STATIC_LIBRARY)
add_library (${LIBSBML_LIBRARY}-static STATIC ${LIBSBML_SOURCES} )

# When we included the 'maddy' library, we needed features added in c++11.
target_compile_features(${LIBSBML_LIBRARY}-static PUBLIC cxx_std_11)

if (WIN32 AND NOT CYGWIN)
    # don't decorate static library 
    set_target_properties(${LIBSBML_LIBRARY}-static PROPERTIES COMPILE_DEFINITIONS "LIBLAX_STATIC=1;LIBSBML_STATIC=1")
endif(WIN32 AND NOT CYGWIN)

target_link_libraries(${LIBSBML_LIBRARY}-static ${LIBSBML_LIBS} ${EXTRA_LIBS})

INSTALL(TARGETS ${LIBSBML_LIBRARY}-static
        EXPORT  ${LIBSBML_LIBRARY}-static-targets
        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
        ${INCLUDE_DESTINATION}
)

# and install the exported target configuration
INSTALL(EXPORT ${LIBSBML_LIBRARY}-static-targets
        DESTINATION ${PACKAGE_CONFIG_DIR}
)

if (${CMAKE_VERSION} VERSION_GREATER 2.8.7)

CONFIGURE_PACKAGE_CONFIG_FILE(
  ${CMAKE_CURRENT_LIST_DIR}/libsbml-static-config.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-static-config.cmake
  INSTALL_DESTINATION ${PACKAGE_CONFIG_DIR})

WRITE_BASIC_PACKAGE_VERSION_FILE(
  ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-static-config-version.cmake
  VERSION ${PROJECT_VERSION}
  COMPATIBILITY AnyNewerVersion
)

INSTALL(FILES 
  ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-static-config-version.cmake
  ${CMAKE_CURRENT_BINARY_DIR}/${LIBSBML_LIBRARY}-static-config.cmake
  DESTINATION ${PACKAGE_CONFIG_DIR}
)
endif()

endif (NOT LIBSBML_SKIP_STATIC_LIBRARY)

##############################################################################
#
# add native tests
#
add_subdirectory(sbml)

##############################################################################
#
# construct list of all header files to create dependency list for 
# language bindings
#

file(GLOB LIBSBML_HEADER_FILES "${CMAKE_CURRENT_BINARY_DIR}/sbml/common/*.h")    
foreach(file ${LIBSBML_SOURCES})
   
   if ("${file}" MATCHES "h$" )
     get_filename_component(full_path ${file} ABSOLUTE)
     list(APPEND LIBSBML_HEADER_FILES ${full_path})     
   endif()

endforeach()

##############################################################################
#
# create language bindings
#
add_subdirectory(bindings)
