utils::globalVariables(c("clootl_data"))


#' Extract a complete or pre-pruned phylogeny from the clootl datastore
#'
#' @description This function extracts one or more phylogenies in the desired taxonomy and tree version.
#' It defaults to the pre-packaged summary trees, but can also be used to extract sets of
#' phylogenies expressing uncertainty, once they have been downloaded from the online repository.
#' @param species A character vector either of scientific names (directly as they come out of the
#' eBird taxonomy, i.e. without underscores) or of six-letter eBird species codes. Any elements of
#' the species vector that do not match a species-level taxon in the specified eBird taxonomy
#' will result in an error. eBird taxonomy files can be accessed using [taxonomyGet()]. Default is set to "all_species".
#' @param label_type Either "scientific" or "code". Default is set to "scientific".
#' @param taxonomy_year The eBird taxonomy year the tree should be output in. Current options
#' are 2021-2024. Both numeric and character inputs are acceptable here. Any value
#' aside from these years will result in an error. Default is most recent year.
#' @param data_path Default to FALSE. If a summary, dated tree is desired, this is sufficient
#' and does not need to be modified. However, if a user wishes to extract a set of complete
#' dated trees, for example to iterate an analysis across a cloud of trees, or to use an
#' older version of the tree than the current one packed in the data object, this function
#' can also accept a path to the downloaded set of trees. If you have already downloaded the AvesData repo
#' available at https://github.com/McTavishLab/AvesData use data_path= the path to the download location.
#' Alternately, you can download the full data repo using [get_avesdata_repo()]. This approach will download the data and
#' set an environmental variable AVESDATA_PATH. When AVESDATA_PATH is set, the data_path will default to this value.
#' To manually set AVESDATA_PATH to the location of your downloaded AvesData repo use [set_avesdata_repo_path()]
#' @param version The desired version of the tree. Default to the most recent
#' version of the tree. Other versions available are listed in clootl_data$versions and can be
#' passed as a character string or as numeric.
#' @param force Default to FALSE.  If FALSE a tree will be returned only if there is an exact match in the tree
#' to all species requested. This function first ensures that the requested output species overlap with species-level
#' taxa in the requested eBird taxonomy. If they do not, the function will error out. The onus is
#' on the user to ensure the requested taxa are valid. This is critical to ensure no unexpected
#' analysis hiccups later--you don't want to find out many steps later that your dataset doesn't
#' match your phylogeny.
#' If force=TRUE even if there is is not a match to all taxa in the requested species list, a tree will be
#' returned for the species that do match.
#'
#'
#' @return One or more phylogenies of the specified taxa in the specified eBird taxonomy version and clootl
#' tree version.
#'
#' @author Eliot Miller, Luna Sanchez Reyes, Emily Jane McTavish
#'
#' @export
#'
#' @import ape
#'
#' @examples
#' ex1 <- extractTree(species=c("amerob", "canwar", "reevir1", "yerwar", "gockin"),
#'    label_type="code")
#' ex2 <- extractTree(species=c("Turdus migratorius",
#'                              "Setophaga dominica",
#'                              "Setophaga ruticilla",
#'                              "Sitta canadensis"),
#'    label_type="scientific",
#'    taxonomy_year="2025",
#'    version="1.6")
#'
extractTree <- function(species="all_species",
                        label_type="scientific",
                        taxonomy_year=2025,
                        version="1.6",
                        data_path=FALSE,
                        force=FALSE){
  label_type <- match.arg(label_type,c('code','scientific'))
  utils::data("clootl_data")


  version <- as.character(version)
  if (!is.element(version, clootl_data$versions)){
    stop("Tree version not recognized: ", version) ## TODO print out actual list from clootl_data$versions
  }

  taxonomy_year <- as.character(taxonomy_year)
  if (!is.element(taxonomy_year, clootl_data$tax_years)){
    stop("Requested taxonomy year unavailable: ", taxonomy_year)
  }

  combo = paste(version, "_tax", taxonomy_year, sep="")
  if (!is.element(combo, clootl_data$combinations)){
      stop(paste(combo, "This combination of tree version and taxonomy year is not available.
        Available combinations are:\n", paste(clootl_data$combinations, collapse=",\n")))
  }


  if (!is.element(label_type, c("code", "scientific")))
  {
    stop("label_type must be set to either 'code' or 'scientific'")
  }

  if((Sys.getenv('AVESDATA_PATH') == "") & (data_path==FALSE) & (version!='1.6')){
      stop("Only tree version 1.6 taxonomy year 2025 is currently packaged with clootl.
      To get alternate tree versions and taxonomy years, run get_avesdata_repo()
      or set path to Aves Data repo using set_avesdata_repo(path),
      or use the argument data_path = AvesData-path")
    }

  if((version=='1.6') & (taxonomy_year==2025)){
    utils::data("clootl_data")
    taxonomyYear <- paste("year", taxonomy_year, sep="")
    tax <- clootl_data$taxonomies[[`taxonomyYear`]]
    fullTree <- clootl_data$trees[[paste("Aves_", version, sep="")]]$summary.trees[[taxonomyYear]]
  }
  else{
      tax <- taxonomyGet(taxonomy_year, data_path)
      fullTree <- treeGet(version, taxonomy_year, data_path)
  }


  species <- as.list(species)
  if(species[1]=="all_species" & label_type=="scientific")
  {
    return(fullTree)
  }

  #if species is set to all.species, redefine species as the full set of taxa
  if(label_type=="scientific")
  {
    #identify mismatches

    issues <- setdiff(species, tax$SCI_NAME)
    species <- intersect(tax$SCI_NAME, species)
    #if there are any, throw an error
     if(length(issues) > 0 & force==FALSE)
    {
      message("Some of your provided species names do not match with names in the requested year's eBird taxonomy:")
      message(paste(length(issues),"names did not match.", length(species), "names did match", sep=" "))
      message("These names did not match:")
      message(paste(issues, collapse = "\n"))
      stop("Re-run with extractTree force=TRUE to generate a tree just for the names that do match.")
    }
      else if(length(issues) > 0 & force==TRUE)
    {
      message("Some of your provided species names do not match with names in the requested year's eBird taxonomy:")
      message(paste(length(issues),"names did not match.", length(species), "names did match", sep=" "))
      message("These names did not match:")
      message(paste(issues, collapse = "\n"))
      message("Argument force=TRUE, returning a tree for the species that match.")
    }

  }
  #check whether the input species are valid
  if(label_type=="code")
  {
     if(species[1]=="all_species")
     {
       species <- tax$SPECIES_CODE
      }

    #identify mismatches
    issues <- setdiff(species, tax$SPECIES_CODE)
    species <- intersect(tax$SPECIES_CODE, species)

    #if there are any, throw an error
    if(length(issues) > 0 & force==FALSE)
    {
      message("Some of your provided species codes do not match with codes in the requested year's eBird taxonomy:")
      message(paste(length(issues),"codes did not match.", length(species), "codes did match", sep=" "))
      message("These codes did not match:")
      message(paste(issues, collapse = "\n"))
      stop("Re-run with extractTree force=TRUE to generate a tree just for the names that do match.")
    }
    if(length(issues) > 0 & force==TRUE)
    {
      message("Some of your provided species names do not match with names in the requested year's eBird taxonomy:")
      message(paste(length(issues),"names did not match.", length(species), "names did match", sep=" "))
      message("These names did not match:")
      message(paste(issues, collapse = "\n"))
      message("Argument force=TRUE, returning a tree for the species that match.")
    }
      ## Prune to requested species
      #swap the scientific names for species codes
      newNames <- data.frame(order=1:length(fullTree$tip.label), orig=fullTree$tip.label)
      #merge and re-sort
      newNames <- merge(newNames, tax[,c("SCI_NAME","SPECIES_CODE")], by.x="orig", by.y="SCI_NAME")
      newNames <- newNames[order(newNames$order),]

      #swap names
      fullTree$tip.label <- newNames$SPECIES_CODE
    }


  #now prune the tree and extract. if species is the full set, no pruning will occur
  pruned <- drop.tip(fullTree, setdiff(fullTree$tip.label, species))

  message(paste("This extracted tree is from version ", version, " and taxonomy year ", taxonomy_year, ".
    Please cite the version, clootl, and the contributing studies using getCitations(tree, version).", sep=""))
  return(pruned)
}


#' Load a bird taxonomy into the R environment
#'
#' @description `taxonomyGet` either reads a taxonomy file and loads it
#' as a `data frame`, or loads the default taxonomy data object.
#'
#' @inheritParams extractTree
#' @param data_path Default to FALSE. If a summary, dated tree is desired, this is sufficient
#' and does not need to be modified. However, if a user wishes to extract a set of complete
#' dated trees, for example to iterate an analysis across a cloud of trees, or to use an
#' older version of the tree than the current one packed in the data object, this function
#' can also accept a path to the downloaded set of trees. If you have already downloaded the AvesData repo
#' available at https://github.com/McTavishLab/AvesData use data_path= the path to the download location.
#' Alternately, you can download the full data repo using [get_avesdata_repo()]. This approach will download the data and
#' set an environmental variable AVESDATA_PATH. When AVESDATA_PATH is set, the data_path will default to this value.
#' To manually set AVESDATA_PATH to the location of your downloaded AvesData repo use [set_avesdata_repo_path()]
#' @param from_file Default to FALSE. If TRUE forces taxonomyGet to use a local copy of the the taxonomy.
#' This is useful for testing changes and/or updating the clootl_data object.
#' @details This will return a data object that has the taxonomy of the requested year.
#' @return A `data.frame` with 17 columns of taxonomic information: order, species code, taxon concept, common name, scientific name, family, OpenTree Taxonomy data, etc.
#' @export
#'
taxonomyGet <- function(taxonomy_year, data_path=FALSE, from_file=FALSE){
  if (from_file==FALSE){
      taxonomy_year = as.character(taxonomy_year)
        if (!is.element(taxonomy_year, clootl_data$tax_years)){
          stop("Requested taxonomy year unavailable: ", taxonomy_year)
        }
      utils::data("clootl_data")
      taxonomyYear <- paste("year", taxonomy_year, sep="")
      tax <- clootl_data$taxonomies[[`taxonomyYear`]]
  } else {
    if (data_path==FALSE || data_path==""){
        data_path = Sys.getenv('AVESDATA_PATH') ## If you didn't download it, this will be ""
    }
    if (!file.exists(data_path)){
        stop("AvesData folder not found at: ", data_path)
    }
    message("Using data download for taxonomy in")
    taxonomy_filename <- paste(data_path,
                             '/Taxonomy_versions/Clements',
                             as.character(taxonomy_year),
                             "/OTT_crosswalk_",
                             as.character(taxonomy_year),
                             ".csv",
                             sep='')
    message(taxonomy_filename)
    if (!file.exists(taxonomy_filename)){
          stop("taxonomy file not found at: ", taxonomy_filename)
          }
    tax = utils::read.csv(taxonomy_filename)
  }
  #create a convenience underscore column
  return(tax)
}

#' Helper to load a tree from a file into the R environment
#'
#' Not exported. Internal use only.
#'
#' @keywords internal
#'
treeGet <- function(version, taxonomy_year, data_path=FALSE, from_file=FALSE){
  #pull the tree file in the right version and taxonomy
  if (data_path==FALSE){
        data_path = Sys.getenv('AVESDATA_PATH') ## If you didn't download it, this will be ""
       }
  if ((data_path=="") & (from_file==TRUE)){
    stop("A data path is required to get tree from file.")
  }
  if((data_path=="") & (from_file==FALSE)){
    ## We will be in here if we have NOT run get_avesdata_repo and downloaded the data
    utils::data("clootl_data")
    taxonomyYear <- paste("year", taxonomy_year, sep="")
    version <- paste("Aves_", version, sep="")
    fullTree <- clootl_data$trees[[version]]$summary.trees[[taxonomyYear]]
  } 
  else {
  if (!file.exists(data_path)){
      stop("AvesData folder not found at: ", data_path)
  } 
    tree_filename <- paste(data_path,
                            "/Tree_versions/",
                            "Aves_",
                            version,
                            "/Clements",
                             as.character(taxonomy_year),
                             "/summary_dated_clements.nex",
                             sep='')
          message("Using data download for tree in")
          message(tree_filename)
    if (!file.exists(tree_filename)){
      stop("Tree :", tree_filename,
        "is not found. This version may not be available for this taxonomy year or you may need to update your AvesData repo using get_avesdata_repo(overwrite=TRUE)")
    } 
    else {
    fullTree <- read.nexus(tree_filename)
    fullTree$tip.label <- gsub("_", " ", fullTree$tip.label)
          }
   }

  return(fullTree)
  }
