---
title: "Levey-Jennings Raport: `r paste0('*', params$report_title, '*')`"
output: html_document
params:
  list_of_plates: !r NULL
  report_title: !r NULL
  dilutions: !r NULL
  additional_notes: !r NULL
  extra_args: !r NULL
---

```{r param-check, echo=FALSE}
list_of_plates <- params$list_of_plates
additional_notes <- params$additional_notes
extra_args <- params$extra_args


if (is.null(list_of_plates)) {
  stop("The `list_of_plates` must be provided when rendering the document.")
}
if (!is.list(list_of_plates)) {
  stop("The `list_of_plates` must be a list of `Plate` instances.")
}
if (length(list_of_plates) == 0) {
  stop("The `list_of_plates` must contain at least one `Plate` instance.")
}
if (length(list_of_plates) < 10) {
  warning("The `list_of_plates` should contain at least 10 `Plate` instances to generate a comprehensive report.")
}
if (!all(sapply(list_of_plates, inherits, "Plate"))) {
  stop("The list_of_plates contains objects that are not of class Plate.")
}
if (is.null(params$report_title)) {
  stop("The `report_title` must be provided when rendering the document.")
}
if (!is(params$report_title, "character")) {
  stop("The `report_title` must be a character value.")
}
if (is.null(params$dilutions)) {
  stop("The `dilutions` must be provided when rendering the document.")
}
if (!is.character(params$dilutions)) {
  stop("The `dilutions` must be a character value.")
}
if (!all(grepl("(^1\\s*)\\/(\\s*\\d+$)", params$dilutions))) {
  stop("The `dilutions` must be in the format '1/digit'.")
}
for (dilution in params$dilutions) {
  if (!all(sapply(list_of_plates, function(plate) dilution %in% plate$get_dilution("STANDARD CURVE")))) {
    plate_where_dilution_is_missing <- which(sapply(list_of_plates, function(plate) !(dilution %in% plate$get_dilution("STANDARD CURVE"))))
    stop(
      "The dilution",
      dilution,
      "is not present in plates ",
      paste(plate_where_dilution_is_missing, collapse = ", ")
    )
  }
}
all_analyte_names <- c()

intersection_analytes <- list_of_plates[[1]]$analyte_names

for (plate in list_of_plates) {
  all_analyte_names <- union(all_analyte_names, plate$analyte_names)
  intersection_analytes <- intersect(intersection_analytes, plate$analyte_names)
}

if (length(intersection_analytes) != length(all_analyte_names)) {
  warning(
    "Not all plates contain the same analytes. The report will contain only the analytes that are present in all plates. Number of all analytes: ",
    length(all_analyte_names),
    ". Number of common analytes: ",
    length(intersection_analytes)
  )
}

analyte_names <- intersection_analytes


if (!is.null(additional_notes) && !is(additional_notes, "character")) {
  stop("The `additional_notes` must be a character value.")
}

date_of_first_plate <- min(sapply(list_of_plates, function(plate) plate$plate_datetime))
date_of_last_plate <- max(sapply(list_of_plates, function(plate) plate$plate_datetime))

# i need to add this trick otherwise i get error in format function
# Error in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L,  :
#  invalid 'trim' argument
# it may cause some problems with timezones
# but few hours difference is not a big deal
date_of_first_plate <- as.POSIXct(date_of_first_plate,
  origin = "1970-01-01",
  tz = "UTC"
)
date_of_last_plate <- as.POSIXct(date_of_last_plate,
  origin = "1970-01-01",
  tz = "UTC"
)
time_span <- difftime(date_of_last_plate, date_of_first_plate, units = "days")


# perform additional check for extra_args

# Validate extra_args: ensure it's a named list
if (!is.list(extra_args)) {
  stop("`extra_args` must be a list.")
}
if (length(extra_args) > 0 && (is.null(names(extra_args)) || any(names(extra_args) == ""))) {
  stop("All elements in `extra_args` must be named.")
}

# Hard-coded list of arguments passed explicitly in this Rmd script
reserved_names <- c(
  "list_of_plates", "analyte_name", "monochromatic"
)

# Check for forbidden overlaps
conflicting_args <- intersect(names(extra_args), reserved_names)
if (length(conflicting_args) > 0) {
  stop(
    "The following `extra_args` fields are not allowed, as they are explicitly passed in the report script: ",
    paste(conflicting_args, collapse = ", "),
    ". Please rename or remove them."
  )
}
```

Report generated on:  `r format(Sys.time(), SerolyzeR.env$report_datetime_format)`.  
First plate was run on: `r if(!is.null(date_of_first_plate)) format(date_of_first_plate, SerolyzeR.env$report_datetime_format) else "Date of running plate was not found in Luminex file"`.  
Last plate was run on: `r if(!is.null(date_of_last_plate)) format(date_of_last_plate, SerolyzeR.env$report_datetime_format) else "Date of running plate was not found in Luminex file"`.  
Time span: `r if(!is.null(time_span)) paste0(round(time_span, 2), " days") else "Dates were not found in Luminex file"`.  
Number of plates used in report: `r length(list_of_plates)`.  
Plates have `r paste0(length(analyte_names), " analytes")`.  
Standard curve sample dilutions: `r format_dilutions(list_of_plates[[1]]$dilutions, list_of_plates[[1]]$dilution_values, list_of_plates[[1]]$sample_types)`.

------------------

`r if(!is.null(additional_notes)) "### Additional notes \n" `  

`r if(!is.null(additional_notes)) additional_notes `  

`r if(!is.null(additional_notes)) "\n -------------------" ` 

### Plates used in this report

<details open>
  <summary>Click to show/hide list</summary>
```{r list-of-plates, echo=FALSE, results='asis'}
for (plate in list_of_plates) {
  cat(paste0("- ", plate$plate_name, "\n"))
}
```
</details>

### Plate layout

<details open>
  <summary>Click to show/hide layout</summary>
```{r plate-layout, fig.width=7, fig.height=4, fig.align='center', dev='jpeg', echo=FALSE, dpi=72}
plot_layout(list_of_plates[[1]])
```
</details>

------------------

```{r echo=FALSE}
svg_plot_file <- function(plot, width = 7, height = 7) {
  file <- tempfile(fileext = ".svg")
  svglite::svglite(file, width = width, height = height)
  print(plot)
  grDevices::dev.off()
  return(file)
}
```

 
### Details for given analyte {.tabset .tabset-fade}

```{r quality-control, results='asis', echo=FALSE, message=FALSE, dpi=72, warning=FALSE}
# Code used to create dynamic tabs based on the number of analytes
for (i in seq_along(analyte_names)) {
  cat(paste0("#### ", analyte_names[i], "\n"))

  # Set figure height based on number of plates (the size of the legend)
  n <- length(list_of_plates)
  l_h <- 0.35 * (1 + (n %/% 3)) # legend size in inches
  h <- 3.5 + l_h # figure height in inches
  rel_l_h <- l_h / h

  # Plot stacked curves monochromatic
  p_mono <- inject(plot_standard_curve_stacked(
    list_of_plates = list_of_plates,
    analyte_name = analyte_names[i],
    monochromatic = TRUE,
    separate_legend = TRUE,
    legend_rel_height = rel_l_h,
    !!!extra_args
  ))
  cat("![](", svg_plot_file(p_mono, width = 5, height = h), "){width='50%'}")

  # Relative for color plot
  l_h <- 0.35 * (1 + (n %/% 2)) # legend size in inches
  h <- 3.5 + l_h # figure height in inches
  rel_l_h <- l_h / h

  # Plot stacked curves colored
  knitr::opts_current$set(fig.height = h)
  p_color <- inject(plot_standard_curve_stacked(
    list_of_plates = list_of_plates,
    analyte_name = analyte_names[i],
    monochromatic = FALSE,
    separate_legend = TRUE,
    legend_rel_height = rel_l_h,
    max_legend_items_per_row = 2,
    !!!extra_args
  ))
  cat("![](", svg_plot_file(p_color, width = 5, height = h), "){width='50%'}")

  cat("\n\n")

  for (dilution in params$dilutions) {
    # Plot levey-jennings
    p <- inject(plot_levey_jennings(
      list_of_plates = list_of_plates, analyte_name = analyte_names[i], dilution = dilution, !!!extra_args
    ))
    cat("![](", svg_plot_file(p, width = 7, height = 5), "){width='50%'}")
  }

  cat("\n\n")
}
```

------------------

### &nbsp;

<hr />
<p style="text-align: center;">Generated using the <a href="https://github.com/mini-pw/SerolyzeR">SerolyzeR </a> package </p>


