#==========================================================
# Infinite Value Check
#==========================================================

#' Check Infinite Values
#'
#' Identifies positive and negative infinite values in
#' numeric variables of a dataset.
#'
#' @param data A data.frame.
#'
#' @return A data.frame of class \code{InfiniteCheck}
#'   containing variable-level information about infinite
#'   values.
#'
#' @details
#' Infinite values are detected only in numeric variables.
#' Positive infinity (\code{Inf}) and negative infinity
#' (\code{-Inf}) are reported separately.
#'
#' Missing values (\code{NA}) are not classified as
#' infinite values.
#'
#' Non-numeric variables are retained in the output and
#' reported with zero infinite values.
#'
#' @examples
#' dat <- data.frame(
#'   x = c(1, 2, Inf, 4),
#'   y = c(1, -Inf, 3, 4),
#'   group = c("A", "B", "C", "D")
#' )
#'
#' infinite_check(dat)
#'
#' @export

infinite_check <- function(data) {

  #---------------------------------------------
  # Validation
  #---------------------------------------------
  validate_data(data)

  #---------------------------------------------
  # Analyze one variable
  #---------------------------------------------
  analyse_variable <- function(x) {

    # Non-numeric variables cannot contain
    # numeric infinite values
    if (!is.numeric(x)) {

      return(
        c(
          PositiveInf = 0L,
          NegativeInf = 0L,
          Infinite = 0L
        )
      )
    }

    #-------------------------------------------
    # Positive infinity
    #-------------------------------------------
    positive_inf <- sum(
      is.infinite(x) & x > 0,
      na.rm = TRUE
    )

    #-------------------------------------------
    # Negative infinity
    #-------------------------------------------
    negative_inf <- sum(
      is.infinite(x) & x < 0,
      na.rm = TRUE
    )

    #-------------------------------------------
    # Total infinity
    #-------------------------------------------
    infinite <- positive_inf + negative_inf

    c(
      PositiveInf = as.integer(positive_inf),
      NegativeInf = as.integer(negative_inf),
      Infinite = as.integer(infinite)
    )
  }

  #---------------------------------------------
  # Apply check
  #---------------------------------------------
  diagnostics <- lapply(
    data,
    analyse_variable
  )

  #---------------------------------------------
  # Construct result
  #---------------------------------------------
  result <- data.frame(

    Variable = names(data),

    Numeric = vapply(
      data,
      is.numeric,
      logical(1)
    ),

    PositiveInf = vapply(
      diagnostics,
      function(x) x[["PositiveInf"]],
      integer(1)
    ),

    NegativeInf = vapply(
      diagnostics,
      function(x) x[["NegativeInf"]],
      integer(1)
    ),

    Infinite = vapply(
      diagnostics,
      function(x) x[["Infinite"]],
      integer(1)
    ),

    stringsAsFactors = FALSE,
    check.names = FALSE
  )

  #---------------------------------------------
  # Flag variables containing infinity
  #---------------------------------------------
  result$HasInfinite <- result$Infinite > 0L

  #---------------------------------------------
  # Class
  #---------------------------------------------
  class(result) <- c(
    "InfiniteCheck",
    "data.frame"
  )

  result
}
