---
title: "`r MotifPeeker:::report_header()`"
date: "`r format(Sys.Date(), '%b-%d-%Y')`"
output: 
    html_document: 
        toc: yes
        toc_float: yes
        code_folding: hide 
        number_sections: true
        css: custom.css
params:
    peak_files:
        value: NULL
    reference_index:
        value: 1
    alignment_files:
        value: NULL
    exp_labels:
        value: NULL
    exp_type:
        value: NULL
    genome_build:
        value: NULL
    motif_files:
        value: NULL
    motif_labels:
        value: NULL
    cell_counts:
        value: NULL
    distance_bootstrap:
        value: NULL
    bootstrap_n:
        value: NULL
    bootstrap_len:
        value: NULL
    motif_discovery:
        value: TRUE
    discover_motifs:
        value: 3
    filter_n:
        value: 6
    motif_db:
        value: NULL
    trim_seq_width:
        value: NULL
    download_buttons:
        value: NULL
    meme_path:
        value: NULL
    out_dir:
        value: NULL
    save_runfiles:
        value: NULL
    BPPARAM:
        value: NULL
    debug:
        value: FALSE
    verbose:
        value: TRUE
---

```{r setup, warning=debug, message=debug, results=ifelse(debug, "markup", "hide")}
knitr::opts_chunk$set(echo = TRUE,
                    message = params$debug,
                    warning = params$debug,
                    cache = FALSE, 
                    error = params$debug,
                    results = "asis")

### Parse input ###
genome_build <- check_genome_build(params$genome_build)
result_len <- length(params$peak_files)
motif_len <- length(params$motif_files)
if (is.null(params$exp_type)) {
    exp_types <- rep("unknown", result_len)
} else {
    exp_types <- params$exp_type
}
peak_files_encode <-
  Vectorize(function(x) check_input(
    x, "encode_id", check_ENCODE, expect_format = c("narrowPeak", "bed"),
    verbose = FALSE), "x")(params$peak_files)
alignment_files_encode <-
  Vectorize(function(x) check_input(
    x, "encode_id", check_ENCODE, expect_format = c("bam"),
    verbose = FALSE), "x")(params$alignment_files)
result <- list(
    peaks = Vectorize(read_peak_file, "peak_file")(peak_files_encode),
    alignments = lapply(alignment_files_encode, Rsamtools::BamFile),
    exp_labels = params$exp_labels,
    exp_type = unname(Vectorize(MotifPeeker:::format_exptype,
                                "exp_type")(exp_types))
)
motif_files_jaspar <- Vectorize(
  function(x) check_input(x, "jaspar_id", check_JASPAR,
                          verbose = params$verbose), "x")(params$motif_files)
user_motifs <- list(
    # motifs = Vectorize(read_motif_file, "motif_file")(motif_files_jaspar),
    motifs = Vectorize(
      function(x) check_input(x, "motif", read_motif_file, inverse = TRUE,
                              verbose = params$verbose),
      "x")(motif_files_jaspar),
    motif_labels = params$motif_labels
)
result$alignments <- unname(result$alignments)
result$peaks <- unname(result$peaks)
user_motifs$motifs <- unname(user_motifs$motifs)

temp_dir <- file.path(tempdir(), paste0("MotifPeeker_temp_", random_string(5)))
out_dir_extra <- ifelse(params$save_runfiles, params$out_dir, temp_dir)
label_colours <- c("#c83e73", "#2b83ba") # No, Yes

## Flags for optional plots
alignment_metrics <- ifelse((length(result$alignments) == 0), FALSE, TRUE)
cellcount_metrics <- ifelse((length(params$cell_counts) == 0), FALSE, TRUE)
user_motif_metrics <- ifelse((length(user_motifs$motifs) == 0 ||
                                    result_len == 1), FALSE, TRUE)
comparison_metrics <- ifelse(result_len == 1, FALSE, TRUE)
discover_metrics <- params$motif_discovery && comparison_metrics

## Misc
ex_emo <- ifelse(requireNamespace("emoji", quietly = TRUE),
                emoji::emoji("exclamation"), "!!")
check_emo <- ifelse(requireNamespace("emoji", quietly = TRUE),
                emoji::emoji("check"), "*")

## Check motif_db
using_jaspar_db <- if (discover_metrics) ifelse(is.null(params$motif_db),
                                       TRUE, FALSE)
motif_db <- if (discover_metrics) ifelse(is.null(params$motif_db),
                                       get_JASPARCORE(), params$motif_db)

### General Metrics ###
## Peak Counts
result$peak_count <- vapply(
        result$peaks, function(x) length(unique(x)), integer(1)
    )
if (alignment_metrics) {
    ## Read Counts
    result$read_count <- vapply(result$alignments, function(x) {
        Rsamtools::countBam(x)$records
    }, integer(1))
    
    ## FRiP scores
    result$frip <- Vectorize(calc_frip, c("read_file",
                                "peak_file"))(result$alignments, result$peaks)
    frip_df <- data.frame(exp_type = result$exp_type,
                            frip = result$frip,
                            exp_label = result$exp_labels)
}
    
## Peak Widths
result$peak_widths <- lapply(result$peaks, function(x){
    x@ranges@width
})
peak_width_len <- vapply(result$peak_widths, length, integer(1))
peak_width_df <- 
    data.frame(exp_label = rep(result$exp_labels, peak_width_len),
                exp_type = rep(result$exp_type, peak_width_len),
                peak_width = unlist(result$peak_widths))

## Motif-Summit Distances
motif_summit_dist_df <- get_df_distances(
        result, user_motifs, genome_build, out_dir_extra, params$BPPARAM,
        params$meme_path, params$debug
    )
motif_summit_dist_bootstrap_df <- if (params$distance_bootstrap) {
    get_df_distances_bootstrapped(
        result, user_motifs, genome_build,
        samples_n = params$bootstrap_n,
        samples_len = params$bootstrap_len,
        BPPARAM = params$BPPARAM,
        meme_path = params$meme_path,
        verbose = params$verbose
    )
} else {
    NULL
}

if (comparison_metrics) {
    ### Known-motif Analysis ###
    comparison_indices <- setdiff(seq_along(result$peaks),
                                  params$reference_index)
    segregated_peaks <- lapply(comparison_indices, function(x) {
        segregate_seqs(result$peaks[[params$reference_index]],
                        result$peaks[[x]])
    })
    
    ## Calculate enrichment for segregated peaks
    if (user_motif_metrics) {
      enrichment_df <- get_df_enrichment(
              result, segregated_peaks, user_motifs, genome_build,
              params$reference_index, out_dir_extra, params$BPPARAM,
              params$meme_path, params$verbose
          )
    }
}
    
### Discovered Motif Analysis ###
if (discover_metrics) {
    denovo_res <- list()
    ## Run STREME
    denovo_res$streme <- denovo_motifs(
      unlist(segregated_peaks), params$trim_seq_width, genome_build,
      params$discover_motifs, filter_n = params$filter_n,
      out_dir = out_dir_extra, meme_path = params$meme_path,
      BPPARAM = params$BPPARAM, verbose = params$verbose, debug = params$debug
    )
    ## Run TOMTOM
    denovo_res$similar_motifs <- find_motifs(
        denovo_res$streme, motif_db, out_dir = out_dir_extra,
        meme_path = params$meme_path, BPPARAM = params$BPPARAM,
        verbose = params$verbose, debug = params$debug
    )
    ## Compare motifs
    denovo_res$comparisons <- motif_similarity(
        denovo_res$streme, BPPARAM = params$BPPARAM
    )
}
```

```{r setup_misc, include = FALSE}
## Junk plot to add Plotly JS
plotly::plot_ly(x = c(1,2,3), type = "histogram")
```

[`MotifPeeker`](https://github.com/neurogenomics/MotifPeeker) compares and
analyses datasets from different epigenomic profiling methods using motif
enrichment as a key benchmark.

# Summary {-}
## Table of Contents {-}
The report consists of the following sections:  

1. [General Metrics](#general-metrics): Provides an overview of metrics related
to dataset peaks, including FRiP scores, peak widths, and motif-to-summit
distances.  

2. [Known Motif Enrichment Analysis](#motif-enrichment-analysis): Presents
statistics on the frequency of enriched user-supplied motifs in the datasets and
compares them between the common and unique peaks from comparison and reference
datasets.  

3. [Discovered Motif Enrichment Analysis](#discovered-motif-analysis): Details
the statistics of discovered motifs in common and unique peaks from
comparison and reference datasets. Examines motif similarities and identifies
the closest known motifs in the [JASPAR](https://jaspar.uio.no/downloads/)
or the provided database.  

## Input Datasets {-}
`MotifPeeker` was run with the following input datasets:

```{r echo = FALSE}
input_summary <- dplyr::tibble(
    "Index" = seq_along(result$peaks),
    "Reference" = ifelse(seq_along(result$peaks) == params$reference_index, check_emo, ""),
    "Dataset Label" = result$exp_labels,
    "Experiment Type" = result$exp_type
)
if (!is.null(params$cell_counts)) {
    input_summary$`Cell Count` <- params$cell_counts
}
input_summary %>% print_DT(class="compact hover stripe", rownames = FALSE)
```

User-provided motifs used:  
  
```{r echo = FALSE}
if (length(user_motifs$motifs) == 0) cat("- **None**  \n")
for (i in seq_along(user_motifs$motifs)) {
    cat(paste0("- **Motif ", i, "**: ", user_motifs$motifs[[i]]@name, "  \n"))
}
```

## Command {-}
`MotifPeeker` report was generated with the following command:
```{r report_command}
cat(MotifPeeker:::report_command(params))
```

<hr>

# General Metrics {.tabset #general-metrics}

## FRiP Score {-}
**Fraction of Reads in Peaks (FRiP)** is the proportion of sequencing reads
within identified peak regions. Higher FRiP scores suggest higher
signal-to-noise ratios in the dataset.   
$$\text{FRiP (Fraction of Reads in Peaks)} = 
\frac{\text{Reads in Peaks}}{\text{Total Reads}}$$
```{r frip_exp_plot}
if (!alignment_metrics) {
    cat("\n", ex_emo,
    " *Skipping FRiP calculation as alignment files were not provided.*",
        "  \n")
} else {
    if (length(result$alignments) != length(result$peaks)) {
        stp_msg <- "Number of alignment files and peak files do not match."
        stop(stp_msg)
    }
    cat("\n### By Experiment Type {- .unlisted}  \n")
    frip_exp_plot <- frip_df %>%
        ggplot2::ggplot(aes(x = exp_type, y = frip, fill = exp_type,
                            text = paste0(
                                "<b>Experiment Label</b>: ", exp_label, "<br>",
                                "<b>Experiment Type</b>: ", exp_type, "<br>",
                                "<b>FRiP Score</b>: ", round(frip, 4)))) +
            geom_boxplot(outlier.shape = NA) +
            geom_jitter() +
            labs(
              x = "Experiment Type",
              y = "FRiP Score",
              fill = "Experiment Type"
            ) +
        scale_fill_viridis(begin = 0.15, end = 0.6, discrete = TRUE,
                           option = "A")
    
    print(MotifPeeker:::to_plotly(frip_exp_plot))
}
```

```{r frip_exp_dt, include = alignment_metrics}
if (alignment_metrics) {
    frip_df %>%
        group_by(exp_type) %>%
        rename("Experiment Type" = exp_type) %>%
        summarise(
          "Mean score" = round(mean(frip), 3),
          "Median Score" = round(median(frip), 3),
          "Standard Deviation" = round(sd(frip), 3)
        ) %>%
        arrange(`Experiment Type`) %>%
    print_DT()
}
```
```{r frip_individual_plot, include = alignment_metrics}
if (alignment_metrics) {
    cat("\n### By Individual Dataset {- .unlisted}  \n")
    
    frip_individual_plot <- frip_df %>%
        ggplot(aes(x = exp_label, y = frip, fill = exp_type, text = paste0(
                                "<b>Experiment Label</b>: ", exp_label, "<br>",
                                "<b>Experiment Type</b>: ", exp_type, "<br>",
                                "<b>FRiP Score</b>: ", round(frip, 4)))) +
            theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
            geom_col() +
            labs(
              x = "Experiment Label",
              y = "FRiP Score",
              fill = "Experiment Type"
            ) +
        scale_fill_viridis(begin = 0.15, end = 0.6, discrete = TRUE,
                           option = "A")
    
    print(MotifPeeker:::to_plotly(frip_individual_plot))
}
```
```{r frip_individual_dt, include = alignment_metrics}
if (alignment_metrics) {
    frip_df$read_count <- result$read_count
    frip_individual_df <- frip_df %>%
        mutate(frip = round(frip, 3)) %>%
        select(exp_label, exp_type, read_count, frip) %>%
        rename(
          "Experiment Label" = exp_label,
          "Experiment Type" = exp_type,
          "FRiP Score" = frip,
          "Read Count" = read_count
        ) %>%
        arrange(`Experiment Type`, `Experiment Label`)
    
    if (length(params$cell_counts) > 0) {
        frip_individual_df$cell_count <- params$cell_counts
        frip_individual_df <- frip_individual_df %>%
            rename(
              "Cell Count" = cell_count
            )
    }
    frip_individual_df %>%
        print_DT()
}
```
```{r frip_cellcount_plot, include = alignment_metrics}
if (alignment_metrics) {
    if (!cellcount_metrics) {
        cat("\n", ex_emo,
        " *Skipping cell count plot as cell counts were not provided.*\n")
    } else {
        cat("\n### By Cell Count {- .unlisted}  \n")
        
        frip_df$cell_count <- as.numeric(params$cell_counts)
        frip_df_cellcount <- frip_df %>%
            group_by(cell_count, exp_type) %>%
                summarise(
                    frip = mean(frip),
                    .groups = "drop"
                )
            
        frip_cellcount_plot <- frip_df %>%
            ggplot(aes(
                x = cell_count,
                y = frip,
                shape = exp_type,
                color = exp_type,
                text = paste0("<b>Experiment Type</b>: ", exp_type, "<br>",
                                "<b>FRiP Score</b>: ", round(frip, 4),
                                "<br><b>Cell Count</b>: ", cell_count))) +
            geom_point() +
            geom_line(data = frip_df_cellcount) +
            labs(
                x = "Cell Count (log10 scale)",
                y = "FRiP Score",
                fill = "Experiment Type",
                shape = "Experiment Type",
                color = "Experiment Type"
            ) +
            scale_x_continuous(trans="log10") +
            scale_color_viridis(begin = 0.15, end = 0.6, discrete = TRUE,
                           option = "A")
        
        print(MotifPeeker:::to_plotly(frip_cellcount_plot))
    }
}
```

## Peak Width Distribution {-}
This section presents the peak width distribution for each experiment type, as
well as for individual experiments and cell counts, reported in base pairs.  

The data plotted represents the central 95% of the overall data, with values
below the 2.5th percentile and above the 97.5th percentile excluded.  

### By Experiment Type {- .unlisted}
```{r peakwidths_exp_plot1}
peak_width_plt1 <- peak_width_df %>%
    filter(peak_width >= stats::quantile(peak_width, 0.025) &
           peak_width <= stats::quantile(peak_width, 0.975)) %>%
    ggplot(aes(x = peak_width, fill = exp_type)) +
        geom_histogram(bins = 30, alpha = 0.5, position = "identity") +
        labs(
            x = "Peak Width",
            y = "Peak Count",
            fill = "Experiment Type"
        ) +
        scale_fill_viridis(begin = 0.2, end = 0.6, discrete = TRUE,
                            option = "A")
    
    print(MotifPeeker:::to_plotly(peak_width_plt1))
```

```{r peakwidths_exp_plot2}
peak_width_plt2 <- peak_width_df %>%
    filter(peak_width >= stats::quantile(peak_width, 0.025) &
           peak_width <= stats::quantile(peak_width, 0.975)) %>%
    ggplot(aes(x = exp_type, y = peak_width, fill = exp_type)) +
        geom_boxplot(alpha = 0.5) +
        labs(
            x = "Experiment Type",
            y = "Peak Width",
            fill = "Experiment Type"
        ) +
        scale_fill_viridis(begin = 0.2, end = 0.6, discrete = TRUE,
                            option = "A")

    print(MotifPeeker:::to_plotly(peak_width_plt2))
```

```{r peakwidths_exp_dt}
peak_width_df %>%
    group_by(exp_type) %>%
    rename("Experiment Type" = exp_type) %>%
    summarise(
        "Mean Peak Width" = round(mean(peak_width), 1),
        "Median Peak Width" = round(median(peak_width), 3),
        "2.5th Quantile" = round(quantile(peak_width, 0.025), 3),
        "97.5th Quantile" = round(quantile(peak_width, 0.975), 3),
        "Standard Deviation" = round(sd(peak_width), 1)
    ) %>%
    arrange(`Experiment Type`) %>%
    print_DT()
```

### By Individual Dataset {- .unlisted}
```{r peakwidths_ind_plot}
peak_width_plt3 <- peak_width_df %>%
    filter(peak_width >= stats::quantile(peak_width, 0.025) &
           peak_width <= stats::quantile(peak_width, 0.975)) %>%
    ggplot(aes(x = exp_label, y = peak_width, fill = exp_type)) +
        geom_boxplot(alpha = 0.5) +
        theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
        labs(
        x = "Experiment Label",
        y = "Peak Width",
        fill = "Experiment Type"
        ) +
        scale_fill_viridis(begin = 0.15, end = 0.6, discrete = TRUE,
                            option = "A")

    print(MotifPeeker:::to_plotly(peak_width_plt3))
```

```{r peakwidths_ind_dt}
peak_width_df$cell_count <- rep(params$cell_counts, peak_width_len)

peak_width_df_all <- if (is.null(peak_width_df$cell_count)) {
    peak_width_df %>%
    group_by(exp_label, exp_type)
} else {
    peak_width_df %>%
    group_by(exp_label, exp_type, cell_count) %>%
    rename("Cell Count" = cell_count,) 
}

peak_width_df_all <- peak_width_df_all %>%
    summarise(
        "Mean Peak Width" = round(mean(peak_width), 1),
        "Median Peak Width" = round(median(peak_width), 3),
        "2.5th Quantile" = round(stats::quantile(peak_width, 0.025), 3),
        "97.5th Quantile" = round(stats::quantile(peak_width, 0.975), 3),
        "Standard Deviation" = round(stats::sd(peak_width), 1),
        .groups = "drop"
    ) %>%
    rename(
        "Experiment Label" = exp_label,
        "Experiment Type" = exp_type
    ) %>%
    arrange(`Experiment Type`, `Experiment Label`)

    print_DT(peak_width_df_all)
```

```{r peakwidths_cellcount_plot}
if (!cellcount_metrics) {
    cat("\n", ex_emo,
    " *Skipping cell count plot as cell counts were not provided.*\n")
} else {
    cat("\n### By Cell Count {- .unlisted}  \n")
    
    peak_width_df_cellcount <- peak_width_df %>%
        group_by(exp_label, exp_type, cell_count) %>%
        drop_na(cell_count) %>%
        summarise(
            mean_width = mean(peak_width),
            .groups = "drop"
        ) %>%
        group_by(exp_type, cell_count) %>%
        summarise(
            mean_width = mean(mean_width),
            .groups = "drop"
        )
        
    peak_width_cellcount_plot <- peak_width_df %>%
        group_by(exp_label, exp_type) %>%
        drop_na(cell_count) %>%
        summarise(
            mean_width = mean(peak_width),
            cell_count = mean(cell_count),
            .groups = "drop"
        ) %>%
        ggplot(aes(
            x = cell_count,
            y = mean_width,
            shape = exp_type,
            color = exp_type,
            text = paste0("<b>Experiment Type</b>: ", exp_type, "<br>",
                            "<b>Mean Peak Width</b>: ", round(mean_width, 1),
                            "<br><b>Cell Count</b>: ", cell_count))) +
        geom_point() +
        geom_line(data = peak_width_df_cellcount) +
        labs(
            x = "Cell Count (log10 scale)",
            y = "Mean Peak Width",
            fill = "Experiment Type",
            shape = "Experiment Type",
            color = "Experiment Type"
        ) +
        scale_x_continuous(trans="log10") +
        scale_color_viridis(begin = 0.15, end = 0.6, discrete = TRUE,
                       option = "A")
    
    print(MotifPeeker:::to_plotly(peak_width_cellcount_plot))
}
```

## Motif-Summit Distance {- .tabset .tabset-fade .tabset-pills}
This section reports the distances between the peak summit and the centre of the
nearest motif. MEME suite's [FIMO](https://meme-suite.org/meme/doc/fimo.html)
tool is used to scan the sequences in the peak file to identify all occurrences
of the provided motifs, and the distances between the centres of each motif
occurrence and the closest peak summit are calculated.  

The data plotted represents the central 95% of the overall data, with values
below the 2.5th percentile and above the 97.5th percentile excluded.  

> A *smaller* absolute mean/median motif-summit distance means the detected
signal is more tightly centred on the motif, which usually reflects higher
precision and quality in epigenomic profiling experiments.  

```{r motif_summit_dist}
if (!user_motif_metrics) {
    cat("\n", ex_emo,
    " *Skipping motif-summit distance plots as no motifs files",
    " were provided.*\n")
} else {
    tab_header_flag <- FALSE # Print tabset header just once
    
    ## Loop through all motifs
    for (motif_i in seq_len(motif_len)) {
        if (!tab_header_flag) {
            cat("**Select Motif:**  \n")
            tab_header_flag <- TRUE
        }
        cat(paste0("\n### ", user_motifs$motifs[[motif_i]]@name,
                  " {- .unlisted}  \n"))
        cat(paste0("**Motif Consensus Sequence:** ",
                  user_motifs$motifs[[motif_i]]@consensus, " (Length: ",
                  nchar(user_motifs$motifs[[motif_i]]@consensus), " bp)  \n"))
        
        cat("\n#### By Experiment Type {- .unlisted}  \n")
        cat("The graph shows how many peaks occur at different distances from",
            "the motif centre. The probability density curve shows where peaks",
            "are most common, higher areas mean peaks are more likely at those",
            "distances. The area under each curve adds up to 1, and different",
            "colours show experiment types.")
        ## Limits
        motif_summit_dist_lims <- motif_summit_dist_df %>%
                      filter(motif_indice == motif_i) %>%
                      pull(distance) %>%
                      stats::quantile(c(0.025, 0.975))
            
        ## Density plot
        motif_summit_dist_plt <- motif_summit_dist_df %>%
            filter(motif_indice == motif_i) %>%
            filter(distance >= motif_summit_dist_lims[1] &
                    distance <= motif_summit_dist_lims[2]) %>%
            ggplot(aes(x = distance, fill = exp_type, color = exp_type,
                        text = paste("<b>Experiment Type</b>:", exp_type))) +
            geom_density(alpha = 0.5) +
            scale_fill_viridis(begin = 0.2, end = 0.6, discrete = TRUE,
                            option = "A") +
            scale_color_viridis(begin = 0.2, end = 0.6, discrete = TRUE,
                            option = "A") +
            labs(color = "Experiment Type",
                fill = "Experiment Type",
                x = "Distance from Motif Centre (bp)", 
                y = "Density")
        motif_summit_dist_plt <- MotifPeeker:::to_plotly(motif_summit_dist_plt,
                                            html_tags = FALSE)
        
        print(htmltools::tagList(motif_summit_dist_plt))
        
        ## DT
        motif_summit_dist_df %>%
            filter(motif_indice == motif_i) %>%
            group_by(exp_type) %>%
            summarise(
                "Absolute Mean Distance" = round(mean(abs(distance)), 2),
                "Absolute Median Distance" = median(abs(distance)),
                "Standard Deviation" = round(sd(distance), 2)
            ) %>%
            rename("Experiment Type" = exp_type) %>%
            print_DT(html_tags = TRUE) %>%
            print()
        
        cat("\n#### By Individual Dataset {- .unlisted}\n")
        if (!distance_bootstrap) {
            cat("This plot shows the average of the absolute distance between peak",
            "summits and the nearest motif center for each input experiment.  \n")
            ## Absolute mean distance plot
            motif_summit_dist_ind_plt <- motif_summit_dist_df %>%
                filter(motif_indice == motif_i) %>%
                group_by(exp_label, exp_type) %>%
                summarise(abs_mean_dist = round(mean(abs(distance)), 2),
                          .groups = "drop") %>%
                ggplot(aes(x = exp_label, y = abs_mean_dist, fill = exp_type,
                            text = paste0(
                                "<b>Experiment Label</b>: ", exp_label, "<br>",
                                "<b>Experiment Type</b>: ", exp_type, "<br>",
                                "<b>Absolute Mean Distance</b>: ", abs_mean_dist
                            ))) +
                    theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
                    geom_col() +
                    labs(
                      x = "Experiment Label",
                      y = "Absolute Mean Distance (bp)",
                      fill = "Experiment Type"
                    ) +
                scale_fill_viridis(begin = 0.15, end = 0.6, discrete = TRUE,
                                   option = "A")
            
                print(MotifPeeker:::to_plotly(motif_summit_dist_ind_plt, html_tags = TRUE))
        } else {
            cat("This plot shows the bootstrapped distribution of the mean absolute distances of motif centre to peak summits. Larger frequencies at smaller distances indicate that the experiment consistently shows peaks close to the motif centre.  \n  ")
            cat("Each experiment was resampled", max(motif_summit_dist_bootstrap_df$bootstrap_iteration), "times. \n")
            
            ## Distance distribution histogram
            motif_summit_dist_bootstrap_plt <- motif_summit_dist_bootstrap_df %>%
                filter(motif_indice == motif_i) %>%
                group_by(exp_label, exp_type) %>%
                ggplot(aes(
                    x = distance,
                    fill = exp_label,
                    text = paste0(
                        "<b>Experiment Label</b>: ",
                        exp_label,
                        "<br>",
                        "<b>Experiment Type</b>: ",
                        exp_type,
                        "<br>"
                    )
                )) +
                geom_histogram(bins = 30,
                               position = "identity",
                               alpha = 0.5) +
                labs(x = "Absolute Mean Distance to Motif Centre (bp)",
                     y = "Frequency of Resamples",
                     fill = "Experiment Label") +
                scale_fill_viridis(
                    begin = 0.15,
                    end = 0.6,
                    discrete = TRUE,
                    option = "A"
                )
            
            print(MotifPeeker:::to_plotly(motif_summit_dist_bootstrap_plt, html_tags = TRUE))
        }
        
        ## DT
        motif_summit_dist_df %>%
            filter(motif_indice == motif_i) %>%
            group_by(exp_label, exp_type) %>%
            summarise(
                "Absolute Mean Distance" = round(mean(abs(distance)), 2),
                "Absolute Median Distance" = median(abs(distance)),
                "Standard Deviation" = round(sd(distance), 2),
                .groups = "keep"
            ) %>%
            arrange(exp_type, exp_label) %>%
            rename(
                "Experiment Label" = exp_label,
                "Experiment Type" = exp_type
            ) %>%
            print_DT(html_tags = TRUE) %>%
            print()
    }
}
```

## Summary Table {-}
Summary of all the general metrics for the input datasets. Peak count only
includes unique peaks.

```{r summary_table}
dplyr::tibble(
    "Experiment Label" = result$exp_label,
    "Experiment Type" = result$exp_type,
    "Cell Count" = if (cellcount_metrics) params$cell_counts else NA,
    "Read Count" = result$read_count,
    "Peak Count" = result$peak_count,
    "FRiP Score" = if (alignment_metrics) round(result$frip, 3) else NA
) %>%
    arrange(`Experiment Type`, `Experiment Label`) %>%
    mutate("Mean Peak Width" = peak_width_df_all$`Mean Peak Width`) %>%
    print_DT(extra = TRUE)
```

# {- .unlisted}
<hr>  

# Known Motif Enrichment Analysis {.tabset .tabset-fade .tabset-pills #motif-enrichment-analysis}
This section provides a comprehensive pair-wise comparison of motif enrichment
patterns between the reference and comparison experiments. The presence of
user-provided motif in the peaks was determined using the
[AME](https://meme-suite.org/meme/doc/ame.html) tool from the MEME suite.  

The overall summary plot compacts all the individual comparisons into one for
quick assessment of relative motif enrichment across experiments. The individual
comparison plots provide a detailed view of the proportion of peaks with the
motif of interest for all, common and unique peaks in the respective reference
and comparison plots.  

Peaks are considered common to both comparison and reference datasets if they
overlap by at least 1 base pair. All other peaks are classified as unique.  
  
**NOTE**: Discrepancies in motif enrichment in common peaks between experiments
may arise due to variations in peak lengths. Experiment with longer peaks may
have a higher proportion of peaks with the motif of interest purely by chance.  

<details>
<summary><b>More Details</b></summary>

The AME tool (Analysis of Motif Enrichment) from the MEME Suite tests whether a
given transcription factor-binding motif (TFBM) appears more often in your set
of peaks compared to a background set of sequences. It works by:

* Scanning each peak and matching background sequence for the motif
* Calculating a score for how well the motif fits each sequence
* Using statistical tests (e.g., Fisher’s exact test or rank-sum test) to see if
motif scores in peaks are higher than in background
* Reporting p-values for enrichment and adjusting them to control false
discoveries when testing many motifs

Since AME creates its background by shuffling your peaks (keeping dinucleotide
frequencies), each dataset has its own background model. The adjusted p-values
are reliable for ranking motifs within a dataset but should not be directly
compared between different datasets.

Instead, to compare across datasets, look at each motif’s rank by p-value,
examine odds ratios or score differences, and consider the percentage of peaks
containing the motif.

More details on AME output: 
[AME Output Documentation](https://meme-suite.org/meme/doc/ame-output-format.html)

</details>

```{r motif_enrichment_analysis}
if (!user_motif_metrics) {
    cat("\n", ex_emo,
    " *Skipping motif-summit distance plots as either no motifs files",
    " was not provided, or only a single dataset was provided.*\n")
} else {
    tab_header_flag <- FALSE # Print tabset header just once
    
    ## Loop through all motifs
    for (motif_i in seq_len(motif_len)) {
        if (!tab_header_flag) {
            cat("**Select Motif:**  \n")
            tab_header_flag <- TRUE
        }
        cat(paste0("\n## ", user_motifs$motifs[[motif_i]]@name,
                   " {- .unlisted .tabset}  \n"))
        cat(paste0("**Motif Consensus Sequence:** ",
                  user_motifs$motifs[[motif_i]]@consensus, " (Length: ",
                  nchar(user_motifs$motifs[[motif_i]]@consensus), " bp)  \n"))
        
        cat("<br>**Select Comparison Group:**  \n")
        
        ### Overall Summary Plot ###
        cat("\n### **Overall** {- .unlisted}  \n")
        cat("These plots are organised in a 2x2 grid comparing motif",
            "enrichment: top row for the comparison experiment and bottom row",
            "for the reference experiment, while columns represent common",
            "peaks on the left and unique peaks on the right.  \n<br>")
        cat("<details><summary>**Show Comparison-Reference Pair Labels**",
        "</summary>  \n")
        cat("(Pair Number. Comparison Experiment - Reference Experiment)  \n  ")
        for (pair_i in seq_along(comparison_indices)) {
          cat(paste0(
            pair_i, ". ", result$exp_labels[comparison_indices][pair_i], " - ",
            result$exp_labels[params$reference_index], "  \n"
          ))
        }
        cat("  \n</details>  \n<br>  \n")
        enrichment_overall_plots <- MotifPeeker:::plot_enrichment_overall(
            enrichment_df, motif_i, label_colours,
            reference_label = result$exp_labels[params$reference_index]
        )
        cat("\n#### Counts of Peaks Containing Motif Across All Experiments ",
        "{- .unlisted}  \n")
        print(enrichment_overall_plots$count_plt)
        
        cat("\n#### Percentages of Peaks Containing Motif Across All ",
        "Experiments {- .unlisted}  \n")
        print(enrichment_overall_plots$perc_plt)
        
        ### Individual Comparison Plots ###
        for (i in comparison_indices) {
            print_labels(result$exp_labels, params$reference_index, i,
                        "known_motif", result$read_count) %>% cat()
            ## Plot
            MotifPeeker:::plot_enrichment_individual(result, enrichment_df, i,
                motif_i, label_colours, params$reference_index) %>%
                print()
            ## DT
            cat("  \n  ")
            MotifPeeker:::dt_enrichment_individual(result, enrichment_df, i, motif_i,
                                    params$reference_index) %>%
                print()
            cat("  \n  ")
        }
    }
}
```

# {- .unlisted}
<hr>  


# Discovered Motif Analysis {.tabset #discovered-motif-analysis}
Discovered motif discovery identifies motif sequences in the common and
unique peaks of the reference and comparison peak files. The Position Weight
Matrix (PWM) of these motifs, representing the probability of each base at
each position in the motif, are compared across different peak sets to identify
similarities.  
  
Similarity is quantified using the Pearson correlation coefficient (PCC) score,
ranging from -1 to +1, with higher values indicating greater similarity. The
scores are also normalised to penalise disparities between motifs of different
lengths. Motifs were discovered using the
[STREME](https://meme-suite.org/meme/doc/streme.html) tool from MEME suite.
  
Explanation of the plots:  
  
1. `Common Motif Comparison`: High correlation between motifs in this plot
validates that the common peaks from both the reference and comparison datasets
are enriched for similar motifs. This suggests consistent binding of shared
regulatory factors across the experiments.  
2. `Unique Motif Comparison`: This plot examines whether unique peaks from both
experiments capture similar motifs. The presence of highly correlated motifs
here indicates that both experiments miss out on capturing peaks with a likely
legitimate motif. This may be caused by strict peak calling thresholds,
potentially excluding true positive signals.  
3. `Cross Motif Comparison A/B`: These plots compare motifs from unique peaks in
one experiment to motifs from common peaks in the other experiment (refer to
axis labels). Higher correlation scores suggests that the experiment
contributing the unique peaks captures a higher proportion of motifs that are
actually shared between the two experiments, suggesting higher sensitivity of
the experiment with unique peaks.
  
The discovered motifs are also compared to motifs from the JASPAR or the
provided database to identify any similar known motifs using
[TOMTOM](https://meme-suite.org/meme/doc/tomtom.html) from MEME suite.
  
**NOTE**: Motifs with poly-nucleotide repeats (such as "AAAAA") should be
avoided due to inflated correlation scores. Motifs containing `filter_n` or more
repeats are not included in the results.  

```{r discover_motif_check, eval = !discover_metrics, include = !discover_metrics}
cat("\n", ex_emo,
    " *Skipping discovered motif analysis as either `motif_discovery` was",
    " set to `FALSE` or only a single dataset was provided.*\n")
```

```{r discover_motif, eval = discover_metrics, include = discover_metrics}
tab_header_flag <- FALSE # Print tabset header just once
for (i in seq_along(comparison_indices)) {
    comparison_i <- comparison_indices[i]
    if (!tab_header_flag) {
        cat("**Select Comparison Group:**  \n")
        tab_header_flag <- TRUE
    }
  print_labels(result$exp_labels, params$reference_index, comparison_i,
              "denovo_motif", result$read_count) %>% cat()
  cat("<br>**Select Plot:**  \n")
  start_i <- 1 + (i - 1) * 4
  end_i <- i * 4
  streme_motifs <- denovo_res$streme[seq(start_i, end_i)]
  similar_motifs <- denovo_res$similar_motifs[seq(start_i, end_i)]
  comparison_matrices <- denovo_res$comparisons[seq(start_i, end_i)]
  denovo_plts <- plot_motif_comparison(comparison_matrices, exp_labels = c(
      result$exp_labels[params$reference_index],
      result$exp_labels[comparison_i]
  ), width = 650, height = 500)
  download_btns <-
    get_download_buttons(i, start_i, segregated_peaks, out_dir = out_dir_extra,
          add_buttons = params$download_buttons, verbose = params$verbose)
  .print_denovo_sections_i <- function(x, y) {
    out <- print_denovo_sections(streme_motifs, similar_motifs, segregated_peaks[[i]],
                      c(x, y), jaspar_link = using_jaspar_db,
                      download_buttons = download_btns)
    for (out2 in out) {    
      for (ele in out2) {
        if (typeof(ele) == "character") {
          cat(ele)
        } else {
          print(ele)
        }
      }
    }
  }
  .print_denovo_plt <- function(i) {
    if (typeof(denovo_plts[[i]]) == "character") {
      cat(denovo_plts[[i]])
    } else {
      print(denovo_plts[[i]])
    }
  }
  
  cat("\n### Common Motif Comparison  {- .unlisted}  \n")
  cat("\n#### Motif Similarity Plot  {- .unlisted}  \n")
  .print_denovo_plt(1)
  cat("\n#### Motif Details  {- .unlisted}  \n")
  .print_denovo_sections_i(1, 2)
  
  cat("\n### Unique Motif Comparison {- .unlisted}  \n")
  cat("\n#### Motif Similarity Plot  {- .unlisted}  \n")
  .print_denovo_plt(2)
  cat("\n#### Motif Details  {- .unlisted}  \n")
  .print_denovo_sections_i(3, 4)
  
  cat("\n### Cross Motif Comparison A {- .unlisted}  \n")
  cat("\n#### Motif Similarity Plot  {- .unlisted}  \n")
  .print_denovo_plt(3)
  cat("\n#### Motif Details  {- .unlisted}  \n")
  .print_denovo_sections_i(3, 2)
  
  cat("\n### Cross Motif Comparison B {- .unlisted}  \n")
  cat("\n#### Motif Similarity Plot  {- .unlisted}  \n")
  .print_denovo_plt(4)
  cat("\n#### Motif Details  {- .unlisted}  \n")
  .print_denovo_sections_i(4, 1)
} 
```


# {- .unlisted}
<hr>  

# Citation {-}
If you use `MotifPeeker`, please cite: 
```{r} 
cat(utils::citation("MotifPeeker")$textVersion)
```

# Session Info {-}
<details> 
```{r Session Info, results='markup'}
utils::sessionInfo()
```
</details> 
<hr>
