#
#   Copyright 2019 University of Sheffield
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http:#www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

cmake_policy(SET CMP0007 NEW)

if(NOT DEFINED git_state_file)
  set(git_state_file "${CMAKE_CURRENT_BINARY_DIR}/git.state")
endif()
if(NOT DEFINED git_state_src)
  set(git_state_src "${CMAKE_CURRENT_LIST_DIR}/gitstate")
endif()
if(NOT DEFINED git_state_tgt)
  if(NOT DEFINED GITCPP_PATH)
    message(FATAL_ERROR "GITCPP_PATH must be set")
  endif(NOT DEFINED GITCPP_PATH)

  string(REGEX REPLACE "\\.[^.]*$" "" _filepath_noext ${GITCPP_PATH})
  set(git_state_tgt "${PROJECT_SOURCE_DIR}/${_filepath_noext}")
endif()

function(WriteGitVersionSrc _filepath)
  ReadGitStatefile(git_sha git_tag git_dirty)

  execute_process(COMMAND
    cp -u "${git_state_src}.hpp.in" "${git_state_tgt}.hpp" 
    RESULT_VARIABLE res
    ERROR_QUIET)
  if(NOT res EQUAL 0)
    message(FATAL_ERROR "Failed to update ${git_state_tgt}.hpp")
  endif(NOT res EQUAL 0)
  
  configure_file("${git_state_src}.cpp.in" "${git_state_tgt}.cpp" @ONLY)

endfunction(WriteGitVersionSrc)


function(ReadGitStatefile hash tag dirty)

  file(STRINGS "${git_state_file}" file_contents)
  list(GET file_contents 1 _hash)
  list(GET file_contents 2 _tag)
  list(GET file_contents 3 _dirty)

  set(${hash} "${_hash}" PARENT_SCOPE)
  set(${tag} "${_tag}" PARENT_SCOPE)
  set(${dirty} "${_dirty}" PARENT_SCOPE)

endfunction(ReadGitStatefile)


function(WriteGitStatefile working_dir)

  GetGitStateString(${working_dir} git_state)

  file(WRITE "${git_state_file}" "${git_state}")

endfunction(WriteGitStatefile)

function(GetGitStateString _working_dir _state_string)

  GetGitState(${_working_dir} hash tag dirty)

  set(info_string "This is a git state file generated by cmake - do not edit")
  set(${_state_string} "${info_string}\n${hash}\n${tag}\n${dirty}\n" PARENT_SCOPE)

endfunction(GetGitStateString)

function(GetGitState _working_dir _hashvar _tagvar _dirty)

  set(${_hashvar} "" PARENT_SCOPE)
  set(${_tagvar} "" PARENT_SCOPE)
  set(${_dirty} "false" PARENT_SCOPE)

  if(NOT GIT_FOUND)
    find_package(Git QUIET)
  endif()
  if(NOT GIT_FOUND)
    message(STATUS "Not a git repository but no release version.info")
    set(${_hashvar} "GIT-NOTFOUND")
    return()
  endif()

  execute_process(COMMAND
    "${GIT_EXECUTABLE}" describe --exact-match --tags HEAD
    WORKING_DIRECTORY "${_working_dir}"
    RESULT_VARIABLE res
    OUTPUT_VARIABLE tag
    ERROR_QUIET
    OUTPUT_STRIP_TRAILING_WHITESPACE)
  if(res EQUAL 0)
    set(${_tagvar} "${tag}" PARENT_SCOPE)
  endif()

  execute_process(COMMAND
    "${GIT_EXECUTABLE}" rev-parse --verify HEAD
    WORKING_DIRECTORY "${_working_dir}"
    RESULT_VARIABLE res
    OUTPUT_VARIABLE hash
    ERROR_QUIET
    OUTPUT_STRIP_TRAILING_WHITESPACE)
  if(res EQUAL 0)
    set(${_hashvar} "${hash}" PARENT_SCOPE)
  else(res EQUAL 0)
    message(WARNING "Error querying git hash:\n${_working_dir}")
  endif(res EQUAL 0)

  execute_process(COMMAND
    "${GIT_EXECUTABLE}" status --porcelain
      WORKING_DIRECTORY "${_working_dir}"
      RESULT_VARIABLE res
      OUTPUT_VARIABLE out
      ERROR_QUIET
      OUTPUT_STRIP_TRAILING_WHITESPACE)
  if(NOT res EQUAL 0)
    message(WARNING "Error querying repo change state")
  endif(NOT res EQUAL 0)
  
  if("${out}" STREQUAL "")
    set(${_dirty} "false" PARENT_SCOPE)
  else("${out}" STREQUAL "")
    set(${_dirty} "true" PARENT_SCOPE)
  endif("${out}" STREQUAL "")

endfunction(GetGitState)

function(EnableMonitoring)
  message(STATUS "Setting up git monitoring")
  add_custom_target(CheckGitVersion ALL
    DEPENDS ${git_state_tgt}.fake)

  add_custom_command(
    OUTPUT
      ${git_state_tgt}.fake
      ${git_state_tgt}.cpp
    COMMAND
      ${CMAKE_COMMAND}
      -DGIT_FUNCTION=DoMonitoring
      -DGIT_WORKING_DIR=${CMAKE_CURRENT_SOURCE_DIR}
      -Dgit_state_src=${git_state_src}
      -Dgit_state_tgt=${git_state_tgt}
      -P "${CMAKE_CURRENT_LIST_FILE}")
endfunction(EnableMonitoring)

Function(CheckGitStatus _working_dir _state_changed)

  GetGitStateString("${_working_dir}" new_state)

  if(EXISTS "${git_state_tgt}.cpp")  
    if(EXISTS "${git_state_file}")
      file(READ "${git_state_file}" old_state)
      if(old_state STREQUAL new_state)
        set("${_state_changed}" "false" PARENT_SCOPE)
        return()
      endif(old_state STREQUAL new_state)
    endif(EXISTS "${git_state_file}")
  endif(EXISTS "${git_state_tgt}.cpp")  

  file(WRITE "${git_state_file}" "${new_state}")
  set(${_state_changed} "true" PARENT_SCOPE)

EndFunction(CheckGitStatus)
  

function(Main)
  if(NOT GIT_FUNCTION STREQUAL DoMonitoring)
    EnableMonitoring()
    set(GIT_WORKING_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
  endif(NOT GIT_FUNCTION STREQUAL DoMonitoring)
  CheckGitStatus("${GIT_WORKING_DIR}" changed)
  if(changed)
    WriteGitVersionSrc("${git_state_tgt}")
  endif(changed)

  execute_process(COMMAND
    cp -u "${git_state_src}.hpp.in" "${git_state_tgt}.hpp" 
    RESULT_VARIABLE res
    ERROR_QUIET)
  if(NOT res EQUAL 0)
    message(FATAL_ERROR "Failed to update ${git_state_tgt}.hpp")
  endif(NOT res EQUAL 0)

endfunction(Main)

Main()
