## interface

## constructors, [, [[, length, width, append, show, detail

## QualityScore

.QualityScore_subset <- function(x, i, j, ..., drop=TRUE) {
    if (!missing(...)) .subset_err()
    initialize(x, quality=quality(x)[i])
}

setMethod("[", c("QualityScore", "ANY", "missing"),
          .QualityScore_subset)

.QualityScore_subset2 <- function(x, i, j, ...) {
    if (!missing(...)) .subset_err()
    quality(x)[[i]]
}

setMethod("[[", c("QualityScore", "ANY", "missing"),
          .QualityScore_subset2)

setMethod(length, "QualityScore", function(x) length(quality(x)))

setMethod(width, "QualityScore", function(x)
          .undefined_method_err(class(x), "width"))

setMethod(append, c("QualityScore", "QualityScore"),
    function(x, values, after=length(x))
{
    initialize(x, quality=append(quality(x), quality(values)))
})

setMethod(detail, "QualityScore", function(x) {
    callNextMethod()
    cat("quality:\n")
    print(quality(x))
})

## NumericQuality

setMethod(width, "NumericQuality",
          function(x) rep(1, length(x)))

setMethod(show, "NumericQuality", function(object) {
    callNextMethod()
    .show_some("quality", quality(object))
})

## IntegerQuality

IntegerQuality <- function(quality=integer(0)) {
    new("IntegerQuality", quality=quality)
}

## Import integer qualities from 454 .qual files
.readFASTA <-
    ## from Biostrings; legacy code but handles numeric values
    function(file, checkComments=TRUE, strip.descs=TRUE)
{
    if (missing(strip.descs))
        warning("use 'strip.descs=FALSE' for compatibility with old version\n",
                "  of readFASTA(), or 'strip.descs=TRUE' to remove the \">\"\n",
                "  at the beginning of the description lines and to get\n",
                "  rid of this warning (see '?readFASTA' for more details)")
    if (is.character(file)) {
        file <- file(file, "r")
        on.exit(close(file))
    } else {
        if (!inherits(file, "connection"))
            stop("'file' must be a character string or connection")
        if (!isOpen(file)) {
            open(file, "r")
            on.exit(close(file))
        }
    }

    s1 <- scan(file=file, what="", sep="\n",
               quote="", allowEscapes=FALSE, quiet=TRUE)
    if (checkComments) {
        ##comments are supposedly lines beginning with semi-colons
        comments <- grep("^;", s1)
        if (length(comments) > 0)
            s1 <- s1[-comments]
    }
    descriptions <- which(substr(s1, 1L, 1L) == ">")
    numF <- length(descriptions)
    if (numF == 0)
        stop("no FASTA sequences found")
    dp <- descriptions + 1L
    dm <- descriptions - 1L
    end <- c(dm[-1], length(s1))
    lapply(seq_len(numF),
           function(i)
           {
               desc <- s1[descriptions[i]]
               if (strip.descs)
                   desc <- substr(desc, 2L, nchar(desc))
               if (end[i] >= dp[i]) {
                   seq <- paste(s1[dp[i]:end[i]], collapse="")
               } else {
                   warning("record \"", desc, "\" contains no sequence")
                   seq <- ""
               }
               list(desc=desc, seq=seq)
           }
    )
}

.readQual <- function(file, reads = NULL) {
  if (!is.null(reads)) { ## a lot faster if the reads are known
    nums <- scan(file, integer(0), n = sum(width(reads)),
                 comment.char = ">")
    inds <- seq_len(length(reads))
    scores <- split(nums, factor(rep(inds, width(reads)), inds))
  } else {
      qual <- .readFASTA(file, strip.descs=TRUE)
      scores <- lapply(strsplit(subListExtract(qual, "seq", TRUE), " +"),
                       as.integer)
      names(scores) <- subListExtract(qual, "desc", TRUE)
  }
  scores
}

setMethod(readQual, "character",
          function(dirPath, reads = NULL, pattern=character(),
                   sample = 1, ...) 
{
    src <- .file_names(dirPath, pattern)[sample]
    scores <- do.call(c, lapply(src, .readQual, reads))
    FastqQuality(sapply(scores, function(elt) rawToChar(as.raw(elt+33))))
})

## MatrixQuality

MatrixQuality <- function(quality=new("matrix")) {
    new("MatrixQuality", quality=quality)
}

.MatrixQuality_subset <- function(x, i, j, ..., drop=FALSE) {
    if (!missing(...)) .subset_err()
    initialize(x, quality=quality(x)[i,, drop=FALSE])
}

setMethod("[", c("MatrixQuality", "ANY", "missing"),
          .MatrixQuality_subset)

.MatrixQuality_subset2 <- function(x, i, j, ...) {
    if (!missing(...)) .subset_err()
    quality(x)[i,]
}

setMethod("[[", c("MatrixQuality", "ANY", "missing"),
          .MatrixQuality_subset2)

setMethod(dim, "MatrixQuality",
          function(x) dim(quality(x)))

setMethod(length, "MatrixQuality",
          function(x) nrow(quality(x)))

setMethod(width, "MatrixQuality",
          function(x) rep(ncol(x), nrow(x)))

## FIXME: implement this, when starts are un-equal
setMethod(narrow, "MatrixQuality",
    function(x, start=NA, end=NA, width=NA, use.names=TRUE)
{
    sew <- solveUserSEW(width(x), start = start, end = end, 
                        width = width)
    if (length(unique(width(sew))) != 1)
        .throw(SRError("UserArgumentMismatch", "%s of %s must be 1",
                       "'length(unique(width()))'",
                       "solved SEW"))
    if (length(unique(start(sew))) == 1) {
        idx <- unique(start(sew)) + seq_len(unique(width(sew))) - 1
        initialize(x, quality=quality(x)[,idx])
    } else {
        .throw(SRError("UserArgumentMismatch",
                       "%s requires unequal 'start' positions",
                       "'narrow,MatrixQuality-method'"))
    }
})

setMethod(append, c("MatrixQuality", "MatrixQuality"),
    function(x, values, after=length(x))
{
    initialize(x, quality=rbind(quality(x), quality(values)))
})

## FastqQuality, SFastqQuality

.FastqQuality_missing <- function(quality, ...) {
    callGeneric(BStringSet(character(0)))
}

.FastqQuality_character <- function(quality, ...) {
    callGeneric(BStringSet(quality), ...)
}

setMethod(FastqQuality, "missing", .FastqQuality_missing)

setMethod(FastqQuality, "character", .FastqQuality_character)

setMethod(FastqQuality, "BStringSet", function(quality, ...) {
    new("FastqQuality", quality=quality)
})

setMethod(SFastqQuality, "missing", .FastqQuality_missing)

setMethod(SFastqQuality, "character", .FastqQuality_character)

setMethod(SFastqQuality, "BStringSet", function(quality, ...) {
    new("SFastqQuality", quality=quality)
})

setAs("FastqQuality", "numeric", function(from) {
    v <- as.vector(t(as(from, "matrix")))
    v[!is.na(v)]
})

setAs("FastqQuality", "matrix", function(from) {
    .Call(.alphabet_as_int, quality(from), 0:255-33L)
})

setAs("FastqQuality", "PhredQuality", function(from)
{
    as(quality(from), "PhredQuality")
})

setAs("SFastqQuality", "matrix", function(from) {
    .Call(.alphabet_as_int, quality(from), 0:255-64L)
})

setAs("SFastqQuality", "SolexaQuality", function(from)
{
    as(quality(from), "SolexaQuality")
})

setMethod(width, "FastqQuality",
    function(x) width(quality(x)))

setMethod(reverse, "FastqQuality", function(x, ...) {
    do.call(class(x), list(reverse(quality(x))))
})

setMethod(narrow, "FastqQuality",
    function(x, start=NA, end=NA, width=NA, use.names=TRUE)
{
    initialize(x, quality=narrow(quality(x), start, end, width,
                    use.names))
})

setMethod(alphabet, "FastqQuality",
          function(x, ...) rawToChar(as.raw(32:126), TRUE))

setMethod(encoding, "FastqQuality",
    function(x)
{
    alf <- alphabet(x)
    x <- setNames(seq(-1, length.out=length(alf)), alf)
    x[x >= 0]
})

setMethod(encoding, "SFastqQuality",
    function(x)
{
    alf <- alphabet(x)
    x <- setNames(seq(-32, length.out=length(alf)), alf)
    x[x >= -5 & x <= 41]
})

setMethod(show, "FastqQuality", function(object) {
    callNextMethod()
    cat("quality:\n")
    show(quality(object))
})

.FastqQuality_af <-
    function(x, as.prob=FALSE, ...) 
{
    res <- callGeneric(quality(x), as.prob=as.prob, ...)
    if (is(res, "matrix")) {
        res <- res[,1+32:126, drop=FALSE]
        colnames(res) <- alphabet(x)
    } else {
        res <- res[1+32:126]
        names(res) <- alphabet(x)
    }
    res
}

setMethod(alphabetFrequency, "FastqQuality", .FastqQuality_af)

.FastqQuality_abc <- function(stringSet, alphabet, ...) {
    if (missing(alphabet))
        alphabet <- Biostrings::alphabet(stringSet)
   .abc_BStringSet(quality(stringSet), alphabet=alphabet, ...)
}

setMethod(alphabetByCycle, "FastqQuality", .FastqQuality_abc)

.SFastqQuality_ascore <- function(object, score=0:255-64L, ...) {
    .Call(.alphabet_score, quality(object), as.numeric(score))
}

setMethod(alphabetScore, "SFastqQuality", .SFastqQuality_ascore)

.FastqQuality_ascore <- function(object, score=0:255-33L, ...) {
    .Call(.alphabet_score, quality(object), as.numeric(score))
}

setMethod(alphabetScore, "FastqQuality", .FastqQuality_ascore)

setMethod(alphabetScore, "PhredQuality", function(object, ...) {
    .Call(.alphabet_score, object, as.numeric(0:255 - 33L))
})

setMethod(trimTailw, "FastqQuality",
    function(object, k, a, halfwidth, ..., ranges=FALSE)
{
    rng <- callGeneric(quality(object), k, a, halfwidth, ...,
                       alphabet=alphabet(object), ranges=TRUE)
    if (ranges) rng
    else narrow(object, 1L, end(rng))[0L != width(rng)]
})

setMethod(trimTails, "FastqQuality",
    function(object, k, a, successive=FALSE, ..., ranges=FALSE)
{
    rng <- callGeneric(quality(object), k, a, successive, ...,
                       alphabet=alphabet(object), ranges=TRUE)
    if (ranges) rng
    else narrow(object, 1L, end(rng))[0L != width(rng)]
})

setMethod(trimEnds, "FastqQuality",
    function(object, a, left=TRUE, right=TRUE, relation=c("<=", "=="),
             ..., ranges=FALSE)
{
    rng <- callGeneric(quality(object), a, left, right, relation,
                       ..., alphabet=alphabet(object), ranges=TRUE)
    if (ranges) rng
    else narrow(object, 1L, end(rng))[0L != width(rng)]
})


setMethod(srrank, "FastqQuality", .forward_xq)

setMethod(srorder, "FastqQuality", .forward_xq)

setMethod(srsort, "FastqQuality", .forward_xq)

setMethod(srduplicated, "FastqQuality", .forward_xq)

.FastqQuality_srduplicated<- function(x, incomparables=FALSE, ...) {
    callGeneric(x=quality(x), ...)
}

setMethod(srduplicated, "FastqQuality", .FastqQuality_srduplicated)
