# Copyright (c) 2019, Triad National Security, LLC
# All rights reserved.

# Copyright 2019. Triad National Security, LLC. This software was
# produced under U.S. Government contract 89233218CNA000001 for Los
# Alamos National Laboratory (LANL), which is operated by Triad
# National Security, LLC for the U.S. Department of Energy. 
# All rights in the program are reserved by Triad National Security,
# LLC, and the U.S. Department of Energy/National Nuclear Security
# Administration. The Government is granted for itself and others acting
# on its behalf a nonexclusive, paid-up, irrevocable worldwide license
# in this material to reproduce, prepare derivative works, distribute
# copies to the public, perform publicly and display publicly, and to
# permit others to do so
 
# 
# This is open source software distributed under the 3-clause BSD license.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. Neither the name of Triad National Security, LLC, Los Alamos
#    National Laboratory, LANL, the U.S. Government, nor the names of its
#    contributors may be used to endorse or promote products derived from this
#    software without specific prior written permission.
# 
#  
# THIS SOFTWARE IS PROVIDED BY TRIAD NATIONAL SECURITY, LLC AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# TRIAD NATIONAL SECURITY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



#
# Jali NetCDF Find Module
#
# Usage:
#    Control the search through NetCDF_DIR or setting environment variable
#    NetCDF_ROOT to the NetCDF installation prefix.
#    By default only searches for C library. To search for the C++ library
#    set 'CXX' in the COMPONENTS option in find_package(NetCDF) 
#
#    This module does not search default paths! 
#
#    Following variables are set:
#    NetCDF_FOUND            (BOOL)       Flag indicating if NetCDF was found
#    NetCDF_INCLUDE_DIR      (PATH)       Path to the NetCDF include file
#    NetCDF_INCLUDE_DIRS     (LIST)       List of all required include files
#    NetCDF_C_LIBRARIES      (LIST)       List of all required libraries to link to 
#                                          the NetCDF C library
#    NetCDF_CXX_LIBRARIES    (LIST)       List of all required libraries to link to 
#                                          the NetCDF C++ library (If CXX is in COMPONENTS)
#
#    Additional variables set
#    NetCDF_C_LIBRARY        (FILE)       NetCDF C library
#    NetCDF_CXX_LIBRARY      (FILE)       NetCDF C++ library (If CXX is in the COMPONENTS)
#    NetCDF_LARGE_DIMS       (BOOL)       Checks the header files for size of 
#                                          NC_MAX_DIMS, NC_MAX_VARS and NC_MAX_VARS_DIMS
#                                          Returns TRUE if
#                                          NC_MAX_DIMS >= 655363
#                                          NC_MAX_VARS >= 524288
#                                          NC_MAX_VAR_DIMS >= 8
#
# #############################################################################

# Standard CMake modules see CMAKE_ROOT/Modules
include(FindPackageHandleStandardArgs)

# Jali CMake functions see <root>/tools/cmake for source
include(PrintVariable)
include(AddPackageDependency)

# Macro to handle print out
macro(_netcdf_status mess)
  if(NOT NetCDF_FIND_QUIETLY)
    message(STATUS "${mess}")
  endif()
endmacro()

# Search for the nc-config binary
find_program(NetCDF_CONFIG_BINARY nc-config
             HINTS ${NetCDF_DIR} ${NetCDF_BIN_DIR}
	     PATH_SUFFIXES bin Bin
	     DOC "NetCDF configuration script")


if ( NetCDF_C_LIBRARIES AND NetCDF_INCLUDE_DIRS )

    # Do nothing. Variables are set. No need to search again

else(NetCDF_C_LIBRARIES AND NetCDF_INCLUDE_DIRS)

    # Cache variables
    if(NetCDF_DIR)
        set(NetCDF_DIR "${NetCDF_DIR}" CACHE PATH "Path to search for NetCDF include and library files")
    endif()

    if(NetCDF_INCLUDE_DIR)
        set(NetCDF_INCLUDE_DIR "${NetCDF_INCLUDE_DIR}" CACHE PATH "Path to search for NetCDF include files")
    endif()

    if(NetCDF_LIBRARY_DIR)
        set(NetCDF_LIBRARY_DIR "${NetCDF_LIBRARY_DIR}" CACHE PATH "Path to search for NetCDF library files")
    endif()

    if ( NOT NetCDF_INCLUDE_DIR )    
      # Search for include files
      # Search order preference:
      #  (1) NetCDF_INCLUDE_DIR - check existence of path AND if the include files exist
      #  (2) NetCDF_DIR/<include>
      #  (3) Default CMake paths See cmake --html-help=out.html file for more information.
      #
      set(netcdf_inc_names "netcdf.h")
      if (NetCDF_INCLUDE_DIR)
  
          if (EXISTS "${NetCDF_INCLUDE_DIR}")
  
              find_path(cdf_test_include_path
                        NAMES ${netcdf_inc_names}
                        HINTS ${NetCDF_INCLUDE_DIR}
                        NO_DEFAULT_PATH)
              if(NOT cdf_test_include_path)
                  message(WARNING "Can not locate ${netcdf_inc_names} in ${NetCDF_INCLUDE_DIR}")
              endif()
              set(NetCDF_INCLUDE_DIR "${cdf_test_include_path}")
  
          else()
              message(WARNING "NetCDF_INCLUDE_DIR=${NetCDF_INCLUDE_DIR} does not exist")
              set(NetCDF_INCLUDE_DIR "NetCDF_INCLUDE_DIR-NOTFOUND")
          endif()
  
      else() 
  
          set(netcdf_inc_suffixes "include")
          if(NetCDF_DIR)
  
              if (EXISTS "${NetCDF_DIR}" )
  
                  find_path(NetCDF_INCLUDE_DIR
                            NAMES ${netcdf_inc_names}
                            HINTS ${NetCDF_DIR}
                            PATH_SUFFIXES ${netcdf_inc_suffixes}
                            NO_DEFAULT_PATH)
  
              else()
                   message(WARNING "NetCDF_DIR=${NetCDF_DIR} does not exist")
                   set(NetCDF_INCLUDE_DIR "NetCDF_INCLUDE_DIR-NOTFOUND")
              endif()    
  
  
          else()
  
              find_path(NetCDF_INCLUDE_DIR
                        NAMES ${netcdf_inc_names}
                        PATH_SUFFIXES ${netcdf_inc_suffixes})
  
          endif()
  
      endif()
  
  
      if ( NOT NetCDF_INCLUDE_DIR )
          message(WARNING "Can not locate NetCDF include directory")
      endif()

    endif(NOT NetCDF_INCLUDE_DIR)  
  
  
    if ( NOT NetCDF_C_LIBRARIES )
      # Search for libraries 
      # Search order preference:
      #  (1) NetCDF_LIBRARY_DIR - check existence of path AND if the include files exist
      #  (2) NetCDF_DIR/<lib,Lib>
      #  (3) Default CMake paths See cmake --html-help=out.html file for more information.
      #
      if (NetCDF_LIBRARY_DIR)
  
          if (EXISTS "${NetCDF_LIBRARY_DIR}")
  
              find_library(_NetCDF_C_LIBRARY
                           NAMES netcdf
                           HINTS ${NetCDF_LIBRARY_DIR}
                           NO_DEFAULT_PATH)
  
          else()
              message(WARNING "NetCDF_LIBRARY_DIR=${NetCDF_LIBRARY_DIR} does not exist")
              set(NetCDF_C_LIBRARY NetCDF_C_LIBRARY-NOTFOUND)
          endif()
  
      else() 
  
          if(NetCDF_DIR)
  
              if (EXISTS "${NetCDF_DIR}" )
  
                  find_library(_NetCDF_C_LIBRARY
                               NAMES netcdf
                               HINTS ${NetCDF_DIR}
                               PATH_SUFFIXES "lib" "Lib"
                               NO_DEFAULT_PATH)
  
              else()
                   message(WARNING "NetCDF_DIR=${NetCDF_DIR} does not exist")
                   set(NetCDF_C_LIBRARY "NetCDF_C_LIBRARY-NOTFOUND")
              endif()    
  
  
          else()
  
              find_library(_NetCDF_C_LIBRARY
                           NAMES netcdf
                           PATH_SUFFIXES ${netcdf_lib_suffixes})
              
          endif()
  
      endif()
  
      # Define the NetCDF library targets
      if ( _NetCDF_C_LIBRARY ) 
  
        if (NetCDF_CONFIG_BINARY)
          execute_process(COMMAND "${NetCDF_CONFIG_BINARY}" "--has-hdf5"
                          RESULT_VARIABLE _ret_code
                          OUTPUT_VARIABLE _stdout
                          ERROR_VARIABLE  _stderr)
                          
          string(REGEX REPLACE "[\n\r ]" "" _hdf5_answer ${_stdout})
          _netcdf_status( "${NetCDF_CONFIG_BINARY} --has-hdf5 returned '${_hdf5_answer}'")
          string(COMPARE EQUAL "${_hdf5_answer}" "yes" _has_hdf5)
          if (${_has_hdf5} ) 
            set(NetCDF_NEEDS_HDF5 TRUE)
            _netcdf_status( "NetCDF requires HDF5")
          else()
  	  set(NetCDF_NEEDS_HDF5 FALSE)
            _netcdf_status( "NetCDF does not require HDF5")
          endif()    
        endif()
  
        # If NetCDF was built with HDF5 then add that to the target properties
        # NetCDF calls HDF5 HL routines and not all HDF5 installations will have 
        # this library. Warn the user if HL is not detected. Use the 
        # HDF5_C_LIBRARIES to define link needs since it will contain hdf5 and
        # hdf5_hl.
        set(_NetCDF_C_LIBRARY_LINK_LIBS)
        if(NetCDF_NEEDS_HDF5) 
  	  find_package(HDF5 QUIET REQUIRED C HL)
  	  if ( HDF5_FOUND )
  	    if ( NOT HDF5_HL_FOUND )
  	      message(WARNING "NetCDF calls the HDF5 HL library but this HDF5 does not have one")
  	    endif() 
  	    set(_NetCDF_C_LIBRARY_LINK_LIBS ${HDF5_C_HL_LIBRARIES} ${HDF5_C_LIBRARIES} z m dl)
  	    list(APPEND NetCDF_INCLUDE_DIRS ${HDF5_INCLUDE_DIRS})
  	  endif()    
        endif()
  
        # Now create the target 'netcdf'
        add_imported_library(netcdf LOCATION "${_NetCDF_C_LIBRARY}" LINK_LANGUAGES "C")
        if ( _NetCDF_C_LIBRARY_LINK_LIBS )
  	set_target_properties(netcdf PROPERTIES
  	                      IMPORTED_LINK_INTERFACE_LIBRARIES "${_NetCDF_C_LIBRARY_LINK_LIBS}")
        endif()
        set(NetCDF_C_LIBRARY netcdf)
        set(NetCDF_C_LIBRARIES ${NetCDF_C_LIBRARY} ${_NetCDF_C_LIBRARY_LINK_LIBS})
	set(NetCDF_C_FOUND TRUE)
  
      endif(_NetCDF_C_LIBRARY)  

  endif(NOT NetCDF_C_LIBRARIES)      


    
endif(NetCDF_C_LIBRARIES AND NetCDF_INCLUDE_DIRS )    

# Large dimension check here
if ( NetCDF_INCLUDE_DIR ) 
       
  set(netcdf_h "${NetCDF_INCLUDE_DIR}/netcdf.h" )
  _netcdf_status( "NetCDF include file ${netcdf_h} will be searched for define values")

  file(STRINGS "${netcdf_h}" netcdf_max_dims_string REGEX "^#define NC_MAX_DIMS")
  string(REGEX REPLACE "[^0-9]" "" netcdf_max_dims "${netcdf_max_dims_string}")

  file(STRINGS "${netcdf_h}" netcdf_max_vars_string REGEX "^#define NC_MAX_VARS")
  string(REGEX REPLACE "[^0-9]" "" netcdf_max_vars "${netcdf_max_vars_string}")

  file(STRINGS "${netcdf_h}" netcdf_max_var_dims_string REGEX "^#define NC_MAX_VAR_DIMS")
  string(REGEX REPLACE "[^0-9]" "" netcdf_max_var_dims "${netcdf_max_var_dims_string}")


  if ( 
       ( (netcdf_max_dims EQUAL 65536)  OR (netcdf_max_dims GREATER 65536)  ) AND
       ( (netcdf_max_vars EQUAL 524288) OR (netcdf_max_vars GREATER 524288) ) AND
       ( (netcdf_max_var_dims EQUAL 8)  OR  (netcdf_max_var_dims GREATER 8) )

     )
       set(NetCDF_LARGE_DIMS TRUE)
  else()
       message(WARNING "The NetCDF found in ${NetCDF_DIR} does not have the correct NC_MAX_DIMS, NC_MAX_VARS and NC_MAX_VAR_DIMS\n"
                       "It may not be compatible with other TPL libraries such MOAB and ExodusII\n" )
       set(NetCDF_LARGE_DIMS FALSE)
  endif()

endif()    

# Determine the version
if ( NOT NetCDF_VERSION )

  if ( NetCDF_CONFIG_BINARY )
    execute_process(COMMAND "${NetCDF_CONFIG_BINARY}" "--version"
                    RESULT_VARIABLE _ret_code
                    OUTPUT_VARIABLE _stdout
                    ERROR_VARIABLE  _stderr)
                          
     string(REGEX REPLACE "[\n\r ]" "" _version_answer "${_stdout}")
     string(REGEX REPLACE "netCDF" "" NetCDF_VERSION "${_version_answer}")
  endif()   

endif()

# Search for the C++ libraries if requested
if ( NetCDF_FIND_COMPONENTS )

  list(FIND NetCDF_FIND_COMPONENTS "CXX" _search_cxx_library)

  if ( ( NOT NetCDF_CXX_LIBRARIES ) AND 
       ( NOT ("${_search_cxx_library}" LESS "0") ) )

    if (NetCDF_LIBRARY_DIR)
  
          if (EXISTS "${NetCDF_LIBRARY_DIR}")
  
              find_library(_NetCDF_CXX_LIBRARY
                           NAMES netcdf_c++
                           HINTS ${NetCDF_LIBRARY_DIR}
                           NO_DEFAULT_PATH)
  
          else()
              message(WARNING "NetCDF_LIBRARY_DIR=${NetCDF_LIBRARY_DIR} does not exist")
              set(NetCDF_CXX_LIBRARY NetCDF_CXX_LIBRARY-NOTFOUND)
          endif()
  
    else() 
  
          if(NetCDF_DIR)
  
              if (EXISTS "${NetCDF_DIR}" )
  
                  find_library(_NetCDF_CXX_LIBRARY
                               NAMES netcdf_c++
                               HINTS ${NetCDF_DIR}
                               PATH_SUFFIXES "lib" "Lib"
                               NO_DEFAULT_PATH)
  
              else()
                   message(WARNING "NetCDF_DIR=${NetCDF_DIR} does not exist")
                   set(NetCDF_CXX_LIBRARY "NetCDF_CXX_LIBRARY-NOTFOUND")
              endif()    
  
  
          else()
  
              find_library(_NetCDF_CXX_LIBRARY
                           NAMES netcdf_c++
                           PATH_SUFFIXES ${netcdf_lib_suffixes})
              
          endif()
  
    endif()

    if ( _NetCDF_CXX_LIBRARY )
      add_imported_library(netcdf_c++ LOCATION "${_NetCDF_C_LIBRARY}" LINK_LANGUAGES "CXX")
      set_target_properties(netcdf_c++ PROPERTIES
                           IMPORTED_LINK_INTERFACE_LIBRARIES "${NetCDF_C_LIBRARY}")
      set(NetCDF_CXX_LIBRARY netcdf_c++)			 
      set(NetCDF_CXX_FOUND TRUE)
      set(NetCDF_CXX_LIBRARIES ${NetCDF_CXX_LIBRARY} ${NetCDF_C_LIBRARIES})
    endif()

  endif()

endif(NetCDF_FIND_COMPONENTS )    



# Summary print out if NOT QUIET
_netcdf_status("NetCDF Package:")
_netcdf_status("              NetCDF_VERSION=${NetCDF_VERSION}")
_netcdf_status("              NetCDF_INCLUDE_DIRS=${NetCDF_INCLUDE_DIRS}")
_netcdf_status("              NetCDF_C_LIBRARIES=${NetCDF_C_LIBRARIES}")
if ( NetCDF_CXX_FOUND )
  _netcdf_status("              NetCDF_CXX_LIBRARIES=${NetCDF_CXX_LIBRARIES}")
endif()

# Send useful message if everything is found
find_package_handle_standard_args(NetCDF DEFAULT_MSG
                                           NetCDF_VERSION
                                           NetCDF_C_LIBRARIES
                                           NetCDF_INCLUDE_DIRS)

mark_as_advanced(
  NetCDF_INCLUDE_DIR
  NetCDF_INCLUDE_DIRS
  NetCDF_C_LIBRARY
  NetCDF_CXX_LIBRARY
  NetCDF_C_LIBRARIES
)
