context("vision-utils")

test_that("vision_make_grid", {

  images <- torch::torch_randn(c(4, 3, 16, 16))

  grid <- vision_make_grid(images, num_rows = 2, padding = 0)


  expect_equal(grid$size(), c(3, 32, 32))
  expect_equal(as.numeric(grid$max() - grid$min()), 1, tolerance = 1e-4)

})

test_that("draw_bounding_boxes works", {

  image_float <- 1 - (torch::torch_randn(c(3, 360, 360)) / 20)
  image_uint <- (255 - (torch::torch_randint(low = 1, high = 60, size = c(3, 360, 360))))$to(torch::torch_uint8())
  x <- torch::torch_randint(low = 1, high = 160, size = c(12,1))
  y <- torch::torch_randint(low = 1, high = 260, size = c(12,1))
  w <- torch::torch_randint(low = 10, high = 100, size = c(12,1))
  h <- torch::torch_randint(low = 30, high = 60, size = c(12,1))
  boxes <- torch::torch_cat(c(x, y, x + w, y +  h), dim = 2)

  expect_error(bboxed_image <- draw_bounding_boxes(image_uint$to(dtype = torch::torch_int32()), boxes),
               class = "type_error", regexp = "torch_uint8")

  expect_no_error(bboxed_image <- draw_bounding_boxes(image_float, boxes, labels = "dog", width = 5))
  expect_no_error(bboxed_image <- draw_bounding_boxes(image_uint, boxes, labels = "Leptailurus serval constantina", width = 1))
  expect_tensor_dtype(bboxed_image, torch::torch_uint8())
  expect_tensor_shape(bboxed_image, c(3, 360, 360))

  expect_no_error(bboxed_image <- draw_bounding_boxes(image_uint, boxes, colors = "black", fill = TRUE))
})

test_that("draw_bounding_boxes correctly mask a complete image", {

  image_float <- 1 - (torch::torch_randn(c(3, 360, 360)) / 20)
  image_uint <- torch::torch_randint(low = 1, high = 240, size = c(3, 360, 360))$to(torch::torch_uint8())
  boxes <- torch::torch_tensor(c(0,0,360,360))$unsqueeze(1)

  expect_no_error(bboxed_image <- draw_bounding_boxes(image_float, boxes, colors = "black", fill = TRUE))
  expect_no_error(bboxed_image <- draw_bounding_boxes(image_uint, boxes, colors = "black", fill = TRUE))
  # some invisible glitch remains
  expect_lte(bboxed_image$sum() %>% as.numeric, 3000)


})

test_that("draw_bounding_boxes supports deprecated color alias", {
  image_uint <- torch::torch_randint(low = 1, high = 240, size = c(3, 64, 64))$to(torch::torch_uint8())
  boxes <- torch::torch_tensor(c(1, 1, 40, 40))$unsqueeze(1)

  expect_warning(
    bboxed_image <- draw_bounding_boxes(image_uint, boxes, color = "black", fill = TRUE),
    "deprecated"
  )
  expect_tensor_dtype(bboxed_image, torch::torch_uint8())
  expect_tensor_shape(bboxed_image, c(3, 64, 64))
})

test_that("draw_segmentation_masks works with boolean mask", {

  image_float <- 1 - (torch::torch_randn(c(3, 360, 360)) / 20)
  image_uint <- torch::torch_randint(low = 190, high = 255, size = c(3, 360, 360))$to(torch::torch_uint8())
  lower_mask <- torch::torch_tril(torch::torch_ones(c(360, 360)), diagonal = FALSE)$to(torch::torch_bool())
  upper_mask <- torch::torch_triu(torch::torch_ones(c(360, 360)), diagonal = FALSE)$to(torch::torch_bool())
  masks <- torch::torch_stack(c(lower_mask, upper_mask), dim = 1)

  expect_no_error(masked_image <- draw_segmentation_masks(image_float, masks))
  expect_tensor_dtype(masked_image, torch::torch_uint8())
  expect_tensor_shape(masked_image, c(3, 360, 360))

  expect_no_error(masked_image <- draw_segmentation_masks(image_uint, masks))
  expect_tensor_dtype(masked_image, torch::torch_uint8())
  expect_tensor_shape(masked_image, c(3, 360, 360))

  colors <-  c("navyblue", "orange3")
  expect_no_error(masked_image <- draw_segmentation_masks(image_uint, masks, colors = colors, alpha = 0.5))
})

test_that("draw_segmentation_masks works with float mask", {

  image_float <- 1 - (torch::torch_randn(c(3, 360, 360)) / 20)
  image_uint <- torch::torch_randint(low = 190, high = 255, size = c(3, 360, 360))$to(torch::torch_uint8())
  lower_mask <- torch::torch_tril(torch::torch_ones(c(360, 360)), diagonal = FALSE)
  upper_mask <- torch::torch_triu(torch::torch_ones(c(360, 360))*2, diagonal = FALSE)
  masks <- torch::torch_stack(c(lower_mask, upper_mask), dim = 1)

  expect_no_error(masked_image <- draw_segmentation_masks(image_float, masks))
  expect_tensor_dtype(masked_image, torch::torch_uint8())
  expect_tensor_shape(masked_image, c(3, 360, 360))

  expect_no_error(masked_image <- draw_segmentation_masks(image_uint, masks))
  expect_tensor_dtype(masked_image, torch::torch_uint8())
  expect_tensor_shape(masked_image, c(3, 360, 360))

  colors <-  c("navyblue", "orange3")
  expect_no_error(masked_image <- draw_segmentation_masks(image_uint, masks, colors = colors, alpha = 0.5))
})

test_that("draw_keypoints works", {

  image_float <- 1 - (torch::torch_randn(c(3, 360, 360)) / 20)
  image_uint <- torch::torch_randint(low = 190, high = 255, size = c(3, 360, 360))$to(torch::torch_uint8())
  keypoints <- torch::torch_randint(low = 60, high = 300, size = c(4, 5, 2))
  colors <-  hcl.colors(n = 5)

  expect_no_error(keypoint_image <- draw_keypoints(image_float, keypoints))
  expect_tensor_dtype(keypoint_image, torch::torch_uint8())
  expect_tensor_shape(keypoint_image, c(3, 360, 360))
  expect_no_error(keypoint_image <- draw_keypoints(image_float, keypoints, colors = colors, radius = 7))

  expect_no_error(keypoint_image <- draw_keypoints(image_uint, keypoints))
  expect_tensor_dtype(keypoint_image, torch::torch_uint8())
  expect_tensor_shape(keypoint_image, c(3, 360, 360))
  expect_no_error(keypoint_image <- draw_keypoints(image_uint, keypoints, colors = colors, radius = 7))
})

test_that("tensor_image_browse works", {
  skip_on_cran()
  skip_on_ci()
  # uint8 color image
  image <- (255 - (torch::torch_randint(low = 1, high = 200, size = c(3, 360, 360))))$to(torch::torch_uint8())
  expect_no_error(tensor_image_browse(image))
  # uint8 grayscale image
  image <- (255 - (torch::torch_randint(low = 1, high = 200, size = c(1, 360, 360))))$to(torch::torch_uint8())
  expect_no_error(tensor_image_browse(image))

  # float color image
  image <- torch::torch_rand(size = c(3, 360, 360))
  expect_no_error(tensor_image_browse(image))
  # float grayscale image
  image <- torch::torch_rand(size = c(1, 360, 360))
  expect_no_error(tensor_image_browse(image))

  # error cases : shape
  image <- torch::torch_randint(low = 1, high = 200, size = c(4, 3, 360, 360))$to(torch::torch_uint8())
  expect_error(tensor_image_browse(image), "Pass individual `image`, not batches")
  image <- torch::torch_randint(low = 1, high = 200, size = c(4, 360, 360))$to(torch::torch_uint8())
  expect_error(tensor_image_browse(image), "Only grayscale and RGB")

})

# ==== COCO sample drawing ====

test_that("draw_bounding_boxes works with coco_detection_sample", {
  skip_if_not(torch::torch_is_installed())

  skip_if(Sys.getenv("TEST_LARGE_DATASETS", unset = 0) != 1,
        "Skipping test: set TEST_LARGE_DATASETS=1 to enable tests requiring large downloads.")

  ds <- coco_detection_dataset(root = "~/data", train = FALSE, year = "2017", download = TRUE)
  item <- ds[3]

  out <- draw_bounding_boxes(item)
  expect_tensor(out)
  expect_equal(out$ndim, 3)
  expect_equal(out$shape[1], 3)  # 3 color channels
  expect_gt(out$shape[2], 100)   # image height is reasonable
  expect_gt(out$shape[3], 100)   # image width is reasonable
})

test_that("draw_segmentation_masks works with coco_detection_sample", {
  skip_if_not(torch::torch_is_installed())

  skip_if(Sys.getenv("TEST_LARGE_DATASETS", unset = 0) != 1,
        "Skipping test: set TEST_LARGE_DATASETS=1 to enable tests requiring large downloads.")

  ds <- coco_detection_dataset(root = "~/data", train = FALSE, year = "2017", download = TRUE)
  item <- ds[3]

  if (item$y$masks$size(1) > 0) {
    out <- draw_segmentation_masks(item)
    expect_tensor(out)
    expect_equal(out$ndim, 3)
    expect_equal(out$shape[1], 3)
    expect_gt(out$shape[2], 100)
    expect_gt(out$shape[3], 100)
  } else {
    skip("No masks in this item — skipping mask drawing test.")
  }
})

