##
## solist.R
##
## Methods for class `solist' (spatial object list)
##
##      and related classes 'anylist', 'ppplist', 'imlist'
##
## plot.solist is defined in plot.solist.R
##
## $Revision: 1.21 $ $Date: 2020/05/07 13:04:05 $

anylist <- function(...) {
  x <- list(...)
  class(x) <- c("anylist", "listof", class(x))
  return(x)
}

print.anylist <- function (x, ...) {
  ll <- length(x)
  if(ll == 0) {
    splat("(Zero length list)")
    return(invisible(NULL))
  }
  nn <- names(x)
  if (length(nn) != ll) 
    nn <- paste("Component", seq.int(ll))
  spaceok <- waxlyrical('space')
  for (i in seq_len(ll)) {
    splat(paste0(nn[i], ":"))
    print(x[[i]], ...)
    if(spaceok && i < ll) cat("\n")
  }
  return(invisible(NULL))
}

as.anylist <- function(x) {
  if(inherits(x, "anylist")) return(x)
  if(!is.list(x))
    x <- list(x)
  class(x) <- c("anylist", "listof", class(x))
  return(x)
}
  
"[.anylist" <- function(x, i, ...) {
  cl <- oldClass(x)
  ## invoke list method
  y <- NextMethod("[")
  if(length(y) == 0) return(list())
  class(y) <- cl
  return(y)
}

"[<-.anylist" <- function(x, i, value) {
  as.anylist(NextMethod("[<-"))
}

summary.anylist <- function(object, ...) {
  as.anylist(lapply(object, summary, ...))
}

pool.anylist <- function(x, ...) {
  do.call(pool, append(x, list(...)))
}

## .................... solist .............................

is.sob <- local({
  ## test whether x is a spatial object suitable for solist
  sobjectclasses <- c("ppp", "psp", "im", "owin", 
                      "quad", "tess", "msr",
                      "quadratcount", "quadrattest", 
                      "layered",
                      "funxy", "distfun", "nnfun", 
                      "lpp", "linnet", "linfun", "lintess",  
                      "influence.ppm", "leverage.ppm")
  # Note 'linim' inherits 'im'
  #      'dfbetas.ppm' inherits 'msr'

  is.sob <- function(x) { inherits(x, what=sobjectclasses) }
  is.sob
})
  
solist <- function(..., check=TRUE, promote=TRUE, demote=FALSE, .NameBase) {
  stuff <- list(...)
  if(length(stuff) && !missing(.NameBase) && !any(nzchar(names(stuff))))
    names(stuff) <- paste(.NameBase, seq_along(stuff))
  if((check || demote) && !all(sapply(stuff, is.sob))) {
    if(demote)
      return(as.anylist(stuff))
    stop("Some arguments of solist() are not 2D spatial objects")
  }
  class(stuff) <- c("solist", "anylist", "listof", class(stuff))
  if(promote) {
    if(all(unlist(lapply(stuff, is.ppp)))) {
      class(stuff) <- c("ppplist", class(stuff))
    } else if(all(unlist(lapply(stuff, is.im)))) {
      class(stuff) <- c("imlist", class(stuff))
    }
  }
  return(stuff)
}

as.solist <- function(x, ...) {
  if(inherits(x, "solist") && length(list(...)) == 0) {
    #' wipe superfluous info
    if(inherits(x, "ppplist")) 
      attributes(x)[c("fsplit", "fgroup")] <- NULL
    class(x) <- c("solist", "anylist", "listof")
    return(x)
  }
  #' needs to be enclosed in list() ?
  if(!is.list(x) || (is.sob(x) && !inherits(x, "layered")))
    x <- list(x)
  return(do.call(solist, append(x, list(...))))
}

is.solist <- function(x) inherits(x, "solist")

print.solist <- function (x, ...) {
  what <- if(inherits(x, "ppplist")) "point patterns" else
          if(inherits(x, "imlist")) "pixel images" else "spatial objects"
  splat(paste("List of", what))
  parbreak()
  NextMethod("print")
}


"[.solist" <- function(x, i, ...) {
  cl <- oldClass(x)
  if(!missing(i) && is.owin(i)) {
    ## spatial subset
    y <- lapply(unclass(x), "[", i=i, ...)
  } else {
    ## invoke list method
    y <- NextMethod("[")
  }
  if(length(y) == 0) return(list())
  class(y) <- cl
  return(y)
}
  
"[<-.solist" <- function(x, i, value) {
  ## invoke list method
  y <- NextMethod("[<-")
  ## check again
  return(do.call(solist, y))
}
  
summary.solist <- function(object, ...) {
  x <- lapply(object, summary, ...)
  attr(x, "otype") <-
    if(inherits(object, "ppplist")) "ppp" else
    if(inherits(object, "imlist")) "im" else ""
  class(x) <- c("summary.solist", "anylist")
  x
}

print.summary.solist <- function(x, ...) {
  what <- switch(attr(x, "otype"),
                 ppp="point patterns",
                 im="pixel images",
                 "spatial objects")
  splat("Summary of", length(x), what)
  parbreak()
  NextMethod("print")
}

as.layered.solist <- function(X) {
  layered(LayerList=X)
}

#'  ----- ppplist and imlist ----------------------------
#'  for efficiency only

as.ppplist <- function(x, check=TRUE) {
  if(check) {
    x <- as.solist(x, promote=TRUE, check=TRUE)
    if(!inherits(x, "ppplist"))
      stop("some entries are not point patterns")
  }
  class(x) <- unique(c("ppplist", "solist", "anylist", "listof", class(x)))
  return(x)
}

is.ppplist <- function(x) inherits(x, "ppplist")

as.imlist <- function(x, check=TRUE) {
  if(check) {
    x <- as.solist(x, promote=TRUE, check=TRUE)
    if(!inherits(x, "imlist"))
      stop("some entries are not images")
  }
  class(x) <- unique(c("imlist", "solist", "anylist", "listof", class(x)))
  return(x)
}

is.imlist <- function(x) inherits(x, "imlist")

# --------------- counterparts of 'lapply' --------------------

anylapply <- function(X, FUN, ...) {
  v <- lapply(X, FUN, ...)
  return(as.anylist(v))
}

solapply <- function(X, FUN, ..., check=TRUE, promote=TRUE, demote=FALSE) {
  v <- lapply(X, FUN, ...)
  u <- as.solist(v, check=check, promote=promote, demote=demote)
  return(u)
}

expandSpecialLists <- function(x, special="solist") {
  ## x is a list which may include entries which are lists, of class 'special'
  ## unlist these entries only
  hit <- sapply(x, inherits, what=special) 
  if(!any(hit)) return(x)
  # wrap each *non*-special entry in list()
  x[!hit] <- lapply(x[!hit], list)
  # now strip one layer of list() from all entries
  return(unlist(x, recursive=FALSE))
}
