
test_that("s2_geography class works", {
  expect_s3_class(s2_geography(), "s2_geography")
  geog <- new_s2_geography(list(NULL))
  expect_output(print(geog), "s2_geography")
  expect_output(str(geog), "s2_geography")
  expect_identical(as_s2_geography(geog), geog)

  expect_identical(
    is.na(as_s2_geography(c("POINT (0 1)", NA_character_))),
    c(FALSE, TRUE)
  )

  # subset assignment
  geog2 <- geog
  geog2[1] <- geog
  expect_identical(geog2, geog)

  geog2 <- geog
  geog2[[1]] <- geog
  expect_identical(geog2, geog)
})

test_that("s2_geography vectors can be put in a data frame", {
  expect_identical(
    data.frame(geog = s2_geography()),
    new_data_frame(list(geog = s2_geography()))
  )
})

test_that("s2_geography vectors can't have other types of objects concatenated or asssigned", {
  geog <- new_s2_geography(list(NULL))
  expect_s3_class(c(geog, geog), "s2_geography")
  expect_error(c(geog, wk::wkt()), "Can't combine 'wk_vctr' objects")
  expect_error(geog[1] <- factor(1), "no applicable method")
  expect_error(geog[[1]] <- factor(1), "no applicable method")
})

test_that("s2_geography vectors can be created from s2_lnglat  and s2_point", {
  expect_wkt_equal(as_s2_geography(s2_lnglat(-64, 45)), "POINT (-64 45)")
  expect_wkt_equal(as_s2_geography(as_s2_point(s2_lnglat(-64, 45))), "POINT (-64 45)")
})

test_that("s2_geography vectors can be created from WKB and WKT", {
  wkb_point <- wk::as_wkb(wk::wkt("POINT (-64 45)", geodesic = TRUE))
  expect_output(print(as_s2_geography(wkb_point)), "POINT \\(-64 45\\)")
  expect_error(
    as_s2_geography(wk::as_wkb("LINESTRING (0 0, 1 1)")),
    "Cartesian wkb\\(\\)"
  )

  # empty, null, and point features are OK
  expect_identical(as_s2_geography(wk::wkb()), as_s2_geography(character()))
  expect_identical(as_s2_geography(wk::wkb(list(NULL))), as_s2_geography(NA_character_))
  expect_silent(as_s2_geography(wk::as_wkb("POINT (0 1)")))
  expect_silent(as_s2_geography(wk::as_wkb("MULTIPOINT (0 1)")))

  wkt_point <- wk::as_wkt(wk::wkt("POINT (-64 45)", geodesic = TRUE))
  expect_output(print(as_s2_geography(wkt_point)), "POINT \\(-64 45\\)")
  expect_error(
    as_s2_geography(wk::wkt("LINESTRING (0 0, 1 1)")),
    "Cartesian wkt\\(\\)"
  )

  # empty, null, and point features are OK
  expect_identical(as_s2_geography(wk::wkt()), as_s2_geography(character()))
  expect_identical(as_s2_geography(wk::wkt(NA_character_)), as_s2_geography(NA_character_))
  expect_silent(as_s2_geography(wk::wkt("POINT (0 1)")))
  expect_silent(as_s2_geography(wk::wkt("MULTIPOINT (0 1)")))

  # also test other classes commonly used to signify WKB or WKT
  expect_output(print(as_s2_geography(structure(wkb_point, class = "WKB")), "POINT \\(-64 45\\)"))
  expect_output(print(as_s2_geography(structure(wkb_point, class = "blob")), "POINT \\(-64 45\\)"))
})

test_that("s2_geography can be exported to WKB/WKT", {
  expect_wkt_equal(
    wk::as_wkb(as_s2_geography("POINT (-64 45)")),
    wk::as_wkb(wk::wkt("POINT (-64 45)", geodesic = TRUE)),
    precision = 10
  )
  expect_wkt_equal(
    wk::as_wkt(as_s2_geography("POINT (-64 45)")),
    wk::as_wkt(wk::wkt("POINT (-64 45)", geodesic = TRUE)),
    precision = 10
  )
})

test_that("s2_geography vectors can be created from wkt", {
  expect_output(print(as_s2_geography("POINT (-64 45)")), "POINT \\(-64 45\\)")
  expect_output(print(as_s2_geography("POINT EMPTY")), "POINT EMPTY")
  expect_output(
    print(as_s2_geography("MULTIPOINT ((-64 45), (30 10))")),
    "MULTIPOINT \\(\\(-64 45\\), \\(30 10\\)\\)"
  )

  expect_output(
    print(as_s2_geography("LINESTRING (-64 45, 0 0)")),
    "LINESTRING \\(-64 45, 0 0\\)"
  )
  expect_output(
    print(as_s2_geography("LINESTRING EMPTY")),
    "LINESTRING EMPTY"
  )
  expect_output(
    print(as_s2_geography("MULTILINESTRING ((-64 45, 0 0), (0 1, 2 3))")),
    "MULTILINESTRING \\(\\(-64 45, 0 0), \\(0 1, 2 3\\)\\)"
  )

  expect_output(print(as_s2_geography("POLYGON EMPTY"), "POLYGON EMPTY"))
  expect_output(
    print(as_s2_geography("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")),
    "POLYGON \\(\\(0 0, 10 0, 10 10"
  )
  expect_output(
    print(as_s2_geography("MULTIPOLYGON (((0 0, 10 0, 10 10, 0 10, 0 0)))")),
    "POLYGON \\(\\(0 0, 10 0, 10 10"
  )
  expect_output(
    print(
      as_s2_geography("MULTIPOLYGON (
        ((40 40, 20 45, 45 30, 40 40)),
        ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))
      )")
    ),
    "MULTIPOLYGON"
  )

  expect_output(
    print(as_s2_geography("GEOMETRYCOLLECTION (POINT (-64 45))")),
    "GEOMETRYCOLLECTION \\(POINT \\(-64 45\\)\\)"
  )

  expect_match(
    s2_as_text(
      as_s2_geography(
        "GEOMETRYCOLLECTION (
          POINT (30 10),
          MULTIPOINT (11 12, 12 13),
          LINESTRING (40 40, 40 41),
          MULTILINESTRING ((-10 -12, -12 -13), (-15 -15, -16 -16)),
          POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0)),
          MULTIPOLYGON (((0 0, -10 0, -10 -10, 0 -10, 0 0))),
          GEOMETRYCOLLECTION (POINT (-100 0), MULTIPOINT(-101 0, -102 0))
        )"
      )
    ),
    paste0(
      "GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT.*?LINESTRING.*?MULTILINESTRING.*?",
      "POLYGON.*?POLYGON.*?GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT"
    )
  )

  expect_output(print(as_s2_geography("GEOMETRYCOLLECTION EMPTY")), "GEOMETRYCOLLECTION EMPTY")
})

test_that("empty points are empty when imported from WKB", {
  wkb_empty <- wk::as_wkb("POINT EMPTY")
  expect_true(s2_is_empty(s2_geog_from_wkb(wkb_empty)))
})

test_that("nested ring depths are correctly exported", {
  # polygon with hole
  expect_match(
    s2_as_text(
      as_s2_geography("MULTIPOLYGON (
        ((40 40, 20 45, 45 30, 40 40)),
        (
          (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
          (30 20, 20 15, 20 25, 30 20)
        )
      )"),
      precision = 15
    ),
    "\\(20 35, 10 30, 10 10, 30 5, 45 20, 20 35\\), \\(30 20, 20 15, 20 25"
  )

  # polygon with a hole in a hole!
  expect_match(
    s2_as_text(
      as_s2_geography("MULTIPOLYGON (
        ((40 40, 20 45, 45 30, 40 40)),
        (
          (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
          (30 20, 20 15, 20 25, 30 20)
        ),
        ((27 21, 21 21, 21 16, 27 21))
      )"),
      precision = 15
    ),
    "\\(\\(27 21, 21 21, 21 16, 27 21\\)\\)\\)"
  )
})

test_that("polygons with holes are interpreted as such by S2", {
  expect_true(
    s2_intersects(
      "MULTIPOLYGON (
        ((40 40, 20 45, 45 30, 40 40)),
        (
          (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
          (30 20, 20 15, 20 25, 30 20),
          (27 21, 21 21, 21 16, 27 21)
        )
      )",
      "POINT (23 19.5)"
    )
  )

  expect_false(
    s2_intersects(
      "MULTIPOLYGON (
        ((40 40, 20 45, 45 30, 40 40)),
        (
          (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
          (30 20, 20 15, 20 25, 30 20)
        )
      )",
      "POINT (23 19.5)"
    )
  )
})

test_that("polygon construction works with oriented = TRUE and oriented = FALSE", {
  polygon_with_bad_hole_nested <- as_s2_geography("MULTIPOLYGON (
    ((40 40, 20 45, 45 30, 40 40)),
    (
      (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
      (30 20, 20 25, 20 15, 30 20)
    )
  )", oriented = FALSE)

  expect_false(s2_intersects(polygon_with_bad_hole_nested, "POINT (23 19.5)"))

  expect_error(
    as_s2_geography("MULTIPOLYGON (
      ((40 40, 20 45, 45 30, 40 40)),
      (
        (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
        (30 20, 20 25, 20 15, 30 20)
      )
    )", oriented = TRUE, check = TRUE),
    "Inconsistent loop orientations"
  )

  expect_silent(
    as_s2_geography("MULTIPOLYGON (
      ((40 40, 20 45, 45 30, 40 40)),
      (
        (20 35, 10 30, 10 10, 30 5, 45 20, 20 35),
        (30 20, 20 25, 20 15, 30 20)
      )
    )", oriented = TRUE, check = FALSE)
  )
})

test_that("Full polygons work", {
  expect_true(s2_intersects(as_s2_geography(TRUE), "POINT(0 1)"))
  expect_wkt_equal(s2_difference(as_s2_geography(TRUE), "POINT(0 1)"), "POLYGON ((0 -90, 0 -90))")
})

test_that("wk crs and geodesic methods are defined", {
  geog <- as_s2_geography("POINT (0 0)")

  expect_identical(wk::wk_crs(geog), wk::wk_crs_longlat())
  expect_true(wk::wk_is_geodesic(geog))

  expect_identical(wk::wk_set_crs(geog, wk::wk_crs_longlat()), geog)
  expect_identical(wk::wk_set_geodesic(geog, TRUE), geog)

  expect_warning(
    wk::wk_set_crs(geog, "EPSG:32620"),
    "is not supported"
  )

  expect_error(wk::wk_set_geodesic(geog, FALSE), "Can't set geodesic")
})

test_that("s2_geography vectors support elementwise assignment", {
  x <- as_s2_geography(c("POINT (0 0)", "POINT (90 0)"))
  x[2] <- as_s2_geography(c("POINT (45 45)"))

  expect_wkt_equal(x, c("POINT (0 0)", "POINT (45 45)"))
})

test_that("DATAPTR can be obtained for s2_geography", {
  Rcpp::cppFunction("bool get_dataptr(SEXP obj) {
    return DATAPTR_RO(obj) != NULL;
  }")

  expect_true(get_dataptr(as_s2_geography("POINT (0 0)")))
})
