include(Colors)

# CMake default message groups
list(APPEND DEFAULT_MISSIVE_MODES "FATAL_ERROR;SEND_ERROR;WARNING;AUTHOR_WARNING;DEPRECATION;NOTICE;STATUS;VERBOSE;DEBUG;TRACE;CHECK_START;CHECK_FAIL;CHECK_PASS")

list(APPEND MISSIVE_MODES ${DEFAULT_MISSIVE_MODES})

function(missive_mode)
  cmake_parse_arguments(ARG "" "NAME;PARENT_MODE;STYLE;APPEND_BEGIN;APPEND_END;APPEND_STYLE;APPEND_STYLE_BEGIN;APPEND_STYLE_END" "" "${ARGN}")
  if(NOT DEFINED ARG_NAME)
    message(FATAL_ERROR "NAME is mandatory !")
  endif()
  string(STRIP ${ARG_NAME} ARG_NAME)
  if(ARG_NAME STREQUAL "")
    message(FATAL_ERROR "NAME cannot be blank !")
  endif()
  string(TOUPPER ${ARG_NAME} UPPER_ARG_NAME)
  if(NOT ${ARG_NAME} STREQUAL ${UPPER_ARG_NAME})
    message(FATAL_ERROR "NAME is not an upper case word !")
  endif()

  list(APPEND MISSIVE_MODES "${MISSIVE_MODES}" "${UPPER_ARG_NAME}")
  list(REMOVE_DUPLICATES MISSIVE_MODES)
  set(MISSIVE_MODES "${MISSIVE_MODES}" PARENT_SCOPE)

  if(NOT DEFINED ARG_PARENT_MODE)
    # If PARENT_MODE is not present it means :
    # -- For new Mode it don't want to be the daughter or some previously defined mode.
    # -- For CMake predefined Mode it's just mean it redefined it.
    list(FIND DEFAULT_MISSIVE_MODES ${ARG_NAME} MODE_FOUND)
    if(${MODE_FOUND} STREQUAL "-1")
      set_property(GLOBAL PROPERTY "${ARG_NAME}_PARENT_MODE" "")
    else()
      set_property(GLOBAL PROPERTY "${ARG_NAME}_PARENT_MODE" "${ARG_NAME}")
    endif()
  else()
    string(STRIP ${ARG_PARENT_MODE} ARG_PARENT_MODE)
    list(FIND MISSIVE_MODES ${ARG_PARENT_MODE} MODE_FOUND)
    if(${MODE_FOUND} STREQUAL "-1")
      list(JOIN MISSIVE_MODES ", " STRING_DEFAULT_MISSIVE_MODES)
      message(FATAL_ERROR "PARENT_MODE must be in this list : ${STRING_DEFAULT_MISSIVE_MODES}.")
    else()
      set_property(GLOBAL PROPERTY "${ARG_NAME}_PARENT_MODE" "${ARG_PARENT_MODE}")
    endif()
  endif()

  if(DEFINED ARG_STYLE)
    set_property(GLOBAL PROPERTY "${ARG_NAME}_STYLE" "${ARG_STYLE}")
  else()
    get_property(STYLE GLOBAL PROPERTY "${ARG_PARENT_MODE}_STYLE")
    set_property(GLOBAL PROPERTY "${ARG_NAME}_STYLE" "${STYLE}")
  endif()

  if(DEFINED ARG_APPEND_BEGIN)
    set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_BEGIN" "${ARG_APPEND_BEGIN}")
  else()
    get_property(APPEND_BEGIN GLOBAL PROPERTY "${ARG_PARENT_MODE}_APPEND_BEGIN")
    set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_BEGIN" "${APPEND_BEGIN}")
  endif()

  if(DEFINED ARG_APPEND_END)
    set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_END" "${ARG_APPEND_END}")
  else()
    get_property(APPEND_END GLOBAL PROPERTY "${ARG_PARENT_MODE}_APPEND_END")
    set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_END" "${APPEND_END}")
  endif()

  if(DEFINED ARG_APPEND_STYLE)
    set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_STYLE_BEGIN" "${ARG_APPEND_STYLE}")
    set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_STYLE_END" "${ARG_APPEND_STYLE}")
  else()
    if(DEFINED ARG_APPEND_STYLE_BEGIN)
      set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_STYLE_BEGIN" "${ARG_APPEND_STYLE_BEGIN}")
    else()
      get_property(APPEND_STYLE_BEGIN GLOBAL PROPERTY "${ARG_PARENT_MODE}_APPEND_STYLE_BEGIN")
      set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_STYLE_BEGIN" "${APPEND_STYLE_BEGIN}")
    endif()
    if(DEFINED ARG_APPEND_STYLE_END)
      set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_STYLE_END" "${ARG_APPEND_STYLE_END}")
    else()
      get_property(APPEND_STYLE_END GLOBAL PROPERTY "${ARG_PARENT_MODE}_APPEND_STYLE_END")
      set_property(GLOBAL PROPERTY "${ARG_NAME}_APPEND_STYLE_END" "${APPEND_STYLE_END}")
    endif()
  endif()
endfunction()

# Restore the default MODES
function(restore_to_default MODE)
  list(FIND DEFAULT_MISSIVE_MODES ${MODE} MODE_FOUND)
  if(${MODE_FOUND} STREQUAL "-1")
    list(JOIN MISSIVE_MODES ", " STRING_DEFAULT_MISSIVE_MODES)
    message(WARNING "Mode ${MODE} is not a CMake Mode : ${STRING_DEFAULT_MISSIVE_MODES}")
  else()
    missive_mode(NAME ${MODE})
  endif()
endfunction()

function(restore_to_default_all_modes)
  foreach(MODE IN LISTS DEFAULT_MISSIVE_MODES)
    restore_to_default(${MODE})
  endforeach()
endfunction()

# This call is to register the default CMake modes
restore_to_default_all_modes()

set(Default "\${Default}")

function(missive)
  if(${ARGC} STREQUAL "1")
    string(REPLACE "\${Default}" "${Reset}" ARGV ${ARGV})
    message("${ARGV}")
    return()
  endif()

  list(GET ARGV 0 MISSIVE_MODE)
  list(FIND MISSIVE_MODES ${MISSIVE_MODE} MISSIVE_MODE_FOUND)
  if(NOT ${MISSIVE_MODE_FOUND} STREQUAL "-1")
    list(REMOVE_AT ARGV 0)
    list(JOIN ARGV ";" STRING_ARGV)
    get_property(PARENT_MODE GLOBAL PROPERTY "${MISSIVE_MODE}_PARENT_MODE")
    get_property(APPEND_BEGIN GLOBAL PROPERTY "${MISSIVE_MODE}_APPEND_BEGIN")
    get_property(APPEND_END GLOBAL PROPERTY "${MISSIVE_MODE}_APPEND_END")
    get_property(APPEND_STYLE_BEGIN GLOBAL PROPERTY "${MISSIVE_MODE}_APPEND_STYLE_BEGIN")
    get_property(APPEND_STYLE_END GLOBAL PROPERTY "${MISSIVE_MODE}_APPEND_STYLE_END")
    get_property(STYLE GLOBAL PROPERTY "${MISSIVE_MODE}_STYLE")
    if(NOT STRING_ARGV STREQUAL "")
      string(REPLACE "\${Default}" "${${STYLE}}" STRING_ARGV ${STRING_ARGV})
    endif()
    if(${ARGV0} STREQUAL "FATAL_ERROR" OR ${ARGV0} STREQUAL "SEND_ERROR")
      set(DEFAULT_COLOR ${Red})
    elseif(${ARGV0} STREQUAL "WARNING" OR ${ARGV0} STREQUAL "AUTHOR_WARNING")
      set(DEFAULT_COLOR ${Yellow})
    else()
      set(DEFAULT_COLOR ${Reset})
    endif()
    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21)
      message(${PARENT_MODE} ${${APPEND_STYLE_BEGIN}} ${APPEND_BEGIN} ${DEFAULT_COLOR} ${${STYLE}} ${STRING_ARGV} ${DEFAULT_COLOR} ${${APPEND_STYLE_END}} ${APPEND_END}${Reset}${DEFAULT_COLOR})
    else()
      message(${PARENT_MODE} ${${APPEND_STYLE_BEGIN}} ${APPEND_BEGIN} ${DEFAULT_COLOR} ${${STYLE}} ${STRING_ARGV} ${DEFAULT_COLOR} ${${APPEND_STYLE_END}} ${APPEND_END}${Reset}${DEFAULT_COLOR}${Reset})
    endif()
  else()
    message(${ARGV})
  endif()
endfunction()

if(NOT NOT_REDEFINE_DEFAULT_MODES)
  missive_mode(NAME "FATAL_ERROR" STYLE "BoldRed")
  missive_mode(NAME "SEND_ERROR" STYLE "BoldRed")
  missive_mode(NAME "WARNING" STYLE  "BoldYellow")
  missive_mode(NAME "AUTHOR_WARNING" STYLE  "BoldYellow")
  missive_mode(NAME "DEPRECATION" STYLE  "BoldWhite")
endif()

if(NOT NOT_DEFINE_MODES)
  missive_mode(NAME "ERROR" STYLE "BoldRed" APPEND_BEGIN "!! " APPEND_END " !!" APPEND_STYLE "BoldRed")
  missive_mode(NAME "WARN" STYLE "BoldYellow" APPEND_BEGIN "## " APPEND_END " ##" APPEND_STYLE "BoldYellow")
  missive_mode(NAME "INFO" STYLE "BoldGreen" APPEND_BEGIN "** " APPEND_END " **" APPEND_STYLE "BoldGreen")
  missive_mode(NAME "NOTE" STYLE "BoldMagenta" APPEND_BEGIN "-- " APPEND_END " --" APPEND_STYLE "BoldMagenta")
  missive_mode(NAME "TO_REMOVE" PARENT_SCOPE "DEBUG" APPEND_BEGIN "-- " APPEND_END " --" APPEND_STYLE "Red" STYLE "Red")
  missive_mode(NAME "TO_ADD" PARENT_SCOPE "DEBUG" APPEND_BEGIN "++ " APPEND_END " ++" APPEND_STYLE "Green" STYLE "Green")
endif()
