context("CSTools::CST_Calibration tests")

##############################################

# dat
library(zeallot)
c(exp, obs) %<-% lonlat_temp

# dat1
exp1 <- list(data = array(1:20, dim = c(time = 20)))
class(exp1) <- 's2dv_cube'
obs1 <- list(data = array(1:20, dim = c(time = 20)))
class(obs1) <- 's2dv_cube'
exp1_2 <- list(data = array(1:20, dim = c(20)))
class(exp1_2) <- 's2dv_cube'
obs1_2 <- list(data = array(1:20, dim = c(20)))
class(obs1_2) <- 's2dv_cube'
exp_cor1 <- list(data = array(1:20, dim = c(20)))
class(exp_cor1) <- 's2dv_cube'

# dat2
exp2 <- exp
exp2$data[1, 2, 1, 1, 1, 1] <- NA
obs2 <- obs
obs2$data[1, 1, 2, 1, 1, 1] <- NA

# dat3
set.seed(1)
exp3 <- array(rnorm(400), dim = c(member = 10, sdate = 10, lat = 2, dataset = 2))
exp3_1 <- array(rnorm(20), dim = c(member = 10, sdate = 10, lat = 2))
set.seed(2)
obs3 <- array(rnorm(60), dim = c(sdate = 10, lat = 2, dataset = 3))
obs3_1 <- array(rnorm(20), dim = c(sdate = 10, lat = 2))
set.seed(3)
exp_cor3 <- array(rnorm(20), dim = c(member = 10, sdate = 10, lat = 2))

# dat4
set.seed(1)
exp4 <- array(rnorm(200), dim = c(member = 10, sdate = 20))
exp4_1 <- array(exp4, dim = c(member = 10, sdate = 20, dataset = 1))
exp4_2 <- exp4_1
exp4_2[1] <- NA
set.seed(2)
obs4 <- array(rnorm(200), dim = c(sdate = 20))
obs4_1 <- array(obs4, dim = c(sdate = 20, dataset = 1))
obs4_2 <- obs4_1
obs4_2[c(1, 3)] <- NA
set.seed(3)
exp_cor4 <- array(rnorm(200), dim = c(member = 10, sdate = 20))

##############################################

test_that("1. Input checks", {
  # s2dv_cube
  expect_error(
    CST_Calibration(exp = 1),
    paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ",
         "as output by CSTools::CST_Load.")
  )
  expect_error(
    CST_Calibration(exp = exp, obs = 1),
    paste0("Parameter 'exp' and 'obs' must be of the class 's2dv_cube', ",
             "as output by CSTools::CST_Load.")
  )
  expect_error(
    CST_Calibration(exp = exp1),
    'argument "obs" is missing, with no default'
  )
  expect_error(
    CST_Calibration(exp = exp1, obs = obs1, exp_cor = 1),
    paste0("Parameter 'exp', 'obs' and 'exp_cor' must be of the class 's2dv_cube', ",
             "as output by CSTools::CST_Load.")
  )
  # exp and obs
  expect_error(
    CST_Calibration(exp = exp1_2, obs = obs1),
    "Parameter 'exp' must have dimension names."
  )
  expect_error(
    CST_Calibration(exp = exp1, obs = obs1_2),
    "Parameter 'obs' must have dimension names."
  )
  expect_warning(
    CST_Calibration(exp = exp2, obs = obs2),
    "Parameter 'exp' contains NA values."
  )
  expect_warning(
    CST_Calibration(exp = exp, obs = obs2),
    "Parameter 'obs' contains NA values."
  )
  expect_warning(
    CST_Calibration(exp = exp2, obs = obs2),
    "Parameter 'obs' contains NA values",
    "Parameter 'exp' contains NA values."
    )
  # exp_cor
  expect_error(
    CST_Calibration(exp = exp1, obs = obs1, exp_cor = exp_cor1, sdate_dim = 'time'),
    "Parameter 'exp_cor' must have dimension names."
  )
  # sdate_dim, memb_dim
  expect_error(
    CST_Calibration(exp = exp1, obs = obs1, sdate_dim = 1),
    paste0("Parameter 'sdate_dim' should be a character string indicating the",
           "name of the dimension where start dates are stored in 'exp'.")
  )
  expect_error(
    CST_Calibration(exp = exp, obs = obs, sdate_dim = 'time'),
    paste0("Parameter 'exp' requires 'sdate_dim' and 'memb_dim' dimensions.")
  )
  expect_error(
    Calibration(exp = array(1:20, dim = c(time = 1, member = 1)), 
                obs = array(1:20, dim = c(time = 2, member = 1)), sdate_dim = 'time'),
    paste0("Parameter 'exp' and 'obs' must have same length of all dimensions", 
           " except 'memb_dim' and 'dat_dim'.")
  )
  expect_error(
    CST_Calibration(exp = exp1, obs = obs1, sdate_dim = 'time'),
    paste0("Parameter 'exp' requires 'sdate_dim' and 'memb_dim' dimensions.")
  )
  ## dat_dim
  expect_error(
    Calibration(exp = exp3, obs = obs3, dat_dim = 1),
    paste0("Parameter 'dat_dim' must be a character string.")
  )
  ## exp, obs, and exp_cor (2)
  expect_error(
    Calibration(exp = array(1:6, c(sdate = 3, member = 2, dataset = 2, lon = 1)), 
                   obs = array(1:6, c(sdate = 3, member = 1, dataset = 3, lon = 2)), 
                   dat_dim = 'dataset'),
    paste0("Parameter 'exp' and 'obs' must have same length of all dimensions", 
           " except 'memb_dim' and 'dat_dim'.")
  )
  expect_error(
    Calibration(exp = array(1:6, c(sdate = 3, member = 2, dataset = 2, lon = 1)), 
                   obs = array(1:6, c(sdate = 3, member = 1, dataset = 3, lon = 1)),
                   exp_cor = array(1:6, c(sdate = 3, member = 1, dataset = 3, lon = 1)), 
                   dat_dim = 'dataset'),
    paste0("If parameter 'exp_cor' has dataset dimension, it must be", 
           " equal to dataset dimension of 'exp'.")
  )
  expect_error(
    Calibration(exp = array(1:6, c(sdate = 3, member = 2, dataset = 2, lon = 1)), 
                obs = array(1:6, c(sdate = 3, member = 1, dataset = 3, lon = 1)),
                exp_cor = array(1:6, c(sdate = 3, member = 1, lon = 1)), dat_dim = 'dataset'),
    paste0("Parameter 'exp' and 'exp_cor' must have the same length of ",
           "all dimensions except 'dat_dim' if there is ",
           "only one reference dataset.")
  )
  ## na.rm
  expect_warning(
    CST_Calibration(exp = exp, obs = obs, na.rm = c(T,F)),
    "Paramter 'na.rm' has length greater than 1, and only the fist element is used."
  )
  # ncores
  expect_error(
    CST_Calibration(exp = exp, obs = obs, ncores = TRUE),
    "Parameter 'ncores' must be either NULL or a positive integer."
  )
})
##############################################

test_that("2. Output checks: dat1", {
  expect_error(
    CST_Calibration(exp = 1),
    "Parameter 'exp' and 'obs' must be of the class 's2dv_cube', "
  )
  expect_error(
    CST_Calibration(obs = 1),
    c("argument \"exp\" is missing, with no default")
  )
  cal <- CST_Calibration(exp = exp, obs = obs)
  expect_equal(
    length(cal), 
    9
  )
  expect_equal(
    as.numeric(dim(cal$data)),
    as.numeric(dim(exp$data))
  )
  expect_equal(
    cal$lat, 
    exp$lat
  )
  expect_equal(
    cal$lat, 
    obs$lat
  )
  expect_equal(
    cal$lon, 
    exp$lon
  )
  expect_equal(
    cal$lon, 
    obs$lon
  )
  expect_equal(
    dim(cal$data),
    c(dataset = 1, member =15, sdate = 6, ftime = 3, lat = 22, lon = 53)
  )
  expect_equal(
    as.vector(cal$data)[1:5],
     c(280.8678, 281.1716, 280.3992, 282.6034, 281.6749),
     tolerance = 0.0001
  )
})  

##############################################

test_that("3. Output checks: dat3", {
  expect_equal(
    dim(Calibration(exp3, obs3, memb_dim = 'member', dat_dim = 'dataset')),
    c(member = 10, sdate = 10, lat = 2, nexp = 2, nobs = 3)
  )
  expect_equal(
    dim(Calibration(exp3, obs3, exp_cor3, memb_dim = 'member', dat_dim = 'dataset')),
    c(member = 10, sdate = 10, lat = 2, nexp = 2, nobs = 3)
  )
  expect_equal(
    as.vector(Calibration(exp3, obs3, memb_dim = 'member', dat_dim = 'dataset'))[5:10],
    c(0.5462052, -0.6882557, 0.7157284, 0.9850565, 0.8105716, -0.1353346),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(Calibration(exp3, obs3, exp_cor3, memb_dim = 'member', dat_dim = 'dataset'))[5:10],
    c(0.6156021, 0.4377728, 0.4971288, 1.6040794, -0.9029668, 1.7659136),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(Calibration(exp3[, , , 1], obs3[, , 1], memb_dim = 'member'))[1:5],
    as.vector(Calibration(exp3, obs3, memb_dim = 'member', dat_dim = 'dataset')[, , , 1, 1][1:5]),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(Calibration(exp3[, , , 1], obs3[, , 1], exp_cor3, memb_dim = 'member'))[1:5],
    as.vector(Calibration(exp3, obs3, exp_cor3, memb_dim = 'member', dat_dim = 'dataset')[, , , 1, 1][1:5])
  )
  expect_equal(
    as.vector(Calibration(exp3, obs3, exp_cor3, memb_dim = 'member', dat_dim = 'dataset', na.rm = TRUE))[1:5],
    c(-0.6271675, 0.0914192, 0.6832362, -0.8313392, 0.6156021),
    tolerance = 0.0001
  )
})

##############################################
test_that("4. Output checks: dat4", {
  expect_equal(
    dim(Calibration(exp4, obs4)),
    c(member = 10, sdate = 20)
  )
  expect_equal(
    as.vector(Calibration(exp4, obs4))[1:5],
    c(-0.6372505, 0.3261267, -0.8860036, 2.0048627, 0.4995904),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(Calibration(exp4, obs4, exp_cor4))[1:5],
    c(-0.8226572, -0.0265896, 0.6290395, -1.0488434, 0.5541127),
    tolerance = 0.0001
  )
  expect_equal(
    dim(Calibration(exp4_1, obs4_1, dat_dim = 'dataset')),
    c(member = 10, sdate = 20, nexp = 1, nobs = 1)
  )
  expect_equal(
    as.vector(Calibration(exp4_1, obs4_1, dat_dim = 'dataset')),
    as.vector(Calibration(exp4, obs4)),
    tolerance = 0.0001
  )
  expect_equal(
    as.vector(Calibration(exp4_1, obs4_1, exp_cor4, dat_dim = 'dataset')),
    as.vector(Calibration(exp4, obs4, exp_cor4)),
    tolerance = 0.0001
  )
  expect_equal(
    suppressWarnings(
      as.vector(Calibration(exp4_1, obs4_2, dat_dim = 'dataset', na.rm = FALSE))
    ),
    rep(as.numeric(NA), 200)
  )
  expect_equal(
    suppressWarnings(
      as.vector(Calibration(exp4_1, obs4_2, dat_dim = 'dataset', na.rm = T))[5:10]
    ),
    c(0.4343443, -0.9018916, 0.6178439, 0.9093767, 0.7205064, -0.3033850),
    tolerance = 0.0001
  )
  expect_equal(
    suppressWarnings(
      as.vector(Calibration(exp4_2, obs4_2, exp_cor4, dat_dim = 'dataset', na.rm = T))[5:10]
    ),
    c(0.4590841, 0.2665937, 0.3308434, 1.5290565, -1.1846831, 1.7042331),
    tolerance = 0.0001
  )
})
