##############################################################################################
#' @title Unit test of wrap.ucrt.dp0p.R,
#' wrapper for computing individual measurement uncertainty for calibrated data at native
#' frequency (NEON L0' data).

#' @description
#' Run unit tests for wrap.ucrt.dp0p.R. The input json for the test is ucrt-coef-fdas-input.json.
#' The tests include positive and negative scenarios.
#' The positive test is for a case when all the params to the function are valid
#' The negative tests are when a param(s) is empty or does not have invalid values

#' Refer to wrap.ucrt.dp0p.R for the details of the function.

#' @param data Data frame of L0 data. Must include POSIXct time variable readout_time.
#' @param ParaUcrt A data frame of the variables for which individual measurement uncertainty is
#' to be calculated. Columns include:\cr
#' \code{var} Character. The variable in data for which to compute uncertainty \cr
#' \code{typeFdas} A single character indicating the type of measurement made by NEON's field
#' data acquisision system (GRAPE). Acceptable values are "R" for resistance measurements, "V"
#' for voltage measurements, or NA (no quotes) for measurements in which FDAS uncertainty does
#' not apply (e.g. digital L0 output) \cr
#' \code{FuncUcrt} A character string indicating the individual measurement (calibration) uncertainty
#' function within the NEONprocIS.cal package that should be used. Note that this does not include
#' FDAS uncertainty. For most NEON data products, this will be "def.ucrt.meas.cnst". Note that any
#' alternative function must accept arguments "data", "infoCal", and "log", even if they are unused
#' in the function, and must return a data frame, one column of which is labeled "ucrtMeas" which
#' corresponds to the individual measurement uncertainty for the variable. Input "data" to the function
#' is an array of the data for the applicable term. Input "infoCal" is a data frame of calibration
#' information (including uncertainty) as returned from NEONprocIS.cal::def.read.cal.xml.
#' If no calibration files are associated with the term, infoCal would be passed in to the function as
#' NULL. Input "log" is a logger object as generated by NEONprocIS.base::def.log.init and used in
#' this script to generate hierarchical logging.\cr
#' @param ucrtCoefFdas A data frame of FDAS uncertainty coefficients, as read by
#' NEONprocIS.cal::def.read.ucrt.coef.fdas. Columns include:\cr
#' \code{Name} Character. Name of the coefficient.\cr
#' \code{Value} Character. Value of the coefficient.\cr
#' \code{.attrs} Character. Relevant attribute (i.e. units)\cr
#' Defaults to NULL, in which case no variables in ParaUcrt may indicate that FDAS uncertainty
#' applies.
#' @param calSlct A named list of data frames, list element corresponding to the variables in
#' ParaUcrt. The data frame in each list element holds information about the calibration files and
#' time periods that apply to the variable, as returned from NEONprocIS.cal::def.cal.slct.
#' See documentation for that function. Assign NULL to list elements (variables) for which calibration
#' information is not applicable (i.e. a function other than def.ucrt.meas.cnst is used to compute its
#' uncertainty).
#' @param DirCal Character string. Relative or absolute path (minus file name) to the main calibration
#' directory. Nested within this directory are directories for each variable in calSlct, each holding
#' calibration files for that variable. Defaults to "./"
#' @param mappNameVar A data frame with in/out variable name mapping as produced by
#' NEONprocIS.base::def.var.mapp.in.out. See documentation for that function. If input (default is NULL),
#' output variable names will be appended as prefixes to the column names in each output data frame.
#' @param log A logger object as produced by NEONprocIS.base::def.log.init to produce structured log
#' output. Defaults to NULL, in which the logger will be created and used within the function.

#' @return A named list, each element corresponding to those in ParaUcrt$var and holding a data
#' frame of uncertainty data. Note that each row in each data frame corresponds to the times in
#' data$readout_time, but the variable readout_time is not included in the output. One column
#' in each data frame is labeled ucrtComb, corresponding to the combined measurement uncertainty
#' of the individual measurements and FDAS (if applicable). If FDAS uncertainty does not apply,
#' ucrtComb is simply a copy of ucrtMeas. \cr

#' @references Currently none
#' License: (example) GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007

#' @keywords Currently none

#' @examples Currently none

#' @seealso Currently none

#' @examples
#' To run with testthat:
#' devtools::test(pkg="<path>/NEON-IS-data-processing/pack/NEONprocIS.cal")
#' an example, devtools::test(pkg="C:/projects/NEON-IS-data-processing/pack/NEONprocIS.cal")

# changelog and author contributions / copyrights
#   Mija Choi (2020-08-05)
#     Original Creation
#   Mija Choi (2020-09-24)
#     adjusted calls to uncertainty funcs to conform to new generic format
#     This includes inputting the entire data frame, the
#     variable to be generate uncertainty info for, and the (unused) argument calSlct
#     Changed input to also specify the FDAS uncertainty function to use, instead of
#     determining it within the code
#     Changed input argument ParaUcrt to FuncUcrt, and changed input column names to support above changes
##############################################################################################
# Define test context
context("\n                       Unit test of wrap.ucrt.dp0p.R\n")

# Unit test of wrap.ucrt.dp0p.R
test_that("Unit test of wrap.ucrt.dp0p.R", {
   # The input json has Name, Value, and .attrs
   
   testDir = "testdata/"
   testJson = "ucrt-coef-fdas-input.json"
   testJsonPath <- paste0(testDir, testJson)
   
   ucrtCoefFdas <- NEONprocIS.cal::def.read.ucrt.coef.fdas (NameFile=testJsonPath,log=NULL)
   
   testFileCal = "calibration.xml"
   testFileCalPath <- paste0(testDir, testFileCal)
   
   infoCal <- NEONprocIS.cal::def.read.cal.xml (testFileCalPath, Vrbs = TRUE)
   
   DirCal = "./calibrations"
   NameVarExpc = character(0)
   TimeBgn = base::as.POSIXct('2019-06-12 00:10:20', tz = 'GMT')
   TimeEnd = base::as.POSIXct('2019-07-07 00:18:28', tz = 'GMT')
   
   varCal <- base::unique(c(NameVarExpc, base::dir(DirCal)))
   
   values <- c(10, 13)
   
   NumDayExpiMax <- data.frame(var = varCal,NumDayExpiMax = values,stringsAsFactors = FALSE)
   #
   calSlct <- NEONprocIS.cal::wrap.cal.slct (
      DirCal = DirCal,
      NameVarExpc = character(0),
      TimeBgn = TimeBgn,
      TimeEnd = TimeEnd,
      NumDayExpiMax = NumDayExpiMax,
      log = NULL
   )
   
   # Happy path 1 - test calibration in resistance
   #
   testData = "L0_data_resistance.csv"
   testDataPath <- paste0(testDir, testData)
   
   data <- read.csv(testDataPath, sep = ",", header = TRUE)
   
   data$readout_time <- as.POSIXct(data$readout_time, tz = 'GMT')
   data = data.frame(data)
   
   FuncUcrtMeas = "def.ucrt.meas.cnst"
   FuncUcrtFdas = "def.ucrt.fdas.rstc.poly"
   var = c("resistance")
   
   FuncUcrt <-
      data.frame(
         var = var,
         FuncUcrtMeas = FuncUcrtMeas,
         FuncUcrtFdas = FuncUcrtFdas,
         stringsAsFactors = FALSE
      )
   
   nameVarIn = c('resistance')
   nameVarOut = c('resistance')
   nameVarDfltSame = c('resistance')
   
   mappNameVar <-
      base::data.frame(
         nameVarIn = nameVarIn,
         nameVarOut = nameVarOut,
         stringsAsFactors = FALSE
      )
   newVar <- nameVarDfltSame[!(nameVarDfltSame %in% nameVarIn)]
   mappNameVar <-
      base::rbind(
         mappNameVar,
         base::data.frame(
            nameVarI = newVar,
            nameVarOut = newVar,
            stringsAsFactors = FALSE
         )
      )
   
   wudp0pList_returned <-
      NEONprocIS.cal::wrap.ucrt.dp0p (
         data,
         FuncUcrt,
         ucrtCoefFdas = ucrtCoefFdas,
         calSlct = calSlct,
         mappNameVar = mappNameVar
      )
   
   elementsList = c(
      "resistance_ucrtMeas",
      "resistance_raw",
      "resistance_dervCal",
      "resistance_ucrtFdas",
      "resistance_ucrtComb",
      "resistance_ucrtExpn"
   )
   
   expect_true ((is.list(wudp0pList_returned)) &&
               !(is.null(wudp0pList_returned)) &&
               all(names(wudp0pList_returned$resistance) == elementsList))
   
   # Happy path 2 - test calibration in voltage
   #
   testData = "L0_data_voltage.csv"
   testDataPath <- paste0(testDir, testData)
   
   data <- read.csv(testDataPath, sep = ",", header = TRUE)
   
   data$readout_time <- as.POSIXct(data$readout_time, tz = 'GMT')
   data = data.frame(data)
   
   FuncUcrtMeas = "def.ucrt.meas.cnst"
   FuncUcrtFdas = "def.ucrt.fdas.volt.poly"
   var = c("voltage")
   
   FuncUcrt <-
      data.frame(
         var = var,
         FuncUcrtMeas = FuncUcrtMeas,
         FuncUcrtFdas = FuncUcrtFdas,
         stringsAsFactors = FALSE
      )
   
   nameVarIn = c('voltage')
   nameVarOut = c('voltage')
   nameVarDfltSame = c('voltage')
   
   mappNameVar <-
      base::data.frame(
         nameVarIn = nameVarIn,
         nameVarOut = nameVarOut,
         stringsAsFactors = FALSE
      )
   newVar <- nameVarDfltSame[!(nameVarDfltSame %in% nameVarIn)]
   mappNameVar <-
      base::rbind(
         mappNameVar,
         base::data.frame(
            nameVarIn = newVar,
            nameVarOut = newVar,
            stringsAsFactors = FALSE
         )
      )
   
   wudp0pList_returned <-
      NEONprocIS.cal::wrap.ucrt.dp0p (
         data,
         FuncUcrt,
         ucrtCoefFdas = ucrtCoefFdas,
         calSlct = calSlct,
         mappNameVar = mappNameVar
      )
   
   elementsList = c(
      "voltage_ucrtMeas",
      "voltage_raw",
      "voltage_dervCal",
      "voltage_ucrtFdas",
      "voltage_ucrtComb",
      "voltage_ucrtExpn"
   )
   
   expect_true ((is.list(wudp0pList_returned)) &&
               !(is.null(wudp0pList_returned)) &&
               all(names(wudp0pList_returned$voltage) == elementsList))
   
   #  Happy path 3 - calibration xml selected has the time expired
   #  All the rest of test data remain the same as in happy path 2, test in voltage
   #
   TimeBgn = base::as.POSIXct('2020-06-12 00:10:20', tz = 'GMT')
   TimeEnd = base::as.POSIXct('2020-07-07 00:18:28', tz = 'GMT')
   
   #
   calSlct <- NEONprocIS.cal::wrap.cal.slct (
      DirCal = DirCal,
      NameVarExpc = character(0),
      TimeBgn = TimeBgn,
      TimeEnd = TimeEnd,
      NumDayExpiMax = NumDayExpiMax,
      log = NULL
   )
   
   wudp0pList_returned <-
      NEONprocIS.cal::wrap.ucrt.dp0p (
         data,
         FuncUcrt,
         ucrtCoefFdas = ucrtCoefFdas,
         calSlct = calSlct,
         mappNameVar = mappNameVar
      )
   
   elementsList = c(
      "voltage_ucrtMeas",
      "voltage_raw",
      "voltage_dervCal",
      "voltage_ucrtFdas",
      "voltage_ucrtComb",
      "voltage_ucrtExpn"
   )
   
   expect_true ((is.list(wudp0pList_returned)) &&
               !(is.null(wudp0pList_returned)) &&
               all(names(wudp0pList_returned$voltage) == elementsList) &&
               is.na(wudp0pList_returned$voltage$voltage_ucrtMeas)
   )
   
   # Negative test #1: ucrtCoefFdas and mappNameVar are not passed to wrap.ucrt.dp0p.R
   
   wudp0pList_returned <- NEONprocIS.cal::wrap.ucrt.dp0p (data,
                                                          FuncUcrt,
                                                          calSlct = calSlct)
   expect_true ((is.list(wudp0pList_returned)) &&
               (is.null(wudp0pList_returned$voltage$voltage_ucrtMeas)))
})
