library(CSTools)

# dat1
dat1 <- array(1:6, dim = c(sdate = 2, ftime = 3, member = 1))
dat1_1 <- dat1
class(dat1_1) <- 's2dv_cube'
dat1_2 <- NULL
dat1_2$data <- dat1
class(dat1_2) <- 's2dv_cube'

# dat2
dat2 <- array(1:6, dim = c(sdate = 2, time = 3, member = 1))
dates2 <- c(seq(as.Date("01-04-2000", format = "%d-%m-%Y"),
               as.Date("03-04-2000", format = "%d-%m-%Y"), by = 'day'),
           seq(as.Date("01-04-2001", format = "%d-%m-%Y"),
               as.Date("03-04-2001", format = "%d-%m-%Y"), by = 'day'))
dim(dates2) <- c(time = 3, sdate = 2)

# exp1
exp <- NULL
exp$data <- array(1:(1 * 3 * 214 * 2),
                  c(memb = 1, sdate = 3, ftime = 214, lon = 2))
exp$dims <- dim(exp$data)
exp$attrs$Dates <- c(seq(as.Date("01-04-2000", format = "%d-%m-%Y"),
                          as.Date("31-10-2000", format = "%d-%m-%Y"), by = 'day'),
                      seq(as.Date("01-04-2001", format = "%d-%m-%Y"),
                          as.Date("31-10-2001", format = "%d-%m-%Y"), by = 'day'),
                      seq(as.Date("01-04-2002", format = "%d-%m-%Y"),
                          as.Date("31-10-2002", format = "%d-%m-%Y"), by = 'day'))
dim(exp$attrs$Dates) <- c(ftime = 214, sdate = 3)
class(exp) <- 's2dv_cube'

##############################################
test_that("1. Initial checks", {
  # s2dv_cube
  expect_error(
    CST_PeriodAccumulation(array(1)),
    "Parameter 'data' must be of the class 's2dv_cube'."
  )
  expect_error(
    CST_PeriodAccumulation(data = dat1_1, start = list(1,2), end = list(2,3)),
    "Parameter 'data' doesn't have 's2dv_cube' structure. Use PeriodAccumulation instead."
  )
  # Dates subset
  expect_warning(
    CST_PeriodAccumulation(data = dat1_2, start = list(1,2), end = list(2,3),
                           time_dim = 'ftime'),
    paste0("Dimensions in 'data' element 'attrs$Dates' are missed and ",
           "all data would be used."),
    fixed = TRUE
  )
  # data
  expect_error(
    PeriodAccumulation('x'), 
    "Parameter 'data' must be numeric."
  )
  expect_error(
    PeriodAccumulation(data = NULL), 
    "Parameter 'data' cannot be NULL."
  )
  # time_dim
  expect_error(
    PeriodAccumulation(data = dat2, time_dim = 'ftime', rollwidth = 1, 
                       dates = dates2), 
    "Parameter 'time_dim' is not found in 'data' dimension."
  )
  # start and end
  expect_error(
    PeriodAccumulation(dat2, dates = '2000-01-01', end = 3, start = 4),
    paste("Parameter 'start' and 'end' must be lists indicating",
          "the day and the month of the period start and end.")
  )
  expect_equal(
    PeriodAccumulation(1:10), 
    55
  )
  data <- array(1:24, c(sdate = 2, time = 3, lon = 4))
  expect_equal(
    PeriodAccumulation(data),
    array(c(9, 12, 27, 30, 45, 48, 63, 66), c(sdate = 2, lon = 4))
  )
  # Test dates warning
  expect_warning(
    PeriodAccumulation(array(1:10, c(time = 10)), 
                       dates = seq(as.Date("01-05-2000", format = "%d-%m-%Y"), 
                                   as.Date("10-05-2000", format = "%d-%m-%Y"), by = 'day'),
                       start = list(05, 02), end = list(05, 09)),
    paste0("Parameter 'dates' must have named dimensions if 'start' and 'end' ", 
           "are not NULL. All data will be used.")
  )
  # start and end when dates is not provided
  expect_warning(
    PeriodAccumulation(array(1:10, c(time = 10)), 
                       start = list(05, 02), end = list(05, 09)),
    paste0("Parameter 'dates' is NULL and the average of the full data ", 
           "provided in 'data' is computed.")
  )
})

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

test_that("2. Seasonal", {
  output <- exp
  output$data <- array(c(sum(exp$data[1,1,21:82,1]), sum(exp$data[1,2,21:82,1]),
                         sum(exp$data[1,3,21:82,1]), sum(exp$data[1,1,21:82,2]),
                         sum(exp$data[1,2,21:82,2]), sum(exp$data[1,3,21:82,2])),
                       c(memb = 1, sdate = 3, lon = 2))

  expect_equal(
    CST_PeriodAccumulation(exp, start = list(21, 4), end = list(21, 6), 
                           time_dim = 'ftime')$data, 
    output$data
  )
})

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

test_that("3. Subset Dates and check time_bounds", {
  exp <- CSTools::lonlat_prec
  res <- CST_PeriodAccumulation(data = CSTools::lonlat_prec, time_dim = 'ftime',
                                start = list(10, 03), end = list(20, 03))
  res2 <- CST_PeriodAccumulation(data = CSTools::lonlat_prec, time_dim = 'ftime')
  # Check dims
  expect_equal(
    dim(res$data), 
    res$dims
  )
  expect_equal(
    dim(res2$data), 
    dim(exp$data)[-which(names(dim(exp$data)) == 'ftime')]
  )
  # Check Dates
  expect_equal(
    dim(res$data)['sdate'], 
    dim(res$attrs$Dates)
  )
  expect_equal(
    dim(res2$data)['sdate'], 
    dim(res2$attrs$Dates)
  )
  # Check time_bounds
  expect_equal(
    res$attrs$Dates, 
    res$attrs$time_bounds$start
  )
  expect_equal(
    dim(res$attrs$time_bounds$start), 
    dim(res$attrs$time_bounds$end)
  )
  expect_equal(
    res2$attrs$Dates, 
    res2$attrs$time_bounds$start
  )
  # Check 'sdates'
  expect_equal(
    all(lubridate::month(res$attrs$time_bounds$start) == 3),
    TRUE
  )
  expect_equal(
    all(lubridate::day(res$attrs$time_bounds$start) == 10),
    TRUE
  )
  expect_equal(
    all(lubridate::month(res$attrs$time_bounds$end) == 03),
    TRUE
  )
  expect_equal(
    all(lubridate::day(res$attrs$time_bounds$end) == 20),
    TRUE
  )
})

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

test_that("4. Rolling", {
  # dates
  expect_error(
    PeriodAccumulation(data = dat2, rollwidth = 1),
    "Parameter 'dates' is NULL. Cannot compute the rolling accumulation."
  )
  # rollwidth
  expect_error(
    PeriodAccumulation(data = dat2, rollwidth = 'a', dates = dates2),
    "Parameter 'rollwidth' must be a numeric value."
  )
  expect_error(
    PeriodAccumulation(data = dat2, rollwidth = 5, dates = dates2),
    "Cannot compute accumulation of 5 months because loaded data has only 3 months."
  )
  # sdate_dim
  expect_error(
    PeriodAccumulation(data = dat2, rollwidth = 1, dates = dates2, 
                       sdate_dim = 'syear'),
    "Parameter 'sdate_dim' is not found in 'data' dimension."
  )
  # Output checks
  expect_equal(
    PeriodAccumulation(data = dat2, rollwidth = -2, dates = dates2, frequency = 'daily'),
    array(c(4,6,8, 10, NA, NA), dim = c(sdate = 2, time = 3, member = 1))
  )
  expect_equal(
    PeriodAccumulation(data = dat2, rollwidth = 3, dates = dates2, frequency = 'daily'),
    array(c(rep(NA, 4), 9, 12), dim = c(sdate = 2, time = 3, member = 1))
  )
  dat2_1 <- dat2
  dat2_1[1,1,1] <- NA
  expect_equal(
    PeriodAccumulation(data = dat2_1, rollwidth = 2, dates = dates2, na.rm = FALSE, 
                       frequency = 'daily'), 
    array(c(rep(NA, 3), 6,8,10), dim = c(sdate = 2, time = 3, member = 1))
  )
  
  # Test rolling with start and end
  expect_equal(
    PeriodAccumulation(data = dat2, rollwidth = 1, dates = dates2, 
                       start = list(1, 4), end = list(2, 4), frequency = 'daily'),
    array(c(1, 2, 3, 4), dim = c(sdate = 2, time = 2, member = 1))
  )
  expect_equal(
    PeriodAccumulation(data = dat2, rollwidth = 2, dates = dates2, 
                       start = list(1, 4), end = list(2, 4), frequency = 'daily'),
    array(c(NA, NA, 4, 6), dim = c(sdate = 2, time = 2, member = 1))
  )
})
