Nothing
      # idNEG
#' Lipids annotation for ESI-
#'
#' Lipids annotation based on fragmentation patterns for LC-MS/MS DIA or DDA data
#' acquired in negative mode. This function compiles all functions writen for
#' ESI- annotations.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 5 seconds.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param lipidClasses classes of interest to run the identification functions.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#' 
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification); and the annotatedPeaklist element shows the original 
#' MS1 peaklist with the annotations on it.
#'
#' @examples
#' \dontrun{
#' msobject <- idNEG(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maribel_alcoriza@iislafe.es>
idNEG <- function(msobject,
                  ppm_precursor = 5,
                  ppm_products = 10,
                  rttol = 5,
                  coelCutoff = 0.8,
                  lipidClasses = c("FA", "FAHFA", "LPC", "LPE", "LPG", "LPI",
                                   "LPS", "PC", "PCo", "PCp", "PE", "PEo", "PEp", 
                                   "PG", "PI", "PS", "Sph", "SphP", "Cer", "CerP", 
                                   "AcylCer", "SM", "CL", "BA"),
                  dbs,
                  verbose = TRUE){
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if ("results" %in% names(msobject$annotation)){
    if(verbose){cat("\n Removing previous results...")}
    msobject$annotation$results <- NULL
    msobject$annotation$detailsAnnotation <- NULL
    msobject$annotatedPeaklist <- NULL
    if(verbose){cat("OK")}
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(lipidClasses %in% c("FA", "FAHFA", "LPC", "LPE", "LPG", "LPI",
                               "LPS", "PC", "PCo", "PCp", "PE", "PEo", "PEp", 
                               "PG", "PI", "PS", "Sph", "SphP", "Cer", "CerP", 
                               "AcylCer", "SM", "CL", "BA"))){
    stop("Lipid classes allowed for negative annotation are: FA, FAHFA, LPC, LPE,
         LPG, LPI, LPS, PC, PCo, PCp, PE, PEo, PEp, PG, PI, PS, Sph, SphP, Cer, 
         CerP, AcylCer, SM, CL and BA")
  }
  if(verbose){cat("\n Starting annotation...")}
  if ("FA" %in% lipidClasses){
    if(verbose){cat("\n  Searching for FA...")}
    msobject <-  idFAneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("FAHFA" %in% lipidClasses){
    if(verbose){cat("\n  Searching for FAHFA...")}
    msobject <-  idFAHFAneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("LPC" %in% lipidClasses){
    if(verbose){cat("\n  Searching for LPC...")}
    msobject <-  idLPCneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("LPE" %in% lipidClasses){
    if(verbose){cat("\n  Searching for LPE...")}
    msobject <-  idLPEneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("LPG" %in% lipidClasses){
    if(verbose){cat("\n  Searching for LPG...")}
    msobject <-  idLPGneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("LPI" %in% lipidClasses){
    if(verbose){cat("\n  Searching for LPI...")}
    msobject <-  idLPIneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("LPS" %in% lipidClasses){
    if(verbose){cat("\n  Searching for LPS...")}
    msobject <-  idLPSneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PC" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PC...")}
    msobject <-  idPCneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PCo" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PCo...")}
    msobject <-  idPConeg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PCp" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PCp...")}
    msobject <-  idPCpneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PE" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PE...")}
    msobject <-  idPEneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PEo" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PEo...")}
    msobject <-  idPEoneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PEp" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PEp...")}
    msobject <-  idPEpneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PG" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PG...")}
    msobject <-  idPGneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PI" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PI...")}
    msobject <-  idPIneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("PS" %in% lipidClasses){
    if(verbose){cat("\n  Searching for PS...")}
    msobject <-  idPSneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("Sph" %in% lipidClasses){
    if(verbose){cat("\n  Searching for Sph...")}
    msobject <-  idSphneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("SphP" %in% lipidClasses){
    if(verbose){cat("\n  Searching for SphP...")}
    msobject <-  idSphPneg(msobject = msobject, ppm_precursor = ppm_precursor,
                           ppm_products = ppm_products, rttol = rttol,
                           coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("Cer" %in% lipidClasses){
    if(verbose){cat("\n  Searching for Cer...")}
    msobject <-  idCerneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("CerP" %in% lipidClasses){
    if(verbose){cat("\n  Searching for CerP...")}
    msobject <-  idCerPneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("AcylCer" %in% lipidClasses){
    if(verbose){cat("\n  Searching for AcylCer...")}
    msobject <-  idAcylCerneg(msobject = msobject, ppm_precursor = ppm_precursor,
                           ppm_products = ppm_products, rttol = rttol,
                           coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("SM" %in% lipidClasses){
    if(verbose){cat("\n  Searching for SM...")}
    msobject <-  idSMneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("CL" %in% lipidClasses){
    if(verbose){cat("\n  Searching for CL...")}
    msobject <-  idCLneg(msobject = msobject, ppm_precursor = ppm_precursor,
                         ppm_products = ppm_products, rttol = rttol,
                         coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if ("BA" %in% lipidClasses){
    if(verbose){cat("\n  Searching for Bile acids...")}
    msobject <-  idBAneg(msobject = msobject, ppm_precursor = ppm_precursor,
                          ppm_products = ppm_products, rttol = rttol,
                          coelCutoff = coelCutoff, dbs = dbs, verbose = verbose)
    if(verbose){cat("OK")}
  }
  if(verbose){cat("\n Preparing output...")}
  msobject <- crossTables(msobject,
                          ppm = ppm_precursor, 
                          rttol = rttol,
                          dbs = dbs)
  if(verbose){cat("OK\n")}
  return(msobject)
}
# idFAneg
#' Fatty Acids (FA) annotation for ESI-
#'
#' FA identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for FA in ESI-. Adducts allowed can
#' be modified in addutcsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idFAneg} function involves 2 steps. 1) FullMS-based
#' identification of candidate FA as M-H or 2M-H. 2) Search of FA class
#' fragments: neutral loss of H2O coeluting with the precursor ion or the
#' molecular ion.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, just MS-only or Subclass level (if any class fragment is defined) are
#' possible) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idFAneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maribel_alcoriza@iislafe.es>
idFAneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M-H", "2M-H"),
                    clfrags = c("fa_M-H", "fa_M-H-H2O"),
                    clrequired = c(FALSE, FALSE),
                    ftype = c("BB", "BB"),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "FA",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("FA" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious FA annotations removed")}
      msobject$annotation$detailsAnnotation$FA <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$fadb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb = list(),
                           intrules  = c(), intConf = list(), nchains = 0,
                           class="FA",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$FA <- list()
    msobject$annotation$detailsAnnotation$FA$candidates <- candidates
    msobject$annotation$detailsAnnotation$FA$classfragments <- classConf$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$FA$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$FA <- list()
  }
  return(msobject)
}
# idFAHFAneg
#' FAHFA annotation for ESI-
#'
#' FAHFA identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for FAHFA in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idFAHFAneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate FAHFA as M-H. 2) Search of FAHFA class fragments:
#' there is't any class fragment by default. 3) Search of specific fragments
#' that inform about chain composition in sn1 (HFA as M-H resulting from the
#' loss of the FA chain) and sn2 (FA chain as M-H). 4) Look for possible
#' chains structure based on the combination of chain fragments. 5) Check
#' intensity rules to confirm chains position. In this case, HFA intensity has
#' to be higher than FA.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idFAHFAneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idFAHFAneg <- function(msobject,
                       ppm_precursor = 5,
                       ppm_products = 10,
                       rttol = 3,
                       rt,
                       adducts = c("M-H"),
                       clfrags = c(),
                       clrequired = c(),
                       ftype = c(),
                       chainfrags_sn1 = c("hfa_M-H"),
                       chainfrags_sn2 = c("fa_M-H"),
                       intrules = c("hfa_sn1/fa_sn2"),
                       rates = c("3/1"),
                       intrequired = c(T),
                       coelCutoff = 0.8,
                       dbs,
                       verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous FAHFA annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "FAHFA",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("FAHFA" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious FAHFA annotations removed")}
      msobject$annotation$detailsAnnotation$FAHFA <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$fahfadb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="FAHFA",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$FAHFA <- list()
    msobject$annotation$detailsAnnotation$FAHFA$candidates <- candidates
    msobject$annotation$detailsAnnotation$FAHFA$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$FAHFA$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$FAHFA$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$FAHFA <- list()
  }
  return(msobject)
}
# idLPCneg
#' Lysophosphocholines (LPC) annotation for ESI-
#'
#' LPC identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for LPC in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments. See \link{chainFrags} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idLPCneg} function involves 3 steps. 1) FullMS-based
#' identification of candidate LPC as M+CH3COO, M-CH3 and M+CH3COO-CH3. To avoid
#' incorrect annotations of PE as PC, candidates which are present just as M-CH3
#' will be ignored. 2) Search of LPC class fragments: 168.0426, 224.0688, lysoPA
#' as M-H or lysoPC as M-CH3 coeluting with the precursor ion. 3) Search of
#' specific fragments that confirm chain composition (FA as M-H).
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, as LPC only have one chain, only Subclass and FA level are possible)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idLPCneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idLPCneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M+CH3COO", "M-CH3", "M+CH3COO-CH3"),
                     clfrags = c(168.0426, 224.0688, "lysopa_M-H", "lysopc_M-CH3"),
                     clrequired = c(F, F, F, F),
                     ftype = c("F", "F", "BB", "BB"),
                     chainfrags_sn1 = c("fa_M-H"),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous LPC annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "LPC",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("LPC" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious LPC annotations removed")}
      msobject$annotation$detailsAnnotation$LPC <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$lysopcdb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=1, sn1)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules = c(), rates = c(),
                                   intrequired = c(), nchains=1,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb,
                           intrules  = c(), intConf, nchains = 1, class="LPC",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPC <- list()
    msobject$annotation$detailsAnnotation$LPC$candidates <- candidates
    msobject$annotation$detailsAnnotation$LPC$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$LPC$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$LPC$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPC <- list()
  }
  return(msobject)
}
# idLPEneg
#' Lysophosphoethanolamines (LPE) annotation for ESI-
#'
#' LPE identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for LPE in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments. See \link{chainFrags} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idLPEneg} function involves 3 steps. 1) FullMS-based
#' identification of candidate LPE as M-H. 2) Search of
#' LPE class fragments: 140.0115, 196.038 and 214.048 coeluting with the
#' precursor ion. If a loss of CH3 group is found coeluting with any candidate,
#' this will be excluded as it is a characteristic fragment of LPC.3) Search of
#' specific fragments that confirm chain composition (FA as M-H).
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, as LPE only have one chain, only Subclass and FA level are possible)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idLPEneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idLPEneg <- function(msobject, ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M-H"),
                     clfrags = c(140.0115, 196.038, 214.048, "lysope_M-CH3"),
                     clrequired = c(F, F, F, "excluding"),
                     ftype = c("F", "F", "F", "BB"),
                     chainfrags_sn1 = c("fa_M-H"),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "LPE",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("LPE" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious LPE annotations removed")}
      msobject$annotation$detailsAnnotation$LPE <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$lysopedb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=1, sn1)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules = c(), rates = c(),
                                   intrequired = c(), nchains=1,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb,
                           intrules  = c(), intConf, nchains = 1, class="LPE",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPE <- list()
    msobject$annotation$detailsAnnotation$LPE$candidates <- candidates
    msobject$annotation$detailsAnnotation$LPE$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$LPE$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$LPE$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPE <- list()
  }
  return(msobject)
}
# idLPGneg
#' Lysophosphoglycerols (LPG) annotation for ESI-
#'
#' LPG identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for LPG in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments. See \link{chainFrags} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idLPGneg} function involves 3 steps. 1) FullMS-based
#' identification of candidate LPG as M-H. 2) Search of LPG class fragments:
#' 152.9958, 227.0326, 209.022 and neutral loss of 74.0359 coeluting with the
#' precursor ion. 3) Search of specific fragments that confirm chain composition
#' (FA as M-H).
#'
#' Results data frame shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, as LPG only have one chain, only Subclass and FA level are possible)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idLPGneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idLPGneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M-H"),
                     clfrags = c(152.9958, 227.0326, 209.022, 74.0359),
                     clrequired = c(F, F, F, F),
                     ftype = c("F", "F", "F", "NL"),
                     chainfrags_sn1 = c("fa_M-H"),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "LPG",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("LPG" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious LPG annotations removed")}
      msobject$annotation$detailsAnnotation$LPG <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$lysopgdb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=1, sn1)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules = c(), rates = c(),
                                   intrequired = c(), nchains=1,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb,
                           intrules  = c(), intConf, nchains = 1, class="LPG",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPG <- list()
    msobject$annotation$detailsAnnotation$LPG$candidates <- candidates
    msobject$annotation$detailsAnnotation$LPG$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$LPG$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$LPG$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPG <- list()
  }
  return(msobject)
}
# idLPIneg
#' Lysophosphoinositols (LPI) annotation for ESI-
#'
#' LPI identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for LPI in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments. See \link{chainFrags} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idLPIneg} function involves 3 steps. 1) FullMS-based
#' identification of candidate LPI as M-H. 2) Search of
#' LPI class fragments: 241.0115, 223.0008, 259.0219 and 297.0375 coeluting
#' with the precursor ion. 3) Search of specific fragments that confirm chain
#' composition (FA as M-H).
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, as LPI only have one chain, only Subclass and FA level are possible)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idLPIneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idLPIneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M-H"),
                     clfrags = c(241.0115, 223.0008, 259.0219, 297.0375),
                     clrequired = c(F, F, F, F),
                     ftype = c("F", "F", "F", "F"),
                     chainfrags_sn1 = c("fa_M-H"),
                     coelCutoff = 0.8,
                     dbs, 
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "LPI",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("LPI" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious LPI annotations removed")}
      msobject$annotation$detailsAnnotation$LPI <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$lysopidb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=1, sn1)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules = c(), rates = c(),
                                   intrequired = c(), nchains=1,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb,
                           intrules  = c(), intConf, nchains = 1, class="LPI",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPI <- list()
    msobject$annotation$detailsAnnotation$LPI$candidates <- candidates
    msobject$annotation$detailsAnnotation$LPI$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$LPI$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$LPI$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPI <- list()
  }
  return(msobject)
}
# idLPSneg
#' Lysophosphoserines (LPS) annotation for ESI-
#'
#' LPS identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for LPS in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments. See \link{chainFrags} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idLPSneg} function involves 3 steps. 1) FullMS-based
#' identification of candidate LPS as M-H and M+Na-2H. 2) Search of
#' LPS class fragments: neutral loss of 87.032 coeluting with the precursor ion.
#' 3) Search of specific fragments that confirm chain composition (FA as M-H).
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, as LPS only have one chain, only Subclass and FA level are possible)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idLPSneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idLPSneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M-H", "M+Na-2H"),
                     clfrags = c(87.032),
                     clrequired = c(F),
                     ftype = c("NL"),
                     chainfrags_sn1 = c("fa_M-H"),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "LPS",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("LPS" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious LPS annotations removed")}
      msobject$annotation$detailsAnnotation$LPS <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$lysopsdb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=1, sn1)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules = c(), rates = c(),
                                   intrequired = c(), nchains=1,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb,
                           intrules  = c(), intConf, nchains = 1, class="LPS",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPS <- list()
    msobject$annotation$detailsAnnotation$LPS$candidates <- candidates
    msobject$annotation$detailsAnnotation$LPS$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$LPS$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$LPS$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$LPS <- list()
  }
  return(msobject)
}
# idPCneg
#' Phosphocholines (PC) annotation for ESI-
#'
#' PC identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PC in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPCneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PC as M+CH3COO, M-CH3 or M+CH3COO-CH3. To avoid
#' incorrect annotations of PE as PC, candidates which are present just as M-CH3
#' will be ignored. 2) Search of PC class fragments: 168.0426, 224.0688 or loss
#' of CH3 coeluting with the precursor ion. 3) Search of specific fragments that
#' inform about chain composition in sn1 (lysoPC as M-CH3 resulting from the
#' loss of the FA chain at sn2) and sn2 (lysoPC as M-CH3 resulting from the loss
#' of sn1 or FA as M-H). 4) Look for possible chains structure based on the
#' combination of chain fragments. 5) Check intensity rules to confirm chains
#' position. In this case, lysoPC from sn1 is at least 3 times more intense than
#' lysoPC from sn2.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPCneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPCneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M+CH3COO", "M-CH3", "M+CH3COO-CH3"),
                    clfrags = c(168.0426, 224.0688, "pc_M-CH3"),
                    clrequired = c(F, F, F),
                    ftype = c("F", "F", "BB"),
                    chainfrags_sn1 = c("lysopc_M-CH3"),
                    chainfrags_sn2 = c("fa_M-H", "lysopc_M-CH3"),
                    intrules = c("lysopc_sn1/lysopc_sn2"),
                    rates = c("3/1"),
                    intrequired = c(T),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PC",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PC" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PC annotations removed")}
      msobject$annotation$detailsAnnotation$PC <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$pcdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  # remove PC which ony appear as M-CH3
  if(length(adducts) > 1 & "M-CH3" %in% adducts){
    candidates <- candidates[candidates$adducts != "M-CH3",]
  }
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains = 2, sn1, sn2)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains = 2,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class = "PC",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PC <- list()
    msobject$annotation$detailsAnnotation$PC$candidates <- candidates
    msobject$annotation$detailsAnnotation$PC$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PC$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PC$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PC <- list()
  }
  return(msobject)
}
# idPConeg
#' Plasmanyl Phosphocholines (PCo) annotation for ESI-
#'
#' PCo identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PCo in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPConeg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PCo as M+CH3COO, M-CH3 or M+CH3COO-CH3. To avoid
#' incorrect annotations of PEo as PCo, candidates which are present just as M-CH3
#' will be ignored. 2) Search of PCo class fragments: 168.0426, 224.0688 or loss
#' of CH3 coeluting with the precursor ion. 3) Search of specific fragments that
#' inform about chain composition in sn1 (LPCo as M-CH3 and M-CH3-H2O resulting 
#' from the loss of the FA chain at sn2) and sn2 (FA as M-H and M-CO2-H). 
#' 4) Look for possible chains structure based on the  combination of chain 
#' fragments. 5) Check intensity rules to confirm chains position. In this case, 
#' FA fragments from sn2 are at least 3 times more intense than LPCo from sn1.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPCneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPConeg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M+CH3COO", "M-CH3", "M+CH3COO-CH3"),
                    clfrags = c(168.0426, 224.0688, "pco_M-CH3"),
                    clrequired = c(F, F, F),
                    ftype = c("F", "F", "BB"),
                    chainfrags_sn1 = c("lysopco_M-CH3", "lysopco_M-CH3-H2O"),
                    chainfrags_sn2 = c("fa_M-H", "fa_M-CO2-H"),
                    intrules = c("lysopco_sn1/fa_sn2"),
                    rates = c(1/3),
                    intrequired = c(T),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PCo",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PCo" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PCo annotations removed")}
      msobject$annotation$detailsAnnotation$PCo <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  
  # candidates search
  candidates <- findCandidates(MS1, dbs$pcodb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  # remove PCo which ony appear as M-CH3 to avoid wrong annotations of PEo
  if(length(adducts) > 1 & "M-CH3" %in% adducts){
    candidates <- candidates[candidates$adducts != "M-CH3",]
  }
  
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PCo",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PCo <- list()
    msobject$annotation$detailsAnnotation$PCo$candidates <- candidates
    msobject$annotation$detailsAnnotation$PCo$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PCo$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PCo$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PCo <- list()
  }
  return(msobject)
}
# idPCpneg
#' Plasmenyl Phosphocholines (PCp) annotation for ESI-
#'
#' PCp identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PCp in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPCpneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PCp as M+CH3COO, M-CH3 or M+CH3COO-CH3. To avoid
#' incorrect annotations of PEp as PCp, candidates which are present just as M-CH3
#' will be ignored. 2) Search of PCp class fragments: 168.0426, 224.0688 or loss
#' of CH3 coeluting with the precursor ion. 3) Search of specific fragments that
#' inform about chain composition in sn1 (LPCp as M-CH3 and M-CH3-H2O resulting 
#' from the loss of the FA chain at sn2) and sn2 (FA as M-H and M-CO2-H). 
#' 4) Look for possible chains structure based on the  combination of chain 
#' fragments. 5) Check intensity rules to confirm chains position. In this case, 
#' FA fragments from sn2 are at least 3 times more intense than LPCp from sn1.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPCpneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPCpneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M+CH3COO", "M-CH3", "M+CH3COO-CH3"),
                     clfrags = c(168.0426, 224.0688, "pcp_M-CH3"),
                     clrequired = c(F, F, F),
                     ftype = c("F", "F", "BB"),
                     chainfrags_sn1 = c("lysopcp_M-CH3", "lysopcp_M-CH3-H2O"),
                     chainfrags_sn2 = c("fa_M-H", "fa_M-CO2-H"),
                     intrules = c("lysopcp_sn1/fa_sn2"),
                     rates = c(1/3),
                     intrequired = c(T),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PCp",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PCp" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PC annotations removed")}
      msobject$annotation$detailsAnnotation$PCp <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  
  # candidates search
  candidates <- findCandidates(MS1, dbs$pcpdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  # remove PCp which ony appear as M-CH3 to avoid wrong annotations of PEp
  if(length(adducts) > 1 & "M-CH3" %in% adducts){
    candidates <- candidates[candidates$adducts != "M-CH3",]
  }
  
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PCp",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PCp <- list()
    msobject$annotation$detailsAnnotation$PCp$candidates <- candidates
    msobject$annotation$detailsAnnotation$PCp$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PCp$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PCp$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PCp <- list()
  }
  return(msobject)
}
# idPEneg
#' Phosphoethanolamines (PE) annotation for ESI-
#'
#' PE identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PE in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPEneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PE as M-H. 2) Search of PE class fragments:
#' 140.0115, 196.038, 214.048 ion coeluting with the precursor ion. If a loss of
#' CH3 group is found coeluting with any candidate, this will be excluded as it
#' is a characteristic fragment of PC. 3) Search of specific fragments that
#' inform about chain composition in sn1 (lysoPE as M-H resulting from the loss
#' of the FA chain at sn2) and sn2 (lysoPE as M-H resulting from the loss
#' of the FA chain at sn1 or FA chain as M-H). 4) Look for possible
#' chains structure based on the combination of chain fragments. 5) Check
#' intensity rules to confirm chains position. In this case, lysoPE from sn1 is
#' at least 3 times more intense than lysoPE from sn2.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPEneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPEneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 5,
                    rt,
                    adducts = c("M-H"),
                    clfrags = c(140.0118, 196.038, 214.048, "pe_M-CH3"),
                    clrequired = c(F, F, F, "excluding"),
                    ftype = c("F", "F", "F", "BB"),
                    chainfrags_sn1 = c("lysope_M-H"),
                    chainfrags_sn2 = c("lysope_M-H", "fa_M-H"),
                    intrules = c("lysope_sn1/lysope_sn2"),
                    rates = c("3/1"),
                    intrequired = c(T),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PE",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PE" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PE annotations removed")}
      msobject$annotation$detailsAnnotation$PE <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$pedb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PE",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PE <- list()
    msobject$annotation$detailsAnnotation$PE$candidates <- candidates
    msobject$annotation$detailsAnnotation$PE$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PE$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PE$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PE <- list()
  }
  return(msobject)
}
# idPEoneg
#' Plasmanyl Phosphoethanolamines (PEo) annotation for ESI-
#'
#' PEo identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PEo in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPEoneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PEo as M-H and M+NaCH3COO. 2) Search 
#' of PEo class fragments: 140.0115, 196.038, 214.048 ion coeluting with the 
#' precursor ion. If a loss of CH3 group is found coeluting with any candidate, 
#' this will be excluded as it is a characteristic fragment of PCo. 3) Search of 
#' specific fragments that inform about chain composition in sn1 (lysoPEo as M-H 
#' and M-H-H2O resulting from the loss of the FA chain at sn2) and sn2 (FA chain 
#' as M-H). 4) Look for possible chains structure based on the combination of 
#' chain fragments. 5) Check intensity rules to confirm chains position. In this 
#' case, FA fragments from sn2 are at least 3 times more intense than LPEo from 
#' sn1.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPEoneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPEoneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 5,
                    rt,
                    adducts = c("M-H", "M+NaCH3COO"),
                    clfrags = c(140.0118, 196.038, 214.048, "peo_M-CH3"),
                    clrequired = c(F, F, F, "excluding"),
                    ftype = c("F", "F", "F", "BB"),
                    chainfrags_sn1 = c("lysopeo_M-H", "lysopeo_M-H-H2O"),
                    chainfrags_sn2 = c("fa_M-H"),
                    intrules = c("lysopeo_sn1/fa_sn2"),
                    rates = c(1/3),
                    intrequired = c(T),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PEo",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PEo" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PEo annotations removed")}
      msobject$annotation$detailsAnnotation$PEo <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  
  # candidates search
  candidates <- findCandidates(MS1, dbs$peodb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PEo",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PEo <- list()
    msobject$annotation$detailsAnnotation$PEo$candidates <- candidates
    msobject$annotation$detailsAnnotation$PEo$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PEo$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PEo$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PEo <- list()
  }
  return(msobject)
}
# idPEpneg
#' Plasmenyl Phosphoethanolamines (PEp) annotation for ESI-
#'
#' PEp identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PEp in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPEpneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PEp as M-H and M+NaCH3COO. 2) Search 
#' of PEp class fragments: 140.0115, 196.038, 214.048 ion coeluting with the 
#' precursor ion. If a loss of CH3 group is found coeluting with any candidate, 
#' this will be excluded as it is a characteristic fragment of PCp. 3) Search of 
#' specific fragments that inform about chain composition in sn1 (lysoPEp as M-H 
#' and M-H-H2O resulting from the loss of the FA chain at sn2) and sn2 (FA chain 
#' as M-H). 4) Look for possible chains structure based on the combination of 
#' chain fragments. 5) Check intensity rules to confirm chains position. In this 
#' case, FA fragments from sn2 are at least 3 times more intense than LPEp from 
#' sn1.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPEoneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPEpneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 5,
                     rt,
                     adducts = c("M-H", "M+NaCH3COO"),
                     clfrags = c(140.0118, 196.038, 214.048, "pep_M-CH3"),
                     clrequired = c(F, F, F, "excluding"),
                     ftype = c("F", "F", "F", "BB"),
                     chainfrags_sn1 = c("lysopep_M-H", "lysopep_M-H-H2O"),
                     chainfrags_sn2 = c("fa_M-H"),
                     intrules = c("lysopep_sn1/fa_sn2"),
                     rates = c(1/3),
                     intrequired = c(T),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PEp",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PEp" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PEp annotations removed")}
      msobject$annotation$detailsAnnotation$PEp <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  
  # candidates search
  candidates <- findCandidates(MS1, dbs$pepdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PEp",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PEp <- list()
    msobject$annotation$detailsAnnotation$PEp$candidates <- candidates
    msobject$annotation$detailsAnnotation$PEp$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PEp$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PEp$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PEp <- list()
  }
  return(msobject)
}
# idPGneg
#' Phosphoglycerols (PG) annotation for ESI-
#'
#' PG identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PG in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPGneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PG as M-H. 2) Search of PG class fragments:
#' 152.9958, 227.0326, 209.022 and neutral loss of 74.0359 coeluting with the
#' precursor ion. 3) Search of specific fragments that inform about chain
#' composition at sn1 (lysoPG as M-H resulting from the loss of the FA chain
#' at sn2) and sn2 (lysoPG as M-H resulting from the loss of the FA chain
#' at sn1 or FA chain as M-H). 4) Look for possible chains structure
#' based on the combination of chain fragments. 5) Check intensity rules to
#' confirm chains position. In this case, lysoPG from sn1 is at least 3 times
#' more intense than lysoPG from sn2.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPGneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPGneg <- function(msobject, ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M-H"),
                    clfrags = c(152.9958, 227.0326, 209.022, 74.0359),
                    clrequired = c(F, F, F, F),
                    ftype = c("F", "F", "F", "NL"),
                    chainfrags_sn1 = c("lysopg_M-H"),
                    chainfrags_sn2 = c("lysopg_M-H", "fa_M-H"),
                    intrules = c("lysopg_sn1/lysopg_sn2"),
                    rates = c("2/1"),
                    intrequired = c(T),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PG",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PG" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PG annotations removed")}
      msobject$annotation$detailsAnnotation$PG <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$pgdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PG",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PG <- list()
    msobject$annotation$detailsAnnotation$PG$candidates <- candidates
    msobject$annotation$detailsAnnotation$PG$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PG$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PG$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PG <- list()
  }
  return(msobject)
}
# idPIneg
#' Phosphoinositols (PI) annotation for ESI-
#'
#' PI identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PI in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPIneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PI as M-H. 2) Search of PI class fragments:
#' 241.0115, 223.0008, 259.0219 and 297.0375 coeluting with the precursor
#' ion. 3) Search of specific fragments that inform about chain composition at
#' sn1 (lysoPI as M-H resulting from the loss of the FA chain at sn2 or lysoPA
#' as M-H if it also losses the head group) and sn2 (lysoPI or lysoPA as M-H
#' resulting from the loss of the FA chain at sn1 or FA chain as M-H). 4) Look
#' for possible chains structure based on the combination of chain fragments.
#' 5) Check intensity rules to confirm chains position. In this case, lysoPI or
#' lysoPA from sn1 is at least 3 times more intense than lysoPI or lysoPA from
#'  sn2.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPIneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPIneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M-H"),
                    clfrags = c(241.0115, 223.0008, 259.0219, 297.0375),
                    clrequired = c(F, F, F, F),
                    ftype = c("F", "F", "F", "F"),
                    chainfrags_sn1 = c("lysopi_M-H", "lysopa_M-H"),
                    chainfrags_sn2 = c("lysopi_M-H", "lysopa_M-H", "fa_M-H"),
                    intrules = c("lysopi_sn1/lysopi_sn2", "lysopa_sn1/lysopa_sn2"),
                    rates = c("3/1", "3/1"),
                    intrequired = c(F, F),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PI",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PI" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PI annotations removed")}
      msobject$annotation$detailsAnnotation$Cer <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$pidb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PI",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PI <- list()
    msobject$annotation$detailsAnnotation$PI$candidates <- candidates
    msobject$annotation$detailsAnnotation$PI$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PI$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PI$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PI <- list()
  }
  return(msobject)
}
# idPSneg
#' Phosphoserines (PS) annotation for ESI-
#'
#' PS identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PS in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idPSneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate PS as M-H or M+Na-2H. 2) Search of PS class
#' fragments: neutral loss of 87.032 (serine) coeluting with the precursor ion.
#' 3) Search of specific fragments that inform about chain composition at sn1
#' (lysoPA as M-H or M-H-H2O resulting from the loss of the FA chain at sn2 and
#' the head group) and sn2 (lysoPA as M-H or M-H-H2O resulting from the loss of
#' the FA chain at sn1 and the head group or FA chain as M-H). 4) Look for
#' possible chains structure based on the combination of chain fragments.
#' 5) Check intensity rules to confirm chains position. In this case, lysoPA
#' from sn1 is at least 3 times more intense than lysoPA from sn2.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idPSneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idPSneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M-H", "M+Na-2H"),
                    clfrags = c(87.032, 152.9958),
                    clrequired = c(F, F),
                    ftype = c("NL", "F"),
                    chainfrags_sn1 = c("lysopa_M-H", "lysopa_M-H-H2O"),
                    chainfrags_sn2 = c("lysopa_M-H", "lysopa_M-H-H2O", "fa_M-H"),
                    intrules = c("lysopa_sn1/lysopa_sn2"),
                    rates = c("3/1"),
                    intrequired = c(T),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "PS",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("PS" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious PS annotations removed")}
      msobject$annotation$detailsAnnotation$PS <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$psdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="PS",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PS <- list()
    msobject$annotation$detailsAnnotation$PS$candidates <- candidates
    msobject$annotation$detailsAnnotation$PS$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$PS$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$PS$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$PS <- list()
  }
  return(msobject)
}
# idSphneg
#' Sphingoid bases (Sph) annotation for ESI-
#'
#' Sph identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for Sph in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idSphneg} function involves 2 steps. 1) FullMS-based
#' identification of candidate Sph as M-H. 2) Search of Sph class fragments:
#' neutral loss of 1 or 2 H2O molecules.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, as Sph only have one chain, only Subclass and FA level are possible)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idSphneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idSphneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3, rt,
                     adducts = c("M-H"),
                     clfrags = c("sph_M-H-H2O", "sph_M-H-2H2O"),
                     clrequired = c(F, F),
                     ftype = c("BB", "BB"),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "Sph",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("Sph" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious Sph annotations removed")}
      msobject$annotation$detailsAnnotation$Sph <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$sphdb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb = list(),
                           intrules  = c(), intConf = list(), nchains = 0,
                           class="Sph",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$Sph <- list()
    msobject$annotation$detailsAnnotation$Sph$candidates <- candidates
    msobject$annotation$detailsAnnotation$Sph$classfragments <- classConf$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$Sph$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$Sph <- list()
  }
  return(msobject)
}
# idSphPpneg
#' Sphingoid bases phosphate (SphP) annotation for ESI-
#'
#' SphP identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for SphP in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idSphpos} function involves 2 steps. 1) FullMS-based
#' identification of candidate SphP as M-H. 2) Search of SphP class fragments:
#' 78.9585, 96.969 or neutral loss of 1 H2O molecule.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (in this
#' case, as SphP only have one chain, only Subclass and FA level are possible)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idSphPneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idSphPneg <- function(msobject,
                      ppm_precursor = 5,
                      ppm_products = 10,
                      rttol = 3,
                      rt,
                      adducts = c("M-H"),
                      clfrags = c(78.9585, 96.9691, "sphP_M-H-H2O"),
                      clrequired = c(F, F, F),
                      ftype = c("F", "F", "BB"),
                      coelCutoff = 0.8,
                      dbs,
                      verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "SphP",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("SphP" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious SphP annotations removed")}
      msobject$annotation$detailsAnnotation$SphP <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$sphPdb, ppm = ppm_precursor,
                               rt = rt, adducts = adducts, rttol = rttol,
                               dbs = dbs, rawData = rawData,
                               coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb = list(),
                           intrules  = c(), intConf = list(), nchains = 0,
                           class="SphP",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$SphP <- list()
    msobject$annotation$detailsAnnotation$SphP$candidates <- candidates
    msobject$annotation$detailsAnnotation$SphP$classfragments <- classConf$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$SphP$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$SphP <- list()
  }
  return(msobject)
}
# idCerneg
#' Ceramides (Cer) annotation for ESI-
#'
#' Cer identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for Cer in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected ratesbetween fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idCerneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate Cer as M-H and M+CH3COO. 2) Search of Cer class
#' fragments: there are no class fragment by default. 3) Search of specific
#' fragments that inform about the sphingoid base (Sph as M-H-2H2O resulting
#' from the loss of the FA chain or loss of part of the sphingoid base) and the
#' FA chain (FA as M-H but with a N instead of an O, what means a mass difference
#' of 1.9918 from the exact mass of the FA or FA as M-H-H2O). 4) Look for 
#' possible chains structure based on the combination of chain fragments. 
#' 5) Check intensity rules to confirm chains position. In this case, there are 
#' no intensity rules by default.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idCerneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idCerneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M-H","M+CH3COO"),
                     clfrags = c(),
                     clrequired = c(),
                     ftype = c(),
                     chainfrags_sn1 = c("NL-nlsph_M-H", "sph_M-H-2H2O", "sph_M-H-H2O"),
                     chainfrags_sn2 = c("fa_Mn-1.9918", "fa_M-H-H2O"),
                     intrules = c(),
                     rates = c(),
                     intrequired = c(),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "Cer",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("Cer" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious Ceramide annotations removed")}
      msobject$annotation$detailsAnnotation$Cer <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$cerdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="Cer",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$Cer <- list()
    msobject$annotation$detailsAnnotation$Cer$candidates <- candidates
    msobject$annotation$detailsAnnotation$Cer$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$Cer$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$Cer$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$Cer <- list()
  }
  return(msobject)
}
# idCerPneg
#' Ceramides phosphate (CerP) annotation for ESI-
#'
#' CerP identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for CerP in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected ratesbetween fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idCerPneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate CerP as M-H. 2) Search of CerP class
#' fragments: 78.9585 and 96.9691. 3) Search of specific fragments that inform 
#' about the sphingoid base (SphP as M-H resulting from the loss of the FA chain) 
#' and the FA chain (FA as M-H but with a N instead of an O, what results in a 
#' mass difference of 1.9918 from the exact mass of the FA, or the difference 
#' between precursor and sn1 chain fragments). 4) Look for possible chains
#' structure based on the combination of chain fragments. 5) Check intensity
#' rules to confirm chains position. In this case, there are no intensity rules
#' by default.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idCerPneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idCerPneg <- function(msobject,
                     ppm_precursor = 5,
                     ppm_products = 10,
                     rttol = 3,
                     rt,
                     adducts = c("M-H"),
                     clfrags = c(78.9585, 96.9691),
                     clrequired = c(F, F),
                     ftype = c("F", "F"),
                     chainfrags_sn1 = c("sphP_M-H"),
                     chainfrags_sn2 = c("fa_Mn-1.9918", ""),
                     intrules = c(),
                     rates = c(),
                     intrequired = c(),
                     coelCutoff = 0.8,
                     dbs,
                     verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "CerP",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("CerP" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious CeramideP annotations removed")}
      msobject$annotation$detailsAnnotation$CerP <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  
  # candidates search
  candidates <- findCandidates(MS1, dbs$cerPdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="CerP",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$CerP <- list()
    msobject$annotation$detailsAnnotation$CerP$candidates <- candidates
    msobject$annotation$detailsAnnotation$CerP$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$CerP$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$CerP$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$CerP <- list()
  }
  return(msobject)
}
# idAcylCerneg
#' Acylceramides (AcylCer) annotation for ESI-
#'
#' AcylCer identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for AcylCer in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the sphingoid base. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param chainfrags_sn3 character vector containing the fragmentation rules for
#' the acyl chain. See \link{chainFrags} for details.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected ratesbetween fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idAcylCerneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate AcylCer as M-H and M+CH3COO. 2) Search of AcylCer 
#' class fragments: no class fragments by default. 3) Search of specific fragments 
#' that inform about the acyl chain (Cer as M-H), the sphingoid base (neutral 
#' loss of 62.0600 of the Sph) and the FA chain (FA as M-H and M-H2O but with a N 
#' instead of an O, what results in a mass differences of 1.9918 and 19.0179 
#' respectively). 4) Look for possible chains structure based on the combination 
#' of chain fragments. 5) Check intensity rules to confirm chains position. In 
#' this case, the fragment coming from the loss of the acyl chain must be at least 
#' 5 times more intense the fragment from the sphingoid base and this one, two 
#' times more intense than the FA chain from sn3.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idAcylCerneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idAcylCerneg <- function(msobject,
                      ppm_precursor = 5,
                      ppm_products = 10,
                      rttol = 3,
                      rt,
                      adducts = c("M-H", "M+CH3COO"),
                      clfrags = c(),
                      clrequired = c(),
                      ftype = c(),
                      chainfrags_sn1 = c("cbdiff-cer_M-H"),
                      chainfrags_sn2 = c("sph_Mn-62.06001", "sph_M-H-H2O"),
                      chainfrags_sn3 = c("fa_Mn-1.9918", "fa_Mn-19.0179"),
                      intrules = c("cbdiff-cer_sn1/sph_sn2", "sph_sn2/fa_sn3"),
                      rates = c("5/1", "2/1"),
                      intrequired = c(T, T),
                      coelCutoff = 0.8,
                      dbs,
                      verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "AcylCer",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("AcylCer" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious AcylCer annotations removed")}
      msobject$annotation$detailsAnnotation$AcylCer <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  
  # candidates search
  candidates <- findCandidates(MS1, dbs$acylcerdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    sn3 <- chainFrags(coelfrags, chainfrags_sn3, ppm_products, candidates, sn1,
                      dbs)
    
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=3, sn1, sn2, sn3)
    
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 3, class="AcylCer",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$AcylCer <- list()
    msobject$annotation$detailsAnnotation$AcylCer$candidates <- candidates
    msobject$annotation$detailsAnnotation$AcylCer$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$AcylCer$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$AcylCer$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$AcylCer <- list()
  }
  return(msobject)
}
# idCLneg
#' Cardiolipines (CL) annotation for ESI-
#'
#' CL identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for CL in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details.
#' @param chainfrags_sn3 character vector containing the fragmentation rules for
#' the chain fragments in sn3 position. See \link{chainFrags} for details.
#' @param chainfrags_sn4 character vector containing the fragmentation rules for
#' the chain fragments in sn4 position. See \link{chainFrags} for details.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}. If some intensity rules should be employed to
#' identify the chains position but they are't known yet, use "Unknown". If it
#' isn't required, leave an empty vector.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idCLneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate CL as M-H or M-2H. 2) Search of CL class fragments:
#' no class fragments are searched by defaults as they use to have bad coelution
#' scores. 3) Search of specific fragments that inform about chain composition
#' at sn1 (lysoPA as M-H-H2O), sn2 (lysoPA as M-H-H2O), sn3 (lysoPA as M-H-H2O)
#' and sn4 (lysoPA as M-H-H2O). 4) Look for possible chains structure based on
#' the combination of chain fragments. 5) Check intensity rules to confirm
#' chains position. For CL there are no intensity rules by default.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idCLneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idCLneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 5,
                    rt,
                    adducts = c("M-H", "M+Na-2H"),
                    clfrags = c(),
                    clrequired = c(),
                    ftype = c(),
                    chainfrags_sn1 = c("lysopa_M-H-H2O"),
                    chainfrags_sn2 = c("lysopa_M-H-H2O"),
                    chainfrags_sn3 = c("lysopa_M-H-H2O"),
                    chainfrags_sn4 = c("lysopa_M-H-H2O"),
                    intrules = c("Unknown"),
                    rates = c(),
                    intrequired = c(),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "CL",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("CL" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious CL annotations removed")}
      msobject$annotation$detailsAnnotation$CL <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$cldb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    sn3 <- chainFrags(coelfrags, chainfrags_sn3, ppm_products, candidates, sn1,
                      dbs)
    sn4 <- chainFrags(coelfrags, chainfrags_sn4, ppm_products, candidates, sn1,
                      dbs)
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=4, sn1, sn2, sn3, sn4)
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=4,
                                   chainsComb)
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 4, class="CL",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$CL <- list()
    msobject$annotation$detailsAnnotation$CL$candidates <- candidates
    msobject$annotation$detailsAnnotation$CL$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$CL$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$CL$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$CL <- list()
  }
  return(msobject)
}
# idBAneg
#' Bile Acids (BA) annotation for ESI-
#'
#' BA identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for BA in ESI-. Adducts allowed can
#' be modified in the adducsTable (dbs argument).
#' @param conjfrag character vector containing the fragmentation rules for
#' the BA-conjugates. By default just taurine and glycine are considered,
#' but baconjdb can be modified to add more possible conjugates.
#' See \link{chainFrags} for details. It can also be an empty vector.
#' @param bafrag character vector containing the fragmentation rules for
#' other BA fragments. See \link{chainFrags} for details. It can be an empty
#' vector.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idBAneg} function involves 3 steps. 1) FullMS-based
#' identification of candidate BA as M-H. 2) Search of BA-conjugate fragments if
#' required. 3) Search of fragments coming from the loss of H2O.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (MS-only
#' if no rules are defined, or Subclass level if they are supported by fragments)
#' and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idBAneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idBAneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M-H"),
                    conjfrag = c("baconj_M-H"),
                    bafrag = c("ba_M-H-H2O", "ba_M-H-2H2O"),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "BA",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("BA" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious BA annotations removed")}
      msobject$annotation$detailsAnnotation$BA <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  # candidates search
  candidates <- findCandidates(MS1, dbs$badb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    # search fragments
    check <- rep(list(vector()), nrow(candidates))
    conjugates <- rep(list(vector()), nrow(candidates))
    bas <- rep(list(vector()), nrow(candidates))
    if (length(conjfrag) > 0){
      conjugates <- chainFrags(coelfrags, conjfrag, ppm_products, dbs = dbs,
                               candidates = candidates)
      for (c in 1:nrow(candidates)){
        if (nrow(conjugates[[c]]) > 0){
          if (dbs$badb[dbs$badb$total ==
                       candidates$cb[c], "conjugate"] %in%
              conjugates[[c]]$cb | dbs$badb[dbs$badb$total ==
                                            candidates$cb[c],
                                            "conjugate"] == ""){
            check[[c]] <- append(check[[c]], TRUE)
          } else {
            check[[c]] <- append(check[[c]], FALSE)
          }
        } else if (dbs$badb[dbs$badb$total ==
                            candidates$cb[c],
                            "conjugate"] == ""){
          check[[c]] <- append(check[[c]], TRUE)
          conjugates[[c]] <- data.frame(0,0,0,0,0,0,0,0)
          colnames(conjugates[[c]]) <- c("cb", "mz", "RT", "int",
                                         "peakID", "coelScore", "db", "adduct")
        } else {
          check[[c]] <- append(check[[c]], FALSE)
          conjugates[[c]] <- data.frame(0,0,0,0,0,0,0,0)
          colnames(conjugates[[c]]) <- c("cb", "mz", "RT", "int",
                                         "peakID", "coelScore", "db", "adduct")
        }
      }
      classConf <- list()
    }
    if (length(bafrag) > 0){
      bas <- chainFrags(coelfrags, bafrag, ppm_products, dbs = dbs,
                        candidates = candidates)
      for (c in 1:nrow(candidates)){
        if(nrow(bas[[c]]) > 0){
          if (candidates$cb[c] %in% bas[[c]]$cb ||
              dbs$badb[dbs$badb$total == candidates$cb[c], "base"]
              %in% bas[[c]]$cb){
            check[[c]] <- append(check[[c]], TRUE)
          } else {
            check[[c]] <- append(check[[c]], FALSE)
          }
        } else {
          check[[c]] <- append(check[[c]], FALSE)
          bas[[c]] <- data.frame(0,0,0,0,0,NA,0,0)
          colnames(bas[[c]]) <- c("cb", "mz", "RT", "int",
                                  "peakID", "coelScore", "db", "adduct")
        }
      }
    }
    check <- matrix(unlist(check), nrow = nrow(candidates), byrow = T)
    classConf$presence <- check
    classConf$passed <- unlist(apply(check, 1, sum)) > 0
    classConf$fragments <- Map(rbind, conjugates, bas)
    classConf$fragments <- lapply(classConf$fragments, function(x){
      x[x$peakID != "0",,drop = FALSE]
    })
    
    # prepare output
    if (length(bafrag) > 0 | length(conjfrag) > 0){
      clfrags <- c("fragment")
    } else {
      clfrags <- c()
    }
    res <- organizeResults(candidates, coelfrags, clfrags = clfrags, classConf = classConf,
                           chainsComb = c(), intrules = c(),
                           intConf = c(), nchains = 0, class="BA",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$BA <- list()
    msobject$annotation$detailsAnnotation$BA$candidates <- candidates
    msobject$annotation$detailsAnnotation$BA$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$BA$chainfragments <- bas$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$BA$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$BA <- list()
  }
  return(msobject)
}
# idSMneg
#' Sphingomyelines (SM) annotation for ESI-
#'
#' SM identification based on fragmentation patterns for LC-MS/MS DIA or DDA
#' data acquired in negative mode.
#'
#' @param msobject an msobject returned by \link{dataProcessing}.
#' @param ppm_precursor mass tolerance for precursor ions. By default, 5 ppm.
#' @param ppm_products mass tolerance for product ions. By default, 10 ppm.
#' @param rttol total rt window for coelution between precursor and product
#' ions. By default, 3 seconds.
#' @param rt rt range where the function will look for candidates. By default,
#' it will search within all RT range in MS1.
#' @param adducts expected adducts for PC in ESI-. Adducts allowed can
#' be modified in adductsTable (dbs argument).
#' @param clfrags vector containing the expected fragments for a given lipid
#' class. See \link{checkClass} for details.
#' @param ftype character vector indicating the type of fragments in clfrags.
#' It can be: "F" (fragment), "NL" (neutral loss) or "BB" (building block).
#' See \link{checkClass} for details.
#' @param clrequired logical vector indicating if each class fragment is
#' required or not. If any of them is required, at least one of them must be
#' present within the coeluting fragments. See \link{checkClass} for details.
#' @param chainfrags_sn1 character vector containing the fragmentation rules for
#' the chain fragments in sn1 position. See \link{chainFrags} for details.
#' @param chainfrags_sn2 character vector containing the fragmentation rules for
#' the chain fragments in sn2 position. See \link{chainFrags} for details. If
#' empty, it will be estimated based on the difference between precursors and
#' sn1 chains.
#' @param intrules character vector specifying the fragments to compare. See
#' \link{checkIntensityRules}.
#' @param rates character vector with the expected rates between fragments given
#' as a string (e.g. "3/1"). See \link{checkIntensityRules}.
#' @param intrequired logical vector indicating if any of the rules is required.
#' If not, at least one must be verified to confirm the structure.
#' @param coelCutoff coelution score threshold between parent and fragment ions.
#' Only applied if rawData info is supplied. By default, 0.8.
#' @param dbs list of data bases required for annotation. By default, dbs
#' contains the required data frames based on the default fragmentation rules.
#' If these rules are modified, dbs may need to be supplied. See \link{createLipidDB}
#' and \link{assignDB}.
#' @param verbose print information messages.
#'
#' @return annotated msobject (list with several elements). The results element
#' is a data frame that shows: ID, lipid class, CDB (total number of carbons
#' and double bounds), FA composition (specific chains composition if it has
#' been confirmed), mz, RT (in seconds), I (intensity), Adducts, ppm (mz error),
#' confidenceLevel (Subclass, FA level, where chains are known but not their
#' positions, or FA position level), peakID, and Score (parent-fragment coelution 
#' score mean in DIA data or relative sum intensity in DDA of all fragments used 
#' for the identification).
#'
#' @details \code{idSMneg} function involves 5 steps. 1) FullMS-based
#' identification of candidate SM as M+CH3COO, M-CH3 or M+CH3COO-CH3. 2) Search 
#' of SM class fragments: 168.0426, 224.0688 or loss of CH3 coeluting with the 
#' precursor ion. 3) Search of specific fragments that inform about chain 
#' composition in sn1 (Sph+phosphocholine as M-CH3-H2O which results in a mass 
#' difference of Sph+150.032) and sn2 (difference between precursor and sn1 chain
#' fragments). 4) Look for possible chains structure based on the
#' combination of chain fragments. 5) Check intensity rules to confirm chains 
#' position. In this case, there are no intensity rules by default.
#'
#' Results data frame shows: ID, lipid class, CDB (total number
#' of carbons and double bounds), FA composition (specific chains composition if
#' it has been confirmed), mz, RT (in seconds), I (intensity, which comes
#' directly from de input), Adducts, ppm (mz error), confidenceLevel (Subclass,
#' FA level, where chains are known but not their positions, or FA position
#' level) and Score (parent-fragment coelution score mean in DIA data or relative 
#' sum intensity in DDA of all fragments used for the identification).
#'
#' @note This function has been writen based on fragmentation patterns
#' observed for three different platforms (QTOF 6550 from Agilent, Synapt G2-Si
#' from Waters and Q-exactive from Thermo), but it may need to be customized for
#' other platforms or acquisition settings.
#'
#' @examples
#' \dontrun{
#' msobject <- idSMneg(msobject)
#' }
#'
#' @author M Isabel Alcoriza-Balaguer <maialba@alumni.uv.es>
idSMneg <- function(msobject,
                    ppm_precursor = 5,
                    ppm_products = 10,
                    rttol = 3,
                    rt,
                    adducts = c("M+CH3COO", "M-CH3", "M+CH3COO-CH3"),
                    clfrags = c(168.0426, 224.0688, "sm_M-CH3"),
                    clrequired = c(F, F, F),
                    ftype = c("F", "F", "BB"),
                    chainfrags_sn1 = c("sph_Mn+150.032"),
                    chainfrags_sn2 = c("fa_Mn-1.9918", ""),
                    intrules = c(),
                    rates = c(),
                    intrequired = c(),
                    coelCutoff = 0.8,
                    dbs,
                    verbose = TRUE){
  ##############################################################################
  # check arguments
  if (msobject$metaData$generalMetadata$polarity != "negative"){
    stop("Data wasn't acquired in negative mode")
  }
  if (missing(dbs)){
    dbs <- assignDB()
  }
  if (!all(c("metaData", "processing", "rawData", "peaklist") %in% names(msobject))){
    stop("Wrong msobject format")
  }
  if (!all(c("MS1", "MS2") %in% names(msobject$rawData))){
    stop("Wrong msobject format")
  }
  if (!msobject$metaData$generalMetadata$acquisitionmode %in% c("DIA", "DDA")){
    stop("Acquisition mode must be DIA or DDA")
  }
  if (!all(adducts %in% dbs[["adductsTable"]]$adduct)){
    stop("Some adducts can't be found at the adductsTable. Add them.")
  }
  if (length(clfrags) > 0){
    if (length(clfrags) != length(clrequired) | length(clfrags) !=
        length(ftype)){
      stop("clfrags, clrequired and ftype should have the same length")
    }
    if (!all(ftype %in% c("F", "NL", "BB"))){
      stop("ftype values allowed are: \"F\", \"NL\" or\"BB\"")
    }
    strfrag <- which(grepl("_", clfrags))
    if (length(strfrag) > 0){
      d <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 1))
      a <- unlist(lapply(strsplit(clfrags[strfrag], "_"), "[[", 2))
      if (!all(a %in% dbs[["adductsTable"]]$adduct)){
        stop("Adducts employed in clfrags also need to be at adductsTable.")
      }
      if (!all(paste(d, "db", sep="") %in% names(dbs))){
        stop("All required dbs must be supplied through dbs argument.")
      }
    }
  }
  ##############################################################################
  # extract data from msobject
  # Peaklist MS1: remove isotopes
  MS1 <- msobject$peaklist$MS1
  MS1 <- MS1[MS1$isotope %in% c("[M+0]"),
             !colnames(MS1) %in% c("isotope", "isoGroup")]
  # Peaklist MS2:
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    MS2 <- msobject$rawData$MS2[,c("mz", "RT", "int", "peakID")]
  } else {
    MS2 <- msobject$peaklist$MS2[,c("mz", "RT", "int", "peakID")]
  }
  rawData <- rbind(msobject$rawData$MS1, msobject$rawData$MS2)
  # if acquisition mode is DDA, extract precursors
  if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
    precursors <- msobject$metaData$scansMetadata[msobject$metaData$scansMetadata$collisionEnergy > 0 &
                                                    msobject$metaData$scansMetadata$msLevel == 2,
                                                  c("RT", "precursor", "Scan")]
  }
  ##############################################################################
  # Remove previous ceramide annotations
  if ("results" %in% names(msobject$annotation)){
    if (nrow(msobject$annotation$results) > 0){
      msobject$annotation$results <- msobject$annotation$results[msobject$annotation$results$Class != "SM",]
    }
  }
  if ("detailsAnnotation" %in% names(msobject$annotation)){
    if("SM" %in% names(msobject$annotation$detailsAnnotation)){
      if(verbose){cat("\nPrevious SM annotations removed")}
      msobject$annotation$detailsAnnotation$SM <- list()
    }
  }
  ##############################################################################
  # set rt limits
  if (missing(rt)){
    rt <- c(min(MS1$RT), max(MS1$RT))
  }
  ##############################################################################
  # Start identification steps
  
  # candidates search
  candidates <- findCandidates(MS1, dbs$smdb, ppm = ppm_precursor, rt = rt,
                               adducts = adducts, rttol = rttol, dbs = dbs,
                               rawData = rawData, coelCutoff = coelCutoff)
  
  if (nrow(candidates) > 0){
    if (msobject$metaData$generalMetadata$acquisitionmode == "DIA"){
      if (nrow(rawData) == 0){
        coelCutoff <- 0 # if no rawData is supplied, coelution score between precursors and fragments will be ignored
      }
      # isolation of coeluting fragments
      coelfrags <- coelutingFrags(candidates, MS2, rttol, rawData,
                                  coelCutoff = coelCutoff)
    } else if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      coelCutoff <- 0
      coelfrags <- ddaFrags(candidates, precursors, rawData, ppm = ppm_products)
    }
    
    # check class fragments
    classConf <- checkClass(candidates, coelfrags, clfrags, ftype, clrequired,
                            ppm_products, dbs)
    
    # search chains fragments
    sn1 <- chainFrags(coelfrags, chainfrags_sn1, ppm_products, dbs = dbs,
                      candidates = candidates)
    sn2 <- chainFrags(coelfrags, chainfrags_sn2, ppm_products, candidates, sn1,
                      dbs)
    
    # combine chain fragments
    chainsComb <- combineChains(candidates, nchains=2, sn1, sn2)
    
    # check chains position based on intensity ratios
    intConf <- checkIntensityRules(intrules, rates, intrequired, nchains=2,
                                   chainsComb)
    
    # prepare output
    res <- organizeResults(candidates, coelfrags, clfrags, classConf, chainsComb, intrules,
                           intConf, nchains = 2, class="SM",
                           acquisitionmode = msobject$metaData$generalMetadata$acquisitionmode)
    
    # update msobject
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$SM <- list()
    msobject$annotation$detailsAnnotation$SM$candidates <- candidates
    msobject$annotation$detailsAnnotation$SM$classfragments <- classConf$fragments
    msobject$annotation$detailsAnnotation$SM$chainfragments <- chainsComb$fragments
    if (msobject$metaData$generalMetadata$acquisitionmode == "DDA"){
      msobject$annotation$detailsAnnotation$SM$coelfrags <- coelfrags
    }
  } else {
    res <- data.frame()
    if ("results" %in% names(msobject$annotation)){
      msobject$annotation$results <- rbind(msobject$annotation$results, res)
    } else {
      msobject$annotation$results <- res
    }
    msobject$annotation$detailsAnnotation$SM <- list()
  }
  return(msobject)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.