
setMethod("droplevels", signature(x="SpatRaster"),
	function(x, level=NULL, layer=1) {
		if (is.null(level)) {
			x@pntr <- x@pntr$droplevels()
			messages(x)
		} else {
			if (is.character(layer)) {
				layer <- match(layer, names(x))
				if (any(is.na(layer))) {
					error("droplevels", "invalid layer")
				}
			}
			x[[layer]][x[[layer]] %in%  level] <- NA
			x@pntr <- x@pntr$droplevels()
			messages(x)
		}
	}
)

setMethod("simplifyLevels", signature(x="SpatRaster"),
	function(x, filename="", overwrite=FALSE, ...) {

		f <- function(r) {
			v <- levels(r)[[1]]
			s <- split(v, v[,2])
			i <- sapply(s, nrow) > 1
			if (any(i)) {
				s <- s[i]
				s <- lapply(s, function(i) cbind(i[-1,1], i[1,1])) 
				s <- do.call(rbind, s)
			}	
			y <- classify(x, s)
			v <- v[-s[,1], ]
			levels(y) <- v
			y
		}
		out <- rast(lapply(x, f))
		if (filename != "") {
			out <- writeRaster(out, filename, overwrite=overwrite, ...)
		}	
		out
	}
)


setMethod("is.factor", signature(x="SpatRaster"),
	function(x) {
		x@pntr$hasCategories()
	}
)

setMethod("as.factor", signature(x="SpatRaster"),
	function(x) {
		x@pntr = x@pntr$makeCategorical(-1, spatOptions())
		messages(x)
		#if (!hasValues(x)) {
		#	error("as.factor", "x has no values")
		#}
		#x <- round(x)
		#u <- unique(x, TRUE)
		#for (i in 1:nlyr(x)) {
		#	set.cats(x, i, data.frame(ID=u[[i]], label=u[[i]], stringsAsFactors=FALSE))
		#}
		#x
	}
)


setMethod("levels", signature(x="SpatRaster"),
	function(x) {
		x <- x@pntr$getCategories()
		lapply(x, function(i) {
			d <- .getSpatDF(i$df)
			if (ncol(d) == 0) return("")
			d[, c(1, max(1, i$index+1))]
		})
	}
)


setMethod("levels<-", signature(x="SpatRaster"),
	function(x, value) {
		x@pntr <- x@pntr$deepcopy()
		if (is.null(value)) {
			x@pntr$removeCategories(-1)
			return(messages(x, "levels<-"))
		} else if (inherits(value, "list")) {
			for (i in 1:length(value)) {
				set.cats(x, i, value[[i]])
			}
		} else {
			set.cats(x, 1, value)
		}
		x
	}
)


combineLevels <- function(x, assign=TRUE) {
	if (nlyr(x) == 1) return(x)
	nms <- names(x)
    lv <- levels(x)
	lv <- lv[sapply(lv, is.data.frame)]
	un <- unique(sapply(lv, names))
	if (length(un) > 2) {
		lv <- lapply(lv, function(i) { colnames(i) <- colnames(lv[[1]]); i})
	}
	lv <- try(do.call(rbind, lv), silent=TRUE)
	if (inherits(lv, "try-error")) { # should not happen anymore
		error("panel", "cannot use combine categories")
	}
	lv <- unique(lv)
	if (length(unique(lv[,1])) < nrow(lv)) {
		error("panel", "cannot combine conflicting categories")			
	}
	lv <- lv[order(lv[,1]), ]
	if (assign) {
		x <- categories(x, 0, lv)
		names(x) <- nms 
		x
	} else {
		lv
	}
}


setMethod ("set.cats" , "SpatRaster",
	function(x, layer=1, value, active=1) {

		if (missing(value)) {
			error("set.cats", "value cannot be missing")
			#return(invisible(x@pntr$setCatIndex(layer-1, index)))
		}

		if (is.character(layer)) {
			layer <- match(layer, names(x))
			if (any(is.na(layer))) {
				error("set.cats", "invalid layer")
			}
		}
		layer <- round(layer)

		if (length(layer) > 1) {
			if (!is.list(value)) {
				error("set.cats", "value should be a list")
			}
			if (length(layer) != length(value)) {
				error("set.cats", "length(value) != length(value)")
			}
			index <- rep_len(active, nlyr(x))
			for (i in 1:length(layer)) {
				ok <- set.cats(x, layer[i], value[[i]], index[i])
				x <- messages(x, "set.cats")
			}
			return(invisible(ok))
		} 

		if (layer < 1) {
			if (layer != 0) {
				error("set.cats", "layer must be >= 0")
			}
			if (nlyr(x) == 1) {
				layer <- 1
			} else {
				if (is.data.frame(value)) {
					value <- replicate(nlyr(x), value, simplify=FALSE)
				} else {
					if (!is.list(value)) {
						error("set.cats", "value should be a list")
					}
					if (length(value) != nlyr(x)) {
						error("set.cats", "length(value) != nlyr(x)")
					}
				}
				index <- rep_len(active, nlyr(x))
				for (i in 1:length(value)) {
					if (!is.null(value[[i]])) {
						ok <- set.cats(x, i, value[[i]], index[i])
						x <- messages(x, "set.cats")
					}
				}
				return(invisible(ok))
			}
		}
		layer <- layer[1]
		if (is.character(layer)) {
			i <- match(layer, names(x))[1]
			if (length(i) == 0) {
				error("set.cats", layer, " is not in names(x)")
			}
			layer <- i
		} else {
			stopifnot(layer > 0 && layer <= nlyr(x))
		}

		if (inherits(value, "list")) {
			value <- value[[1]]
		}
		if (is.null(value)) {
			x@pntr$removeCategories(layer-1)
			messages(x, "set.cats")
			return(invisible(TRUE))
		}

		setname <- FALSE
		if (!is.data.frame(value)) {
			if (is.vector(value) || is.factor(value)) {
				if ((length(value) == 1) && value[1] == "") {
					return(invisible(""))
				}
				warn("set.cats", "setting categories like this is deprecated; use a two-column data.frame instead")
				value <- data.frame(value=0:(length(value)-1), category=value, stringsAsFactors=FALSE)
			} else {
				error("set.cats", "value should be a data.frame")
			}
		} else {
			setname <- TRUE
			if (ncol(value) == 1) {
				error("set.cats", "value should have at least two columns")
			} else {
				if (!is.numeric(value[[1]])) {
					error("set.cats", "the first column of 'value' must be numeric")
				}
				value[,1] <- round(value[[1]])
				if (length(unique(value[[1]])) != nrow(value)) {
					error("set.cats", "duplicate values (IDs) supplied")
				}
			}
		}
		value[[1]] <- as.integer(value[[1]])
		for (i in seq_along(value)) {
			if (is.factor(value[[i]])) {
				value[[i]] <- as.character(value[[i]])
			}
		}

		index <- max(1, min(ncol(value), active))
		if (setname) {
			nms <- names(x)
			cn <- colnames(value)[index+1]
			if (!(tolower(cn) %in% c("histogram", "count", "red", "green", "blue", "alpha", "opacity", "r", "g", "b", "a"))) {
				nms[layer] <- cn
				if (! x@pntr$setNames(nms, FALSE)) {
					error("names<-", "cannot set name")
				}
			}
		}
		if (any(is.na(value[[1]]))) {
			error("set.cats", "you cannot associate a category with NA")
		}
		if (any(table(value[[1]]) > 1)) {
			error("set.cats", "you cannot have duplicate IDs")
		}

		value <- .makeSpatDF(value)
		ok <- x@pntr$setCategories(layer-1, value, index)
		x <- messages(x, "set.cats")
		invisible(ok)
	}
)



setMethod ("categories" , "SpatRaster",
	function(x, layer=1, value, active=1, ...) {
		#... to accept but ignore old argument "index"
		x@pntr <- x@pntr$deepcopy()
		set.cats(x, layer, value, active)
		x
	}
)


setMethod ("activeCat" , "SpatRaster",
	function(x, layer=1) {
		layer <- layer[1]
		if (is.character(layer)) {
			layer = which(layer == names(x))[1]
			if (is.na(layer)) {
				error("activeCat", "invalid layer name")
			}
		}
		if (layer < 1) {
			sapply(1:nlyr(x), function(i) x@pntr$getCatIndex(i-1))
		} else {
			if (!is.factor(x)[layer]) {
				return(NA)
			}
			x@pntr$getCatIndex(layer-1)
		}
	}
)

setMethod("activeCat<-" , "SpatRaster",
	function(x, layer=1, value) {
		if (missing(value)) {
			value <- layer[1]
			layer <- 1
		} else {
			layer <- layer[1]
		}
		if ((layer < 1) | (layer > nlyr(x))) {
			error("activeCat", "invalid layer")
		}
		if (!is.factor(x)[layer]) {
			error("activeCat", "layer is not categorical")
		}
		if (is.character(value)) {
			g <- cats(x)[[layer]]
			value <- which(value == names(g))[1] - 1
			if (is.na(value)) {
				error("activeCat", "invalid category name")
			}
		}
		x <- deepcopy(x)
		if (!x@pntr$setCatIndex(layer-1, value)) {
			error("activeCat", "invalid category index")
		}
		x
	}
)

setMethod("cats" , "SpatRaster",
	function(x, layer) {
		if (!missing(layer)) {
			x <- subset(x, layer, NSE=FALSE)
		}
		cats <- x@pntr$getCategories()
		lapply(1:nlyr(x), function(i) {
			if (cats[[i]]$df$nrow == 0) {
				return(NULL)
			}
			.getSpatDF(cats[[i]]$df)
		})
	}
)


# superseded by levels(x)[[layer]]
..active_cats <- function(x, layer) {
	ff <- is.factor(x)
	if (!any(ff)) {
		return (lapply(ff, function(i) NULL))
	}
	cats <- x@pntr$getCategories()
	x <- lapply(1:length(cats), function(i) {
		if (cats[[i]]$df$nrow == 0) return(NULL)
		r <- .getSpatDF(cats[[i]]$df)
		a <- activeCat(x, i)
		if (a < 0) return(NULL)
		r[, c(1, a+1)]
	})

	if (!missing(layer)) {
		x[[layer]]
	} else {
		x
	}
}


setMethod ("as.numeric", "SpatRaster",
	function(x, index=NULL, filename="", ...) {
		if (!any(is.factor(x))) {
			x <- deepcopy(x)
			x@pntr$setValueType(0)
			return(x)
		}
		if (nlyr(x) > 1) {
			x <- lapply(1:nlyr(x), function(i) as.numeric(x[[i]], index=index))
			x <- rast(x)
			if (filename != "") {
				x <- writeRaster(x, filename, ...)
			}
			return(x)
		}
		g <- cats(x)[[1]]
		if (!is.null(index)) {
			if (is.character(index)) {
				index <- match(index, colnames(g))
				if (is.na(index)) {
					error("as.numeric", "index is not category name")				
				}
				if (index == 1) {
					levels(x) <- NULL
					x@pntr$setValueType(0)
					if (filename != "") {
						x <- writeRaster(x, filename, ...)
					}
					return(x)
				}		
			} else {
				index <- round(index[1])
				if (!((index >= 1) & (index < ncol(g)))) {
					error("as.numeric", "index out of range")
				}
				index <- index + 1
			}
		} else {
			index <- activeCat(x, 1)
			if (index <= 1) {
				levels(x) <- NULL
				x@pntr$setValueType(0)
				if (filename != "") {
					x <- writeRaster(x, filename, ...)
				}
				return(x)
			}
		}
		from <- g[,1]
		to <- g[,index]
		if (!is.numeric(to)) {
			suppressWarnings(toto <- as.numeric(to))
			if (sum(is.na(toto) > sum(is.na(to)))) {
				to <- as.integer(as.factor(to))
			} else {
				to <- toto
			}
		}
		m <- cbind(from, to)
		m <- m[!is.na(m[,1]), ,drop=FALSE]
		classify(x, m, names=names(g)[index], filename, ...)
	}
)



catLayer <- function(x, index, ...) {
		stopifnot(nlyr(x) == 1)
		if (!is.factor(x)) return(x)
		g <- cats(x)[[1]]
		if (!is.null(index)) {
			if (!((index > 1) & (index <= ncol(g)))) {
				error("as.numeric", "invalid index")
			}
		} else {
			index <- set.cats(x, 1)
		}
		from <- g[,1]
		toc <- g[,index]

		addFact <- FALSE
		if (!is.numeric(toc)) {
			addFact <- TRUE
			ton <- as.integer(as.factor(toc))
		} else {
			ton <- toc
		}
		m <- cbind(from, ton)
		m <- m[!is.na(m[,1]), ,drop=FALSE]
		x <- classify(x, m, names=names(g)[index], ...)
		if (addFact) {
			fact <- unique(data.frame(ton, toc))
			names(fact) <- c("ID", names(g)[index])
			fact <- fact[order(fact[,1]), ]
			set.cats(x, 1, fact)
		}
		x
}



setMethod("catalyze", "SpatRaster",
	function(x, filename="", ...) {
		g <- cats(x)
		from_vals <- NULL
		to_vals_list <- list()
		nms <- NULL

		for (i in 1:nlyr(x)) {
			gg <- g[[i]]
			if (nrow(gg) > 0) {
				nms <- colnames(gg)[-1]
				from_vals <- as.numeric(gg[, 1])
				for (j in 2:ncol(gg)) {
					to_vals <- gg[, j]
					if (!is.numeric(to_vals)) {
						suppressWarnings(toto <- as.numeric(to_vals))
						if (sum(is.na(toto)) > sum(is.na(to_vals))) {
							to_vals <- as.integer(as.factor(to_vals))
						} else {
							to_vals <- toto
						}
					}
					to_vals_list[[length(to_vals_list) + 1]] <- as.numeric(to_vals)
				}
				break
			}
		}

		if (length(to_vals_list) > 0 && !is.null(from_vals)) {
			opt <- spatOptions(filename, ...)
			x@pntr <- x@pntr$lookup_catalyze(from_vals, to_vals_list, opt)
			if (!is.null(nms)) names(x) <- nms
			messages(x)
		} else {
			messages(x)
		}
		x
	}
)



setMethod("concats", "SpatRaster",
	function(x, y, filename="", ...) {
		opt <- spatOptions(filename, ...)
		x@pntr = x@pntr$combineCats(y@pntr, opt)
		messages(x, "concats")
	}
)

setMethod("addCats", "SpatRaster",
	function(x, value, merge=FALSE, layer=1) {
		if (!(is.factor(x)[layer])) {
			error("addCat", "the layer has no categories to add to")
		}
		cts <- cats(x)[[layer]]
		nact <- ncol(cts)
		if (merge) {
			if (ncol(value) < 2) {
				error("addCat", "'value' must have at least two columns when using 'merge=TRUE'")
			}
			cts <- merge(cts, value, by=1, all.x=TRUE)
			cts <- cts[order(cts[,1]), ]
		} else {
			if (nrow(cts) != nrow(value)) {
				error("addCat", "the number of categories does not match")
			}
			cts <- cbind(cts, value)
		}
		categories(x, layer=layer, cts, active=nact)
	}
)

