#' TENxPeaks: The class to represent 10x Peaks files
#'
#' This class is designed to work with the files denoted with "peak_annotation"
#' in the file name. These are usually produced as tab separated value files,
#' i.e., `.tsv`.
#'
#' @details This class is a straightforward class for handling peak data. It can
#'   be used in conjunction with the `annotation` method on a
#'   `SingleCellExperiment` to add peak information to the experiment. The
#'   ranged data is represented as a `GRanges` class object.
#'
#' @return A `TENxPeaks` class object
#'
#' @exportClass TENxPeaks
.TENxPeaks <- setClass(
    Class = "TENxPeaks",
    contains = "TENxFile"
)

.check_peaks <- function(object) {
    fpath <- path(object)
    if (!endsWith(fpath, "peak_annotation.tsv"))
        "Provide a 10X Peaks file ending in 'peak_annotation.tsv'"
    else
        TRUE
}

.validPeaksFile <- function(object) {
    .check_peaks(object)
}

S4Vectors::setValidity2("TENxPeaks", .validPeaksFile)

#' Import 10x peak annotation files from 10x
#'
#' This constructor function is designed to work with the files denoted with
#' "peak_annotation" in the file name. These are usually produced as tab
#' separated value files, i.e., `.tsv`.
#'
#' @details The output class allows handling of peak data. It can be used in
#'   conjunction with the `annotation` method on a `SingleCellExperiment` to add
#'   peak information to the experiment. The ranged data is represented as a
#'   `GRanges` class object.
#'
#' @inheritParams TENxFile
#'
#' @return A `GRanges` class object of peak locations
#'
#' @examples
#'
#' fi <- system.file(
#'     "extdata", "pbmc_granulocyte_sorted_3k_ex_atac_peak_annotation.tsv",
#'     package = "TENxIO", mustWork = TRUE
#' )
#' peak_file <- TENxPeaks(fi)
#' peak_anno <- import(peak_file)
#' peak_anno
#'
#' example(TENxH5)
#'
#' ## Add peaks to an existing SCE
#' ## First, import the SCE from an example H5 file
#' h5f <- system.file(
#'     "extdata", "pbmc_granulocyte_ff_bc_ex.h5",
#'     package = "TENxIO", mustWork = TRUE
#' )
#' con <- TENxH5(h5f)
#' sce <- import(con)
#' ## auto-import peaks when using annotation<-
#' annotation(sce, name = "peak_annotation") <- peak_file
#' annotation(sce)
#'
#' @export
TENxPeaks <- function(resource, extension, ...) {
    if (missing(extension))
        extension <- .get_ext(resource)
    .TENxPeaks(resource = resource, extension = extension, ...)
}

#' @describeIn TENxPeaks-class Import a peaks_annotation file from 10x as a
#'   `GRanges` representation
#'
#' @inheritParams BiocIO::import
#'
#' @export
setMethod("import", "TENxPeaks", function(con, format, ...) {
    if (identical(Sys.info()[["sysname"]], "Darwin"))
        readr::local_edition(1)
    panno <- readr::read_tsv(
        file = path(con), col_types = c("c", "n", "n", "c", "n", "c")
    )
    grs <- makeGRangesFromDataFrame(panno, keep.extra.columns = TRUE)
    metadata(grs) <- metadata(con)
    grs
})

#' @describeIn TENxPeaks-class Replacement method to add annotation data to a
#'   `SingleCellExperiment`
#'
#' @inheritParams BiocGenerics::annotation
#'
#' @importFrom S4Vectors metadata metadata<-
#' @importFrom BiocGenerics annotation<-
#'
#' @export
setReplaceMethod("annotation", "SingleCellExperiment",
    function(object, ..., value) {
        if (!is(value, "TENxPeaks"))
            stop("'value' must be of class 'TENxPeaks'")
        args <- list(...)
        append <- isTRUE(args[["append"]])
        anno_name <- args[["name"]]
        if (is.null(anno_name))
            anno_name <- "peak_annotation"
        meta <- metadata(object)
        annotated <- "annotation" %in% names(meta)
        if (annotated && !append)
            warning(
                "'append = FALSE'; replacing annotation in metadata",
                call. = FALSE
            )
        vlist <- structure(
            list(import(value)), .Names = anno_name
        )
        if (append)
            vlist <- append(meta[["annotation"]], vlist)
        metadata(object)[["annotation"]] <- vlist
        object
    }
)

#' @describeIn TENxPeaks-class Extraction method to obtain annotation data from
#'   a `SingleCellExperiment` representation
#'
#' @importFrom BiocGenerics annotation
#'
#' @export
setMethod("annotation", "SingleCellExperiment", function(object, ...) {
    metadata(object)[["annotation"]]
})
