context("stan_distsamp function and methods")

skip_on_cran()

#Line transect fits
data(linetran)
ltUMF <- with(linetran, {
        unmarkedFrameDS(y = cbind(dc1, dc2, dc3, dc4),
        siteCovs = data.frame(Length, area, habitat),
        dist.breaks = c(0, 5, 10, 15, 20),
        tlength = linetran$Length * 1000, survey = "line", unitsIn = "m")
        })

linetran_big <- do.call("rbind", lapply(1:12, function(x) linetran))
ltUMF_big <- with(linetran_big, {
        unmarkedFrameDS(y = cbind(dc1, dc2, dc3, dc4),
        siteCovs = data.frame(Length, area, habitat),
        dist.breaks = c(0, 5, 10, 15, 20),
        tlength = linetran_big$Length * 1000, survey = "line", unitsIn = "m")
        })

good_fit <- TRUE
tryCatch({
fit_line_hn <- suppressWarnings(stan_distsamp(~1~habitat, ltUMF, chains=2,
                                              iter=200, refresh=0))
fit_line_exp <- suppressWarnings(stan_distsamp(~1~habitat, ltUMF, keyfun="exp",
                                              chains=2, iter=200, refresh=0))
fit_line_haz <- suppressWarnings(stan_distsamp(~1~habitat, ltUMF, keyfun="hazard",
                                               chains=2, iter=15, refresh=0))
fit_line_abun <- suppressWarnings(stan_distsamp(~1~habitat, ltUMF, output="abund",
                                                chains=2, iter=200, refresh=0))
line_mods <- list(fit_line_hn, fit_line_exp, fit_line_haz, fit_line_abun)
}, error=function(e){
  good_fit <<- FALSE
})

ltUMF_na <- ltUMF
ltUMF_na@y[1,] <- NA
ltUMF_na@y[2,1] <- NA

data(pointtran)
ptUMF <- with(pointtran, {
             unmarkedFrameDS(y = cbind(dc1, dc2, dc3, dc4, dc5),
             siteCovs = data.frame(area, habitat),
             dist.breaks = seq(0, 25, by=5), survey = "point", unitsIn = "m")
             })
pointtran_big <- do.call("rbind", lapply(1:4, function(x) pointtran))
ptUMF_big <- with(pointtran_big, {
             unmarkedFrameDS(y = cbind(dc1, dc2, dc3, dc4, dc5),
             siteCovs = data.frame(area, habitat),
             dist.breaks = seq(0, 25, by=5), survey = "point", unitsIn = "m")
             })

tryCatch({
fit_pt_hn <- suppressWarnings(stan_distsamp(~1~habitat, ptUMF, chains=2,
                                              iter=200, refresh=0))
fit_pt_exp <- suppressWarnings(stan_distsamp(~1~habitat, ptUMF, keyfun="exp",
                                              chains=2, iter=200, refresh=0))
fit_pt_haz <- suppressWarnings(stan_distsamp(~1~habitat, ptUMF, keyfun="hazard",
                                               chains=2, iter=15, refresh=0))
point_mods <- list(fit_pt_hn, fit_pt_exp, fit_pt_haz)
}, error=function(e){
  good_fit <<- FALSE
})

skip_if(!good_fit, "Test setup failed")

test_that("stan_distsamp output structure is correct",{
  expect_true(all(sapply(line_mods, function(x) class(x)[1])=="ubmsFitDistsamp"))
  expect_true(all(sapply(line_mods,
                         function(x) class(x@response)[1])=="ubmsResponseDistsamp"))
  expect_equal(sapply(line_mods, function(x) x@response@y_dist),
              c("halfnorm", "exp", "hazard", "halfnorm"))
  expect_equal(nsamples(fit_line_hn), 200)

  expect_true(all(sapply(point_mods, function(x) class(x)[1])=="ubmsFitDistsamp"))
  expect_equal(sapply(point_mods, function(x) x@response@y_dist),
              c("halfnorm", "exp", "hazard"))
  expect_is(fit_pt_haz['scale'], 'ubmsSubmodelScalar')
  expect_is(fit_line_haz['scale'], 'ubmsSubmodelScalar')
})

test_that("stan_distsamp produces accurate results",{
  skip_on_cran()
  skip_on_ci()
  skip_on_covr()

  #Line transect
  set.seed(123)
  stan_mod <- suppressWarnings(stan_distsamp(~1~1, ltUMF_big,
                                             chains=2, iter=200, refresh=0))
  um_mod <- distsamp(~1~1, ltUMF_big)
  expect_RMSE(coef(stan_mod), coef(um_mod), 0.01)

  stan_mod <- suppressWarnings(stan_distsamp(~1~1, ltUMF_big, keyfun="exp",
                                             chains=2, iter=200, refresh=0))
  um_mod <- distsamp(~1~1, ltUMF_big, keyfun="exp")
  expect_RMSE(coef(stan_mod), coef(um_mod), 0.02)

  stan_mod <- suppressWarnings(stan_distsamp(~1~1, ltUMF_big, output="abund",
                            chains=2, iter=200, refresh=0))
  um_mod <- distsamp(~1~1, ltUMF_big, output="abund")
  expect_RMSE(coef(stan_mod), coef(um_mod), 0.01)

  #Point
  set.seed(123)
  stan_mod <- suppressWarnings(stan_distsamp(~1~1, ptUMF, chains=2,
                                             iter=200, refresh=0))
  um_mod <- distsamp(~1~1, ptUMF)
  expect_RMSE(coef(stan_mod), coef(um_mod), 0.05)

  stan_mod <- suppressWarnings(stan_distsamp(~1~1, ptUMF_big, keyfun="exp",
                                             chains=2, iter=200, refresh=0))
  expect_RMSE(coef(stan_mod), c(4.9806, 2.0597), 0.05)
  #unmarked model fails here

  #Need hazard tests here at some point, maybe when I can speed it up
})

test_that("stan_distsamp handles NA values",{
  expect_error(stan_distsamp(~1~1, ltUMF_na))
})

test_that("extract_log_lik method works",{
  ll <- extract_log_lik(fit_pt_hn)
  expect_is(ll, "matrix")
  expect_equal(dim(ll), c(200/2 * 2, numSites(fit_pt_hn@data)))
  expect_between(sum(ll), -39000, -38000)
})

test_that("ubmsFitDistsamp gof method works",{
  set.seed(123)
  g <- lapply(line_mods, function(x) gof(x, draws=5, quiet=TRUE))
  expect_true(all(sapply(g, function(x) class(x))=="ubmsGOF"))
  gof_plot_method <- methods::getMethod("plot", "ubmsGOF")
  pdf(NULL)
  pg <- gof_plot_method(g[[1]])
  dev.off()
  expect_is(pg, "gg")

  set.seed(123)
  g <- lapply(point_mods, function(x) gof(x, draws=5, quiet=TRUE))
  expect_true(all(sapply(g, function(x) class(x))=="ubmsGOF"))
})

test_that("ubmsFitDistsamp predict method works",{
  pr <- predict(fit_line_hn, "state")
  expect_is(pr, "data.frame")
  expect_equal(dim(pr), c(12, 4))
  expect_between(pr[1,1], 0, 5)
  pr <- predict(fit_line_hn, "det")
  expect_equal(dim(pr), c(12,4))
  #expect_true(between(pr[1,1], 5, 20))
  #with newdata
  nd <- data.frame(habitat=c("A","B"))
  pr <- predict(fit_line_hn, "state", newdata=nd)
  expect_equal(dim(pr), c(2,4))
  expect_between(pr[1,1], 0, 5)
})

test_that("ubmsFitDistsamp sim_z method works",{
  set.seed(123)
  samples <- 1:3
  zz <- sim_z(fit_line_hn, samples, re.form=NULL)
  expect_is(zz, "matrix")
  expect_equal(dim(zz), c(length(samples), 12))
  expect_between(mean(zz), 10, 20)

  set.seed(123)
  pz <- posterior_predict(fit_line_hn, "z", draws=3)
  expect_equivalent(dim(zz), dim(pz))

  zlist <- lapply(line_mods, function(x) sim_z(x, samples, re.form=NULL))
  expect_true(all(sapply(zlist, function(x) all(dim(x)==dim(pz)))))

  zlist2 <- lapply(point_mods, function(x) sim_z(x, samples, re.form=NULL))
  expect_true(all(sapply(zlist2, function(x) all(dim(x)==c(3,30)))))
})

test_that("ubmsFitDistsamp sim_y method works",{
  set.seed(123)
  samples <- 1:3
  yy <- sim_y(fit_line_hn, samples, re.form=NULL)
  expect_is(yy, "matrix")
  expect_equal(dim(yy), c(3, 48))
  expect_between(mean(yy), 1, 4)
  set.seed(123)
  py <- posterior_predict(fit_line_hn, "y", draws=3)
  expect_equivalent(dim(yy), dim(py))

  #Test abundance model
  yabun <- sim_y(fit_line_abun, samples, re.form=NULL)
  expect_between(mean(yy), 1, 4)

  ylist <- lapply(line_mods, function(x) sim_y(x, samples, re.form=NULL))
  expect_true(all(sapply(ylist, function(x) all(dim(x)==dim(py)))))

  ylist2 <- lapply(point_mods, function(x) sim_y(x, samples, re.form=NULL))
  expect_true(all(sapply(ylist2, function(x) all(dim(x)==c(3,150)))))
})

test_that("Posterior linear pred methods work for ubmsFitDistsamp",{
  set.seed(123)
  samples <- get_samples(fit_line_hn, 3)
  lp1 <- sim_lp(fit_line_abun, "state", transform=TRUE, samples=samples,
                newdata=NULL, re.form=NULL)
  expect_equal(dim(lp1), c(length(samples), 12))
  set.seed(123)
  pl <- posterior_linpred(fit_line_hn, draws=3, submodel="det")
})

test_that("Fitted/residual methods work with ubmsFitDistsamp",{
  ubms_fitted <- methods::getMethod("fitted", "ubmsFit")
  ubms_residuals <- methods::getMethod("residuals", "ubmsFit")
  ubms_plot <- methods::getMethod("plot", "ubmsFit")

  set.seed(123)
  ft <- ubms_fitted(fit_line_hn, "state", draws=5)
  ft2 <- ubms_fitted(fit_line_hn, "det", draws=5)
  ft3 <- ubms_fitted(fit_line_abun, "state", draws=5)
  expect_equal(dim(ft), c(5,12))
  expect_equal(dim(ft2), c(5,48))
  expect_equal(dim(ft3), c(5,12))
  expect_equal(mean(ft), mean(ft3), tol=5)

  res <- ubms_residuals(fit_line_hn, "state", draws=5)
  res2 <- ubms_residuals(fit_line_hn, "det", draws=5)
  expect_equal(dim(res), c(5,12))
  expect_equal(dim(res2), c(5,48))

  pdf(NULL)
  rp <- plot_residuals(fit_line_hn, "state")
  rp2 <- plot_residuals(fit_line_hn, "det")
  rp3 <- ubms_plot(fit_line_hn)
  mp <- plot_marginal(fit_line_hn, "state")
  dev.off()

  expect_is(rp, "gg")
  expect_is(rp2, "gg")
  expect_is(rp3, "gtable")
  expect_is(mp, "gg")
})

test_that("sim_state works for ubmsFitDistsamp",{
  sdens <- sim_state(fit_line_hn, 1:3)
  adens <- sim_state(fit_line_abun, 1:3)
  expect_equal(mean(sdens), mean(adens), tol=4)
})

test_that("getP and sim_p for ubmsFitDistsamp work",{

  plist <- lapply(line_mods, function(x) sim_p(x, 1:3))
  expect_true(all(sapply(plist, function(x) all(dim(x)==c(3,48)))))

  plist2 <- lapply(point_mods, function(x) sim_p(x, 1:3))
  expect_true(all(sapply(plist2, function(x) all(dim(x)==c(3,150)))))

  gplist <- lapply(line_mods, function(x) getP(x, 3))
  expect_true(all(sapply(gplist, function(x) all(dim(x)==c(12,4,3)))))

  gplist2 <- lapply(point_mods, function(x) getP(x, 3))
  expect_true(all(sapply(gplist2, function(x) all(dim(x)==c(30,5,3)))))
})

test_that("hist function works for ubmsFitDistsamp",{

  ubms_hist <- methods::getMethod("hist", "ubmsFitDistsamp")

  line_hist <- lapply(line_mods[c(1,2,4)], ubms_hist, draws=3)
  expect_true(all(sapply(line_hist, inherits, "gg")))
  point_hist <- lapply(point_mods[1:2], ubms_hist, draws=3)
  expect_true(all(sapply(point_hist, inherits, "gg")))

  fit_line_pcov <- suppressWarnings(stan_distsamp(~habitat~1, ltUMF, chains=2,
                                    iter=200, refresh=0))
  expect_warning(hwarn <- ubms_hist(fit_line_pcov))
  expect_is(hwarn, "gg")


  pdf(NULL)
  line_hist[[1]]
  line_hist[[2]]
  line_hist[[3]]
  point_hist[[1]]
  point_hist[[2]]
  hwarn
  dev.off()
})

test_that("ubmsResponseDistsamp methods work",{

  resp <- fit_line_hn@response
  expect_equal(get_K(resp), max(rowSums(fit_line_hn@data@y)) + 50)
  expect_equal(get_K(resp, 40), 40)
  expect_error(get_K(resp, 10))

  resp2 <- resp
  resp2@y[1,] <- NA
  expect_equal(length(get_Kmin(resp2)), nrow(resp2@y)-1)

  resp3 <- resp
  resp3@units_in <- "km"
  resp3@units_out <- "kmsq"
  expect_equal(mean(get_area_adjust(resp)), 16, tol=1e-3)
  expect_equal(mean(get_area_adjust(resp3)), 16e4, tol=1)
})

test_that("distsamp spatial works", {
  skip_on_cran()
  umf2 <- ltUMF
  umf2@siteCovs$x <- runif(numSites(umf2), 0, 10)
  umf2@siteCovs$y <- runif(numSites(umf2), 0, 10)
  fit_spat <- suppressMessages(suppressWarnings(stan_distsamp(~1~habitat+RSR(x,y,1),
                                umf2, chains=2, iter=100, refresh=0)))
  expect_is(fit_spat@submodels@submodels$state, "ubmsSubmodelSpatial")
  expect_equal(names(coef(fit_spat))[3], "state[RSR [tau]]")

  ps <- plot_spatial(fit_spat)
  expect_is(ps, "gg")
})

test_that("kfold errors when used on a stan_distsamp model", {
  expect_error(kfold(fit_line_hn),
               "kfold method not yet supported for stan_distsamp models")
})
