---
title: "Raport for plate: `r paste0('*', params$plate$plate_name, '*')`"
output: html_document
params:
  plate: !r NULL
  use_model: !r NULL
  counts_lower_threshold: !r NULL
  counts_higher_threshold: !r NULL
  additional_notes: !r NULL
  extra_args: !r list()
---

```{r param-check, echo=FALSE}
library(rlang)

plate <- params$plate
additional_notes <- params$additional_notes
extra_args <- params$extra_args

if (is.null(plate)) {
  stop("The `plate` must be provided when rendering the document.")
}
if (is.null(params$use_model)) {
  stop("The `use_model` must be provided when rendering the document.")
}
if (is.null(params$counts_lower_threshold)) {
  stop("The `counts_lower_threshold` must be provided when rendering the document.")
}
if (is.null(params$counts_higher_threshold)) {
  stop("The `counts_higher_threshold` must be provided when rendering the document.")
}
if (!is(plate, "Plate")) {
  stop("The `plate` must be an instance of `Plate` class.")
}
if (!is(params$use_model, "logical")) {
  stop("The `use_model` must be a logical value.")
}
if (!is(params$counts_lower_threshold, "numeric")) {
  stop("The `counts_lower_threshold` must be a numeric value.")
}
if (!is(params$counts_higher_threshold, "numeric")) {
  stop("The `counts_higher_threshold` must be a numeric value.")
}
if (!is.null(additional_notes) && !is(additional_notes, "character")) {
  stop("The `additional_notes` must be a character value.")
}

# 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(
  "plate", "analyte_name", "model"
)

# 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)`.  
Plate was run on: `r if(!is.null(plate$plate_datetime)) format(plate$plate_datetime, SerolyzeR.env$report_datetime_format) else "Date of running plate was not found in Luminex file"`.  
Plate batch name: `r if(!is.null(plate$batch_name)) plate$batch_name else "Batch name was not found in Luminex file"`.  
This is plate with `r paste0(length(plate$sample_names), " samples and ", length(plate$analyte_names), " analytes")`.  
Standard curve sample dilutions: `r format_dilutions(plate$dilutions, plate$dilution_values, plate$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 -------------------" ` 


### 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(plate)
```
</details>


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

### Quick overview of standard curves

<details open>
  <summary>Click to show/hide overview</summary>

<style>
  /* Set up a grid layout with 5 items per row */
  .plot-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 5px; /* Space between the plots */
    margin-top: 20px;
  }

  .plot-container {
    border: 2px solid transparent;
    transition: border-color 0.3s ease;
    transition: box-shadow 0.3s ease;
    border-radius: 5px;
  }

  .plot-container.active {
    border-color: rgba(0, 0, 0, 0.65);
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.6); 
  }

  .plot-container img {
    border-radius: 5px;
  }
</style>

<script>
  document.addEventListener('DOMContentLoaded', function () {

    // Add event listeners to the standard curve thumbnails
    var thumbnailsLinks = document.querySelectorAll('.plot-container a');
    thumbnailsLinks[0].closest('.plot-container').classList.add('active');

    thumbnailsLinks.forEach(function (link) {
      link.addEventListener('click', function (event) {
        
        var selectedThumbnail = this.closest('.plot-container');
        var thumbnails = document.querySelectorAll('.plot-container');
        var generated_tablist = document.querySelectorAll('.nav.nav-tabs li');
        var targetId = this.getAttribute('href').substring(1);
        var targetElement = document.getElementById(targetId);

        // Remove 'active' class from all thumbnails and add to the clicked one
        thumbnails.forEach(function (p) {
          p.classList.remove('active');
        });
        selectedThumbnail.classList.add('active');

        // Find and add 'active' class to the corresponding tab item and remove from others
        generated_tablist.forEach(function (tabItem) {
          var tabLink = tabItem.querySelector('a');
          if (tabLink && tabLink.getAttribute('href').substring(1) === targetId) {
            tabItem.classList.add('active');
          } else {
            tabItem.classList.remove('active');
          }
        });

        // Scroll to the analyte details section after a delay
        if (targetElement) {
          setTimeout(function () {
            targetElement.scrollIntoView({
              behavior: 'smooth',
              block: 'start'
            });
          }, 160);
        }
      });
    });
  });

  // Add event listeners to the tabs in order to synchronize analyte selectors
  setTimeout(function () {
    var tabs = document.querySelectorAll('.nav.nav-tabs li');
    tabs.forEach(function (tab) {
      tab.addEventListener('click', function (event) {
        var targetId = this.querySelector('a').getAttribute('href').substring(1);
        var thumbnails = document.querySelectorAll('.plot-container');

        thumbnails.forEach(function (thumb) {
          if (thumb.querySelector('a').getAttribute('href').substring(1) === targetId) {
            thumb.classList.add('active');
          } else {
            thumb.classList.remove('active');
          }
        });
      });
    });
  }, 600);
</script>



```{r standard-curves-overview, results='asis', echo=FALSE, message=FALSE, warning=FALSE, out.width="100%", out.height="100%", dev='svglite', dpi=36}
# Code used to create dynamic tabs based on the number of analytes
# Start a grid for the plots
cat('<div class="plot-grid">')

models_list <- list()
# Generate the clickable plots within the grid
for (i in seq_along(plate$analyte_names)) {
  # HTML structure for each plot with a clickable link
  cat(paste0(
    '<div class="plot-container">',
    '<a href="#',
    tolower(plate$analyte_names[i]),
    '" role="tab" data-toggle="tab" aria-controls="',
    tolower(plate$analyte_names[i]),
    '" aria-expanded="false">'
  ))
  # Generate the plot

  model <- inject(
    create_standard_curve_model_analyte(plate = plate, analyte_name = plate$analyte_names[i], !!!extra_args)
  )

  models_list[[plate$analyte_names[i]]] <- model
  print(plot_standard_curve_thumbnail(plate, plate$analyte_names[i]))
  cat("</a></div>")
}
# Close the grid
cat("</div>")
```
</details>

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

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

  # Warning for high dose hook
  mfi <- plate$get_data(plate$analyte_names[i], "STANDARD CURVE")
  dilutions_numeric <- plate$get_dilution_values("STANDARD CURVE")
  warning <- tryCatch(handle_high_dose_hook(mfi, dilutions_numeric),
    error = function(e) e,
    warning = function(w) w
  )

  if (inherits(warning, "warning")) {
    cat("<div class='alert alert-info' role='alert'>")
    cat(warning$message)
    cat("</div>")
  }

  # Warning for bad coverage of the standard curve
  max_mfi_for_standard_curve <- max(mfi)
  test_samples <- plate$get_data(plate$analyte_names[i], "TEST")
  number_of_test_samples <- length(test_samples)
  number_of_high_test_sample <- sum(test_samples > max_mfi_for_standard_curve)
  fraction <- round(number_of_high_test_sample / number_of_test_samples * 100)

  if (fraction >= 25) {
    cat("<div class='alert alert-warning' role='alert'>")
    cat(paste0(
      "Warning: ",
      number_of_high_test_sample,
      "/",
      number_of_test_samples,
      " (",
      fraction,
      "%) of samples have MFI values greater than the maximum value on the standard curve. For data analysis, consider using nMFI values instead of RAU."
    ))
    cat("</div>")
  }

  # Plot counts
  print(plot_counts(plate,
    plate$analyte_names[i],
    lower_threshold = params$counts_lower_threshold,
    higher_threshold = params$counts_higher_threshold
  ))

  # Plot MFI
  print(inject(plot_mfi_for_analyte(
    plate = plate,
    analyte_name = plate$analyte_names[i],
    !!!extra_args
  )))

  # Plot Standard Curve
  print(inject(plot_standard_curve_analyte(plate = plate, analyte_name = plate$analyte_names[i], !!!extra_args)))
  if (params$use_model) {
    model <- models_list[[plate$analyte_names[i]]]
    print(inject(plot_standard_curve_analyte_with_model(plate = plate, model = model, !!!extra_args)))
  }

  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>


