##' make_tcga_group
##'
##' make tcga group for given tcga sample names or expression matrix
##'
##' @param exp sample names or an expression matrix/data.frame with sample
##'   names in its columns
##' @export
##' @return a group factor with normal and tumor, corresponding to the sample
##'   names
##' @author Xiaojie Sun
##' @examples
##' k = make_tcga_group(exp_hub1);table(k)
##' k2 = make_tcga_group(colnames(exp_hub1));table(k2)
##' @seealso
##' \code{\link{sam_filter}};\code{\link{match_exp_cl}}

make_tcga_group <- function(exp){
  samples <- if (!is.null(dim(exp))) colnames(exp) else as.character(exp)
  if (is.null(samples) || !length(samples)) stop("no sample names detected,please check it")
  k1 = startsWith(samples,"TCGA")
  if(!any(k1))stop("no TCGA samples detected,please check it")
  k2 = suppressWarnings(as.numeric(substr(samples,14,15))<10)
  group_list = ifelse(k1&k2,"tumor","normal")
  group_list = factor(group_list,levels = c("normal","tumor"))
  return(group_list)
}

##' trans_exp
##'
##' Transform rownames of an expression matrix from Ensembl IDs to gene
##' symbols.
##'
##' @param exp expression matrix with Ensembl IDs as rownames
##' @param mrna_only only keep mrna rows in result
##' @param lncrna_only only keep lncrna rows in result
##' @param gtex logical, whether to include GTEx data
##' @return a transformed expression set with symbol
##' @author Xiaojie Sun
##' @export
##' @examples
##' exp = matrix(rnorm(1000),ncol = 10)
##' rownames(exp) = sample(mRNA_anno$gene_id,100)
##' colnames(exp) = paste0("sample",1:10)
##' k  = trans_exp(exp)
##' @seealso
##' \code{\link{trans_array}}

trans_exp = function(exp,mrna_only = FALSE,
                     lncrna_only = FALSE,gtex = FALSE){
  kd = any(grepl("\\.", rownames(exp)))
  lanno = lnc_anno
  manno = mRNA_anno
  if(!kd){
    lanno$gene_id = sub("\\.\\d*$", "", lanno$gene_id)
    manno$gene_id = sub("\\.\\d*$", "", manno$gene_id)
  }
  map_by_annotation <- function(annotation) {
    idx <- match(rownames(exp), annotation$gene_id)
    keep <- !is.na(idx)
    res <- exp[keep, , drop = FALSE]
    anno <- annotation[idx[keep], , drop = FALSE]
    keep <- !duplicated(anno$gene_name) & !is.na(anno$gene_name) & nzchar(anno$gene_name)
    res <- res[keep, , drop = FALSE]
    rownames(res) <- anno$gene_name[keep]
    res
  }
  mRNA_exp = map_by_annotation(manno)
  lnc_exp = map_by_annotation(lanno)
  message(paste0(nrow(mRNA_exp),
                 " of genes successfully mapping to mRNA,",
                 nrow(lnc_exp),
                 " of genes successfully mapping to lncRNA"))
  if(mrna_only){
    return(mRNA_exp)
  }else if(lncrna_only){
      return(lnc_exp)
  }else{
    expa  = rbind(mRNA_exp,lnc_exp)
    k = !duplicated(rownames(expa))
    expa = expa[k,]
    return(expa)
    message(paste0(sum(!k),
                   " duplicated genes removed"))
    }
}
utils::globalVariables(c("lnc_anno","mRNA_anno"))

##' trans_array
##'
##' transform rownames for microarray or rnaseq expression matrix
##'
##' @param exp microarray or RNA-seq expression matrix
##' @param ids data.frame with original rownames and new rownames
##' @param from colname for original rownames
##' @param to colname for new rownames
##' @param average logical, if TRUE, average rows that map to the same target
##'   symbol; if FALSE, keep the first matched row for each symbol.
##' @return a transformed expression set with new rownames
##' @author Xiaojie Sun
##' @export
##' @examples
##' exp = matrix(1:50,nrow = 10)
##' rownames(exp) = paste0("g",1:10)
##' ids = data.frame(probe_id = paste0("g",1:10),
##'                 symbol = paste0("G",c(1:9,9)))
##' trans_array(exp,ids)
##' trans_array(exp,ids,average = TRUE)
##' @seealso
##' \code{\link{trans_exp}}

trans_array = function(exp,ids,from = "probe_id",
                       to = "symbol",average = FALSE){
  if(!is.character(ids[,from])) ids[,from] = as.character(ids[,from])
  # FIX: keep only IDs that are present in the expression matrix first.
  # Reason: this avoids carrying unmatched annotation rows into dedup/average.
  keep = ids[,from] %in% rownames(exp)
  ids = ids[keep, , drop = FALSE]
  exp = exp[ids[,from], , drop = FALSE]
  keep = !is.na(ids[,to]) & ids[,to] != ""
  ids = ids[keep, , drop = FALSE]
  exp = exp[keep, , drop = FALSE]
  if(average){
    exp = .tinyarray_collapse_mean_rows(exp, ids[,to])
    message(paste0(nrow(exp)," rownames transformed after averaging duplicate rows"))
  }else{
    keep = !duplicated(ids[,to])
    ids = ids[keep, , drop = FALSE]
    exp = exp[keep, , drop = FALSE]
    rownames(exp)=ids[,to]
    message(paste0(nrow(exp)," rownames transformed after duplicate rows removed"))
  }
  return(exp)
}


##' sam_filter
##'
##' drop duplicated samples from the same patients
##'
##' @param exp TCGA or TCGA+GTEx expression matrix from GDC or Xena
##' @return a transformed expression set without duplicated samples
##' @author Xiaojie Sun
##' @export
##' @examples
##' exp_hub1[1:4,1:4]
##' dim(exp_hub1)
##' exp2 = sam_filter(exp_hub1)
##' dim(exp2)
##' g = make_tcga_group(exp_hub1);table(g)
##' table(!duplicated(substr(colnames(exp_hub1[,g=="tumor"]),1,12)))
##' @seealso
##' \code{\link{make_tcga_group}};\code{\link{match_exp_cl}}

sam_filter = function(exp){
  exp = exp[,order(colnames(exp))]
  n1 = ncol(exp)
  group = make_tcga_group(exp)
  exptumor = exp[,group == "tumor"]

  expnormol = exp[,group == "normal"]
  exptumor = exptumor[,!duplicated(substr(colnames(exptumor),1,12))]
  expnormol = expnormol[,!duplicated(substr(colnames(expnormol),1,12))]

  exp = cbind(exptumor,expnormol)
  message(paste("filtered",n1-ncol(exp),"samples."))
  return(exp)
}


##' match_exp_cl
##'
##' match exp and clinical data from TCGA
##'
##' @param exp TCGA  expression set
##' @param cl TCGA clinical data.frame
##' @param id_column which column contains patient IDs, either a column number or
##'   column name
##' @param sample_centric logical, default TRUE; keep all samples from the same
##'   patients. If FALSE, keep only one tumor sample for each patient.
##' @return a transformed clinical data.frame with sample ids.
##' @author Xiaojie Sun
##' @export
##' @examples
##' a = match_exp_cl(exp_hub1,meta1[,2:4],"X_PATIENT")
##' exp_matched = a[[1]]
##' cl_matched = a[[2]]
##' b = match_exp_cl(exp_hub1,meta1[,2:4],"X_PATIENT",sample_centric = FALSE)
##' exp_matched = b[[1]]
##' cl_matched = b[[2]]
##' @seealso
##' \code{\link{make_tcga_group}};\code{\link{sam_filter}}

match_exp_cl = function(exp,cl,id_column = "id",sample_centric = TRUE){
  if (is.numeric(id_column)) {
    if (length(id_column) != 1L || is.na(id_column) || id_column < 1L || id_column > ncol(cl)) {
      stop("id_column must be a valid column number or column name", call. = FALSE)
    }
    colnames(cl)[id_column] = "id"
  } else {
    if (!id_column %in% colnames(cl)) {
      stop("id_column not found in cl", call. = FALSE)
    }
    colnames(cl)[colnames(cl) == id_column] = "id"
  }
  cl = cl[cl$id %in% substr(colnames(exp),1,12),]
  exp = exp[,substr(colnames(exp),1,12) %in% cl$id]
  patient <- substr(colnames(exp),1,12)
  if(nrow(cl)==0) stop("your exp or cl doesn't match,please check them.")
  da = data.frame(sample_id = colnames(exp),
                  id = patient)
  cl = merge(da,cl,by ="id",all.y = TRUE)
  cl = cl[match(colnames(exp),cl$sample_id),]
  if(!sample_centric) {
    Group = make_tcga_group(exp)
    exp = exp[,Group=="tumor"]
    cl = cl[Group=="tumor",]
    cl = cl[order(colnames(exp)),]
    exp = exp[,sort(colnames(exp))]
    exp = exp[,!duplicated(cl$id)]
    cl = cl[!duplicated(cl$id),]
  }
  k = identical(colnames(exp),cl$sample_id)
  if(k)message("match successfully")
  rownames(cl) = cl$sample_id
  return(list(exp_matched = exp,
              cl_matched = cl))
  message("New version of tinyarray canceled global assigning inside the package,
          please obtain exp_matched and cl_matched by split this list result.")
}

##' trans_ensembl_exp
##'
##' Transform rownames of an expression set from Ensembl IDs to gene symbols
##' using the latest gene annotation.
##'
##' @param exp expression matrix with Ensembl IDs as rownames
##' @param mrna_only only keep mrna rows in result
##' @param lncrna_only only keep lncrna rows in result
##' @param species choose human, mouse, or rat; default is human
##' @param average logical, if TRUE, average rows that map to the same target
##'   symbol; if FALSE, keep the first matched row for each symbol.
##' @return a transformed expression set with symbol
##' @author Xiaojie Sun
##' @export
##' @examplesIf requireNamespace("AnnotationDbi", quietly = TRUE) && requireNamespace("org.Hs.eg.db", quietly = TRUE)
##' exp = matrix(rnorm(1000),ncol = 10)
##' rownames(exp) = sample(mRNA_anno$gene_id,100)
##' colnames(exp) = c(paste0("TCGA",1:5),paste0("GTEX",1:5))
##' k  = trans_ensembl_exp(exp)
##' @seealso
##' \code{\link{trans_exp_new}}
trans_ensembl_exp = function(exp,mrna_only = FALSE,
                             lncrna_only = FALSE,
                             species = "human",
                             average = FALSE){
  if(is.data.frame(exp)){exp = as.matrix(exp)}
  if(mrna_only & lncrna_only) stop("please change neither mrna_only or lncrna_only to FALSE")
  rownames(exp) = sub("\\.\\d+$", "", rownames(exp))
  anno = .tinyarray_ensembl_annotation_table(exp, species = species)
  if(mrna_only){
    anno = anno[grepl("protein", tolower(anno$GENETYPE)), , drop = FALSE]
  }else if(lncrna_only){
    anno = anno[grepl("lncrna", tolower(anno$GENETYPE)), , drop = FALSE]
  }
  exp = trans_array(exp,ids = anno,from = "ENSEMBL",to = "SYMBOL",average = average)
  return(exp)
}

##' trans_exp_new
##'
##' Route expression-matrix ID conversion by type.
##'
##' @param exp expression matrix
##' @param mrna_only only keep mrna rows in result when `type = "ensembl"`
##' @param lncrna_only only keep lncrna rows in result when `type = "ensembl"`
##' @param species choose human, mouse, or rat; default is human
##' @param average logical, if TRUE, average rows that map to the same target
##'   symbol; if FALSE, keep the first matched row for each symbol.
##' @param type conversion type, one of `ensembl`, `array`, or `entrez`
##' @param ids annotation table used when `type = "array"`
##' @param id alias of `ids`; kept for compatibility
##' @return a transformed expression set with symbol
##' @author Xiaojie Sun
##' @export
##' @examplesIf requireNamespace("AnnotationDbi", quietly = TRUE) && requireNamespace("org.Hs.eg.db", quietly = TRUE)
##' exp = matrix(rnorm(1000),ncol = 10)
##' rownames(exp) = sample(mRNA_anno$gene_id,100)
##' colnames(exp) = c(paste0("TCGA",1:5),paste0("GTEX",1:5))
##' k  = trans_exp_new(exp)
##' @seealso
##' \code{\link{trans_ensembl_exp}};\code{\link{trans_array}};\code{\link{trans_entrezexp}}
trans_exp_new = function(exp, mrna_only = FALSE,
                         lncrna_only = FALSE,
                         species = "human",
                         average = FALSE,
                         type = c("ensembl", "array", "entrez"),
                         ids = NULL,
                         id = NULL){
  if (is.data.frame(exp)) {
    exp <- as.matrix(exp)
  }
  if (is.null(type) || !length(type)) {
    type <- "ensembl"
  }
  if (identical(tolower(type[[1L]]), "enrtez")) {
    warning("`type = \"enrtez\"` is treated as `type = \"entrez\"`.", call. = FALSE)
    type[[1L]] <- "entrez"
  }
  type <- match.arg(tolower(type[[1L]]), choices = c("ensembl", "array", "entrez"))

  if (type == "ensembl") {
    return(trans_ensembl_exp(
      exp = exp,
      mrna_only = mrna_only,
      lncrna_only = lncrna_only,
      species = species,
      average = average
    ))
  }

  if (!is.null(id)) {
    if (is.null(ids)) {
      ids <- id
    } else {
      warning("`id` is ignored because `ids` is already provided.", call. = FALSE)
    }
  }
  if (!is.null(ids) && type != "array") {
    warning("`ids` is only used when `type = \"array\"`.", call. = FALSE)
  }

  if (type == "array") {
    if (is.null(ids)) {
      stop("please provide `ids` when `type = \"array\"`.", call. = FALSE)
    }
    ids <- .tinyarray_standardize_probe_symbol_table(ids)
    if (!nrow(ids)) {
      stop("`ids` does not contain any valid `probe_id` / `symbol` pairs.", call. = FALSE)
    }
    return(trans_array(exp, ids = ids, from = "probe_id", to = "symbol", average = average))
  }

  trans_entrezexp(exp, species = species, average = average)
}

##' trans_entrezexp
##'
##' Transform rownames of an expression set from Entrez IDs to gene symbols
##' using \code{clusterProfiler::bitr()}.
##'
##' @param entrezexp expression matrix with Entrez IDs as rownames
##' @param species choose human, mouse, or rat; default is human
##' @param average logical, if TRUE, average rows that map to the same target
##'   symbol; if FALSE, keep the first matched row for each symbol.
##' @return a transformed expression set with symbol
##' @author Xiaojie Sun
##' @export
##' @examplesIf requireNamespace("clusterProfiler", quietly = TRUE) && requireNamespace("org.Hs.eg.db", quietly = TRUE)
##' exp = matrix(rnorm(200),ncol = 10)
##' rownames(exp) = c("79691", "56271", "8662", "10394", "55630", "159162", "23541",
##'                   "79723", "54413", "22927", "92342", "23787", "5550", "8924",
##'                   "55274", "866", "8844", "353299", "587", "1473")
##' colnames(exp) = paste0("s",1:10)
##' exp2 = trans_entrezexp(exp)
##' @seealso
##' \code{\link{trans_exp}}
trans_entrezexp = function(entrezexp,species="human",average = FALSE){
  org = .tinyarray_orgdb_for_species(species)
  or = org$orgdb
  if(is.data.frame(entrezexp)){entrezexp = as.matrix(entrezexp)}
  .tinyarray_require_clusterProfiler()
  re = clusterProfiler::bitr(rownames(entrezexp),fromType = "ENTREZID",toType = "SYMBOL",OrgDb = or)
  exp = trans_array(entrezexp,ids = re,from = "ENTREZID",to = "SYMBOL",average = average)
  return(exp)
}

##' @rdname trans_entrezexp
##' @export
trans_entrez_exp <- trans_entrezexp
