context("sf")

test_that("sf objects are created",{

  is_sf <- function(x) {
    a <- attributes(x)
    all( a$class == c("sf", "data.frame") ) & a$sf_column == "geometry"
  }

  df <- data.frame(
    id = c(1,1,1,1,1,2,2,2,2,2)
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  res <- sfheaders:::rcpp_sf_point(df, 1:4, "", FALSE )
  expect_true( is_sf( res ) )

  res <- sfheaders:::rcpp_sf_point(df, 1:2, "", FALSE )
  expect_true( is_sf( res ) )

  res <- sfheaders:::rcpp_sf_multipoint(df, 1:4, NULL, "", FALSE )
  expect_true( is_sf( res ) )

  res <- sfheaders:::rcpp_sf_linestring(df, 1:4, NULL, "", FALSE )
  expect_true( is_sf( res ) )

  res <- sfheaders:::rcpp_sf_multilinestring(df, 1:4, NULL, NULL, "", FALSE )
  expect_true( is_sf( res ) )

  res <- sfheaders:::rcpp_sf_polygon(df, 1:4, NULL, NULL, "", FALSE, FALSE )
  expect_true( is_sf( res ) )

  res <- sfheaders:::rcpp_sf_multipolygon(df, 1:4, NULL, NULL, NULL, "", FALSE, FALSE )
  expect_true( is_sf( res ) )
})

test_that("correct number of rows returned",{

  is_sf <- function(x) {
    a <- attributes(x)
    all( a$class == c("sf", "data.frame") ) & a$sf_column == "geometry"
  }

  df <- data.frame(
    id1 = c(1,1,1,1,1,2,2,2,2,2)
    , id2 = c(1,1,2,2,1,1,2,2,3,3)
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  res <- sfheaders:::rcpp_sf_point( df, c(2:3), "", FALSE )
  expect_true( nrow(res) == nrow( df ) )

  res <- sfheaders:::rcpp_sf_multipoint( df, c(2:3), 0L, "", FALSE )
  expect_true( nrow(res) == length( unique( df$id1 ) ) )
  expect_true( all( res$id == unique( df$id1 ) ) )

  res <- sfheaders:::rcpp_sf_linestring( df, c(2:3), 0L, "", FALSE )
  expect_true( nrow(res) == length( unique( df$id1 ) ) )
  expect_true( all( res$id == unique( df$id1 ) ) )

  res <- sfheaders:::rcpp_sf_multilinestring( df, c(2:3), 0L, NULL, "", FALSE )
  expect_true( nrow(res) == length( unique( df$id1 ) ) )
  expect_true( all( res$id == unique( df$id1 ) ) )

  res <- sfheaders:::rcpp_sf_polygon( df, c(2:3), 0L, NULL, "", FALSE, FALSE )
  expect_true( nrow(res) == length( unique( df$id1 ) ) )
  expect_true( all( res$id == unique( df$id1 ) ) )

  res <- sfheaders:::rcpp_sf_multipolygon( df, c(2:3), 0L, NULL, NULL, "", FALSE, FALSE )
  expect_true( nrow(res) == length( unique( df$id1 ) ) )
  expect_true( all( res$id == unique( df$id1 ) ) )

})

test_that("ID order maintained",{

  is_sf <- function(x) {
    a <- attributes(x)
    all( a$class == c("sf", "data.frame") ) & a$sf_column == "geometry"
  }

  df <- data.frame(
    id1 = c(1,1,1,1,1,2,2,2,2,2)
    , id2 = c(1,1,2,2,2,1,2,2,3,3)
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  res <- sfheaders:::rcpp_sf_point( df, c(2:3), "", FALSE )
  m1 <- unclass( res$geometry[[1]] )
  expect_equal( m1[1], df[1, "x"] )
  expect_equal( m1[2], df[1, "y"] )

  m7 <- unclass( res$geometry[[7]] )
  expect_equal( m7[1], df[7, "x"] )
  expect_equal( m7[2], df[7, "y"] )


  res <- sfheaders:::rcpp_sf_multipoint( df, c(2:3), 0L, "", FALSE )
  m1 <- unclass( res$geometry[[1]] )
  m2 <- unclass( res$geometry[[2]] )

  expect_equal( m1[, 1], df[ df$id1 == 1, "x" ] )
  expect_equal( m1[, 2], df[ df$id1 == 1, "y" ] )

  expect_equal( m2[, 1], df[ df$id1 == 2, "x" ] )
  expect_equal( m2[, 2], df[ df$id1 == 2, "y" ] )

  res <- sfheaders:::rcpp_sf_polygon( df, c(2L:3L), 0L, 1L, keep = FALSE, "", close = FALSE )
  m1 <- res$geometry[[1]][[1]]
  m2 <- res$geometry[[1]][[2]]

  m3 <- res$geometry[[2]][[1]]
  m4 <- res$geometry[[2]][[2]]
  m5 <- res$geometry[[2]][[3]]

  expect_equal( m1[, 1], df[ df$id1 == 1 & df$id2 == 1, "x"] )
  expect_equal( m1[, 2], df[ df$id1 == 1 & df$id2 == 1, "y"] )

  expect_equal( m2[, 1], df[ df$id1 == 1 & df$id2 == 2, "x"] )
  expect_equal( m2[, 2], df[ df$id1 == 1 & df$id2 == 2, "y"] )

  expect_equal( m3[, 1], df[ df$id1 == 2 & df$id2 == 1, "x"] )
  expect_equal( m3[, 2], df[ df$id1 == 2 & df$id2 == 1, "y"] )

  expect_equal( m4[, 1], df[ df$id1 == 2 & df$id2 == 2, "x"] )
  expect_equal( m4[, 2], df[ df$id1 == 2 & df$id2 == 2, "y"] )

  expect_equal( m5[, 1], df[ df$id1 == 2 & df$id2 == 3, "x"] )
  expect_equal( m5[, 2], df[ df$id1 == 2 & df$id2 == 3, "y"] )

  df <- data.frame(
    id1 = c(1,1,1,1,1,2,2,2,2,2)
    , id2 = c(1,1,2,2,1,1,2,2,3,3) ## this errored in sf_polygon
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  ## vv no longer errors now we're using rleid()
  #expect_error( sfheaders:::rcpp_sf_polygon( df, c(2:3), 0L, 1L, close = FALSE, keep = "", FALSE ), "geometries - error indexing lines, perhaps caused by un-ordered data?" ) ## because the id2 is out of order
  expect_error( sfheaders:::rcpp_sf_linestring( df, c(2L:3L), 1L, "", keep = FALSE ), "sfheaders - error indexing lines, perhaps caused by un-ordered data?" )
  expect_error( sfheaders:::rcpp_sf_linestring( df, c(2L:3L), 1L, "", keep = TRUE ), "sfheaders - error indexing lines, perhaps caused by un-ordered data?" )
  expect_error( sfheaders:::rcpp_sf_linestring( df, c(2:3), 0, "", keep = FALSE ), "geometries - require either integer or string column indices" )

})

test_that("unordered ids cause issues",{

  df <- data.frame(
    id1 = c(2,2,2,2,2,1,1,1,1,1)
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  res <- sfheaders::sf_linestring(df, linestring_id = "id1")
  #expect_true( !any( res$id == unique( df$id1 ) ) )
  expect_true( all( res$id == unique( df$id1 ) ) )

  ## sub-group order works
  df <- data.frame(
    id1 = c(1,1,1,1,1,2,2,2,2,2)
    , id2 = c(2,2,3,3,3,1,1,1,2,2)
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  res <- sfheaders::sf_polygon(df, polygon_id = "id1", linestring_id = "id2", close = FALSE )
  expect_true( all( res$id == unique( df$id1 ) ) )
  m1 <- res$geometry[[1]][[1]]
  m2 <- res$geometry[[1]][[2]]
  m3 <- res$geometry[[2]][[1]]
  m4 <- res$geometry[[2]][[2]]

  expect_equal( m1, unname( as.matrix( df[ df$id1 == 1 & df$id2 == 2, 3:6 ] ) ) )
  expect_equal( m2, unname( as.matrix( df[ df$id1 == 1 & df$id2 == 3, 3:6 ] ) ) )
  expect_equal( m3, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 1, 3:6 ] ) ) )
  expect_equal( m4, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 2, 3:6 ] ) ) )

  ## sub-group order doesn't work
  df <- data.frame(
    id1 = c(1,1,1,1,1,2,2,2,2,2)
    , id2 = c(2,2,3,3,3,3,3,1,2,2)
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  res <- sfheaders::sf_polygon(df, polygon_id = "id1", linestring_id = "id2", close = FALSE )
  expect_true( all( res$id == unique( df$id1 ) ) )
  m1 <- res$geometry[[1]][[1]]
  m2 <- res$geometry[[1]][[2]]
  m3 <- res$geometry[[2]][[1]]
  m4 <- res$geometry[[2]][[2]]
  m5 <- res$geometry[[2]][[3]]


  ## these tests will pass, but the coordinates will be wronge, becase the ID order is wrong
  expect_equal( m1, unname( as.matrix( df[ df$id1 == 1 & df$id2 == 2, 3:6 ] ) ) )
  expect_equal( m2, unname( as.matrix( df[ df$id1 == 1 & df$id2 == 3, 3:6 ] ) ) )
  expect_equal( m3, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 3, 3:6 ] ) ) )
  expect_equal( m4, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 1, 3:6 ] ) ) )
  expect_equal( m5, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 2, 3:6 ] ) ) )


  df <- data.frame(
    id1 = c(1,1,1,1,1,2,2,2,2,2)
    , id2 = c(2,2,3,3,3,3,3,1,2,2)
    , id3 = c(1,2,1,1,1,1,2,2,1,2)
    , x = 1:10
    , y = 1:10
    , z = 1:10
    , m = 1:10
  )

  res <- sfheaders::sf_multipolygon(df, multipolygon_id = "id1", polygon_id = "id2", linestring_id = "id3", close = FALSE )
  expect_true( all( res$id == unique( df$id1 ) ) )

  m1 <- res$geometry[[1]][[1]][[1]]
  m2 <- res$geometry[[1]][[1]][[2]]
  m3 <- res$geometry[[1]][[2]][[1]]


  m4 <- res$geometry[[2]][[1]][[1]]
  m5 <- res$geometry[[2]][[1]][[2]]

  m6 <- res$geometry[[2]][[2]][[1]]

  m7 <- res$geometry[[2]][[3]][[1]]
  m8 <- res$geometry[[2]][[3]][[2]]


  ## these tests will pass, but the coordinates will be wronge, becase the ID order is wrong
  expect_equal( m1, unname( as.matrix( df[ df$id1 == 1 & df$id2 == 2 & df$id3 == 1, 4:7 ] ) ) )
  expect_equal( m2, unname( as.matrix( df[ df$id1 == 1 & df$id2 == 2 & df$id3 == 2, 4:7 ] ) ) )
  expect_equal( m3, unname( as.matrix( df[ df$id1 == 1 & df$id2 == 3 & df$id3 == 1, 4:7 ] ) ) )

  expect_equal( m4, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 3 & df$id3 == 1, 4:7 ] ) ) )
  expect_equal( m5, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 3 & df$id3 == 2, 4:7 ] ) ) )
  expect_equal( m6, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 1 & df$id3 == 2, 4:7 ] ) ) )
  expect_equal( m7, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 2 & df$id3 == 1, 4:7 ] ) ) )
  expect_equal( m8, unname( as.matrix( df[ df$id1 == 2 & df$id2 == 2 & df$id3 == 2, 4:7 ] ) ) )


})


test_that("R API to sf works",{

  is_sf <- function(x) {
    a <- attributes(x)
    all( a$class == c("sf", "data.frame") ) & a$sf_column == "geometry"
  }

  df <- data.frame(
    multi_poly_id = rep(1,10)
    , poly_id = c(1,1,1,1,1,1,1,2,2,2)
    , line_id = rep(1,10)
    , x = 1:10
    , y = 1:10
  )

  res <- sf_point(obj = df, x = "x", y = "y")
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == nrow( df ) )

  res <- sf_multipoint(obj = df, x = "x", y = "y", multipoint_id = "poly_id")
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == length(unique( df$poly_id ) ) )

  res <- sf_linestring(obj = df, x = "x", y = "y", linestring_id = "poly_id")
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == length(unique( df$poly_id ) ) )

  res <- sf_multilinestring(obj = df, x = "x", y = "y", multilinestring_id = "poly_id")
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == length(unique( df$poly_id ) ) )

  res <- sf_polygon(obj = df, x = "x", y = "y", polygon_id = "poly_id")
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == length(unique( df$poly_id ) ) )

  res <- sf_multipolygon(obj = df, x = "x", y = "y", multipolygon_id = "poly_id")
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == length(unique( df$poly_id ) ) )

})

test_that("string ids work",{

  is_sf <- function(x) {
    a <- attributes(x)
    all( a$class == c("sf", "data.frame") ) & a$sf_column == "geometry"
  }

  df <- data.frame(
    id = letters[1:5]
    , x = 1:5
    , y = 1:5
    , stringsAsFactors = FALSE
  )
  res <- sfheaders:::sf_linestring(
    obj = df
    , x = "x"
    , y = "y"
    , linestring_id = "id"
  )
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == length(unique( df$id ) ) )

  ## and NULL ids
  res <- sfheaders:::sf_linestring(
    obj = df
    , x = "x"
    , y = "y"
    , linestring_id = NULL
  )

  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == 1 )

  ## logical ids
  df <- data.frame(
    id = c(T,T,T,F,F)
    , x = 1:5
    , y = 1:5
    , stringsAsFactors = FALSE
  )

  res <- sfheaders:::sf_linestring(
    obj = df
    , x = "x"
    , y = "y"
    , linestring_id = "id"
  )
  expect_true( is_sf( res ) )
  expect_true( nrow( res ) == length(unique( df$id ) ) )

})


test_that("sf properties are kept",{

  df <- data.frame(
    multi_poly_id = rep(1,10)
    , poly_id = c(1,1,1,1,1,1,1,2,2,2)
    , line_id = rep(1,10)
    , x = 1:10
    , y = 1:10
    , val = letters[1:10]
  )

  res <- sf_point(obj = df, x = "x", y = "y", keep = TRUE )
  expect_true(all(c("multi_poly_id", "poly_id", "line_id","val","geometry") == names(res)))
  expect_true( all( res$multi_poly_id == df$multi_poly_id ) )
  expect_true( all( res$val == df$val ) )

  res <- sf_multipoint(obj = df, x = "x", y = "y", multipoint_id = "poly_id", keep = TRUE )
  expect_true(all(c( "poly_id","multi_poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( res[ 2, ]$val == "h" )

  res <- sf_multipoint(obj = df, x = "x", y = "y", keep = TRUE )
  expect_true(all(c( "multi_poly_id", "poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( nrow( res ) == 1 )

  res <- sf_linestring(obj = df, x = "x", y = "y", linestring_id = "poly_id", keep = TRUE )
  expect_true(all(c("poly_id", "multi_poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( res[ 2, ]$val == "h" )

  res <- sf_multilinestring(obj = df, x = "x", y = "y", multilinestring_id = "poly_id", keep = TRUE )
  expect_true(all(c("poly_id", "multi_poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( res[ 2, ]$val == "h" )

  res <- sf_multilinestring(obj = df, x = "x", y = "y", multilinestring_id = "line_id", keep = TRUE )
  expect_true(all(c("line_id", "multi_poly_id", "poly_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( nrow( res ) == 1 )

  res <- sf_polygon(obj = df, x = "x", y = "y", polygon_id = "poly_id", keep = TRUE )
  expect_true(all(c("poly_id", "multi_poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( res[ 2, ]$val == "h" )

  res <- sf_polygon(obj = df, x = "x", y = "y", polygon_id = "multi_poly_id", keep = TRUE )
  expect_true(all(c("multi_poly_id", "poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( nrow( res ) == 1 )

  res <- sf_multipolygon(obj = df, x = "x", y = "y", multipolygon_id = "poly_id", keep = TRUE )
  expect_true(all(c("poly_id", "multi_poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( res[ 2, ]$val == "h" )

  res <- sf_multipolygon(obj = df, x = "x", y = "y", multipolygon_id = "multi_poly_id", polygon_id = "poly_id", keep = TRUE )
  expect_true(all(c("multi_poly_id", "line_id","val","geometry") == names(res)))
  expect_true( res[ 1, ]$val == "a" )
  expect_true( nrow( res ) == 1 )

})

test_that("geometry colums required when keep = TRUE",{

  df <- data.frame(
    multi_poly_id = rep(1,10)
    , poly_id = c(1,1,1,1,1,1,1,2,2,2)
    , line_id = rep(1,10)
    , x = 1:10
    , y = 1:10
    , val = letters[1:10]
  )

  expect_error( sf_point(obj = df, keep = TRUE ) )
  expect_error( sf_multipoint(obj = df, multipoint_id = "poly_id", keep = TRUE ) )
  expect_error( sf_linestring(obj = df, linestring_id = "poly_id", keep = TRUE ) )
  expect_error( sf_multilinestring(obj = df,  multilinestring_id = "poly_id", keep = TRUE ) )
  expect_error( sf_polygon(obj = df, polygon_id = "poly_id", keep = TRUE ) )
  expect_error(  sf_multipolygon(obj = df, multipolygon_id = "poly_id", keep = TRUE ) )

})

test_that("different property types work",{

  df <- data.frame(
    x = 1:10
    , y = 1:10
    , str = letters[1:10]
    , log = c(T,F)
    , cplx = complex(10)
    , raw = raw(10)
    , stringsAsFactors = FALSE
  )

  res <- sf_linestring(obj = df, x = "x", y = "y", keep = TRUE)
  expect_true( is.raw( res$raw ) )
  expect_true( is.complex( res$cplx ) )
  expect_true( is.logical( res$log ) )
  expect_true( is.character( res$str ) )

  df <- data.frame(
    x = 1
    , y = 1
    , a = I(list(z = "a", m = 1:5))
    , stringsAsFactors = F
  )

  # expect_error(
  #   sf_point(obj = df, x = "x", y = "y", keep = TRUE )
  #   , "sfheaders - unsupported column type using keep = TRUE"
  # )
  #
  # df <- data.frame( x = 1, y = 2 )
  # sf_point( obj = df[0, ], x = "x", y = "y", keep = TRUE )

})

test_that("ids returned correctly - issue 83",{

  df <- data.frame(
    x = 1:5
    , y = 1:5
    , my_id = 1
  )

  expect_true( all( names( sf_multipoint(df, multipoint_id = "my_id") ) == c("my_id", "geometry") ) )
  expect_true( all( names( sf_linestring(df, linestring_id = "my_id") ) == c("my_id", "geometry") ) )
  expect_true( all( names( sf_polygon(df, polygon_id = "my_id") ) == c("my_id", "geometry") ) )
  expect_true( all( names( sf_multipolygon(df, multipolygon_id = "my_id") ) == c("my_id", "geometry") ) )

})




