# TaqMan qPCR Calculator
# Written by: Peter Huynh
# Version 2.0

# Double check units

# 1.0 INITIALIZATION ----
library(shiny)
library(shinythemes)
library(shinyscreenshot)
library(mathjaxr)

if(interactive())
{
# 2.0 UI ----
    ui <- fluidPage(
        # Enable theme selector to browse available themes.
        # shinythemes::themeSelector(),
        theme = shinytheme("readable"), #alternate: cerulean
        withMathJax(),
        shinyFeedback::useShinyFeedback(),
        titlePanel("TaqMan qPCR Volume Calculator"),
        sidebarLayout(
            sidebarPanel(
                h3("Input Parameters"),
                numericInput("wells",
                    label = "Wells:",
                    value = 96,
                    min = 1,
                    step = 1
                ),
                numericInput("well_vol",
                    label = "Well volume (µL):",
                    value = 10,
                    min = 0,
                    step = 1
                ),
                numericInput("cdna_vol",
                    label = "cDNA volume (µL):",
                    value = 1,
                    min = 0,
                    step = 1
                ),
                sliderInput("probe_n",
                    label = "Number of probes:",
                    value = 2,
                    min = 1,
                    max = 8,
                    step = 1
                ),
                numericInput("probe_x",
                    label = "Probe concentration (x):",
                    value = 20,
                    min = 0,
                    step = 1
                ),
                numericInput("mmix_x",
                    label = "Master Mix concentration (x):",
                    value = 2,
                    min = 0,
                    step = 1
                ),
                actionButton("screenshot",
                    "Screenshot"
                ),
                downloadButton("downloadData",
                    "Download"
                ),
            ),

            mainPanel(
                tabsetPanel(
                    # MAIN CALCULATOR
                    tabPanel(
                        "Main",
                        h3("Parameters"),
                        tableOutput("parameter_values"),
                        h3("Calculated Volumes"),
                        tableOutput("volumes"),
                    ),
                    # CALCULATION BREAKDOWN
                    tabPanel(
                        "Calculations",
                        h3("Calculation Steps"),
                        h4("1. Determine excess wells using ceiling."),
                        fluidRow(
                            column(width = 6, wellPanel(uiOutput("calc1.1"))),
                            column(width = 6, wellPanel(uiOutput("calc1.2")))
                        ),
                        h4(HTML(paste("2. Determine excess constant k", tags$sub("excess"), ".", sep=""))),
                        fluidRow(
                            column(width = 6, wellPanel(uiOutput("calc2.1"))),
                            column(width = 6, wellPanel(uiOutput("calc2.2")))
                        ),
                        h4("3. Calculate working master mix volume."),
                        fluidRow(
                            column(width = 6, wellPanel(uiOutput("calc3.1"))),
                            column(width = 6, wellPanel(uiOutput("calc3.2")))
                        ),
                        h4(HTML(paste("4. Calculate concentration constant k", tags$sub("Conc"), ".", sep=""))),
                        fluidRow(
                            column(width = 6, wellPanel(uiOutput("calc4.1"))),
                            column(width = 6, wellPanel(uiOutput("calc4.2")))
                        ),
                        h4("5. Calculate TaqMan probe (20x) volume."),
                        fluidRow(
                            column(width = 6, wellPanel(uiOutput("calc5.1"))),
                            column(width = 6, wellPanel(uiOutput("calc5.2")))
                        ),
                        h4("6. Calculate master mix stock (2x) volume."),
                        fluidRow(
                            column(width = 6, wellPanel(uiOutput("calc6.1"))),
                            column(width = 6, wellPanel(uiOutput("calc6.2")))
                        ),
                        h4("7. Calculate molecular grade water volume."),
                        fluidRow(
                            column(width = 6, wellPanel(uiOutput("calc7.1"))),
                            column(width = 6, wellPanel(uiOutput("calc7.2")))
                        )
                    ),

                    # ABOUT PAGE
                    tabPanel(
                        "About",
                        h2("About"),
                        hr(),
                        h3("Non-Affiliation Disclaimer"),
                        p("We are not affiliated, associated, endorsed by, or in any way officially connected with Thermo Fisher Scientific Inc."),
                        p("The information provided in this module is for general information purposes only."),
                        p("All of the information is provided in good faith, however we make no guarantee or warranty of any kind regarding the accuracy, adequacy, validity, reliability, availability, or completeness of any information in this module.")
                    )
                )
            )
        )
    )

# 3.0 SERVER ----
    server <- function(input, output) {
        # DARK MODE
        output$mode <- renderText({
            paste("You are in", input$mode, "mode.'")
        })

        # SCREENSHOT
        observeEvent(
            input$screenshot,
            {
                screenshot()
            }
        )

        # DOWNLOAD
        output$downloadData <- downloadHandler(
            filename = function() {
                paste(input$wells, ".csv", sep="")
            },
            content = function(file) {
                write.csv(volumeInput(), file, row.names = FALSE)
            }
        )

        #MAIN CALCULATOR
        # Calculate number of excess wells for pipetting
        wells_calc <- reactive({
            as.integer(ceiling(input$wells * 1.125))
        })

        # Calculate working master mix volume per well
        mmix_vol_per_well <- reactive({
            as.numeric(input$well_vol) - as.numeric(input$cdna_vol)
        })

        # Calculate total working master mix volume based on number of wells and well volume
        total_vol <- reactive({
            wells_calc() * mmix_vol_per_well()
        })

        # Calculate excess ratio (used for calculations)
        kexcess <- reactive({
            wells_calc() / as.numeric(input$wells)
        })

        # Round kexcess for clean display
        kexcess_display <- reactive({
            round((wells_calc() / as.numeric(input$wells)), 4)
        })

        # Calculate concentration constant (kconc)
        kconc <- reactive({
            input$well_vol / mmix_vol_per_well()
        })

        # Round kconc for clean display
        kconc_display <- reactive({
            round((input$well_vol / mmix_vol_per_well()), 4)
        })

        # Calculate cDNA volume
        cdna_vol <- reactive({
            as.integer(input$cdna_vol)
        })
        # Calculate Master Mix volume
        mmix_vol <- reactive({
            total_vol() / as.numeric(input$mmix_x) * kconc()
        })

        # Calculate Probe volume
        probe_vol <- reactive({
            total_vol() / as.numeric(input$probe_x) * kconc()
        })


        # Assign number of probes
        probe_count <- reactive({
          as.integer(input$probe_n)
        })

        # Calculate H2O volume
        h2o_vol <- reactive({
            #total_vol() - probe_vol() - probe_vol() - mmix_vol()
            total_vol() - probe_count()*probe_vol() - mmix_vol()
        })

        # OUTPUT TABLE GENERATION
        # PARAMETER TABLE GENERATION
        parameterInput <- reactive({
            df <- data.frame(
                row.names = c(
                    "Calculated Wells",
                    "cDNA Vol. Per Well (µL)",
                    "Master Mix Vol. Per Well (µL)",
                    "k_Excess",
                    "k_Conc",
                    "[Probe] (x)",
                    "[Master Mix] (x)"
                ),
                Value = as.character(c(
                    wells_calc(),
                    cdna_vol(),
                    mmix_vol_per_well(),
                    kexcess_display(),
                    kconc_display(),
                    input$probe_x,
                    input$mmix_x
                )),
                stringsAsFactors = FALSE
            )
            df <- t(df)
        })

        # PARAMETER TABLE RENDERING
        output$parameter_values <- renderTable({
            parameterInput()
        })

        # VOLUME TABLE GENERATION
        volumeInput <- reactive({
            df <- data.frame(
                row.names = c(
                    paste0("Probe ", 1:probe_count(), " Vol. (µL)"),
                    "Master Mix Vol. (µL)",
                    "H2O Vol. (µL)",
                    "Total Vol. (µL)"
                ),
                Value = as.character(c(
                    rep(probe_vol(), probe_count()),
                    mmix_vol(),
                    h2o_vol(),
                    total_vol()
                ))
            )
            df <- t(df)
        })

        # VOLUME TABLE GENERATION
        output$volumes <- renderTable({
            volumeInput()
        })

        # CALCULATION STEPS
        # 1. Calculate excess wells using ceiling
        output$calc1.1 <- renderUI({
            withMathJax(
                helpText("$$\\lceil{\\#\\ Wells\\ \\cdot\\ 1.125}\\rceil$$"),
                helpText("$$={\\# Calculated\\ wells}$$")
            )
        })

        output$calc1.2 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\lceil{%i\\ wells \\cdot 1.125}\\rceil$$", input$wells)),
                helpText(sprintf("$$=\\lceil{%0.2f}\\rceil\\ = %i\\ calculated\\ wells$$", (input$wells*1.125), wells_calc()))
            )
        })
        # 2 Calculate excess constant (k_excess)
        output$calc2.1 <- renderUI({
            withMathJax(
                helpText("$$\\frac{\\#\\ Calculated\\ wells}{\\#\\ Target\\ wells}$$"),
                helpText("$$=k_{excess}$$")
            )
        })

        output$calc2.2 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\frac{%i\\ wells}{%i\\ wells}$$", wells_calc(), input$wells)),
                helpText(sprintf("$$=%0.4f$$", kexcess()))
            )
        })

        # 3. Calculate working master mix volume
        output$calc3.1 <- renderUI({
            withMathJax(
                helpText(sprintf("$${\\# Wells} \\cdot k_{excesss} \\cdot {Working\\ master\\ mix\\ vol.\\ per\\ well\\ (µL)}$$")),
                helpText(sprintf("$$ = {Working\\ master\\ mix\\ vol. (µL)}$$"))
            )
        })

        output$calc3.2 <- renderUI({
            withMathJax(
                helpText(sprintf("$$ %i\\ wells \\cdot \\frac{%i}{%i}\\ excess\\ \\cdot\\ %0.1f\\ µL\\ per\\ well$$", input$wells, wells_calc(), input$wells, mmix_vol_per_well())),
                helpText(sprintf("$$ = %0.1f\\ µL\\ of\\ working\\ master\\ mix$$", total_vol()))
            )
        })

        # 4. Calculate concentration constant (k_conc)
        output$calc4.1 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\frac{Final\\ well\\ vol.\\ (µL)}{Working\\ master\\ mix\\ vol.\\ per\\ well\\ (µL)}$$")),
                helpText(sprintf("$$ = {k_{Conc}}$$"))
            )
        })

        output$calc4.2 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\frac{%0.1f\\ µL\\ per\\ well}{%0.1f\\ µL\\ master\\ mix\\ per\\ well}$$", input$well_vol, mmix_vol_per_well())),
                helpText(sprintf("$$= %f$$", kconc()))
            )
        })

        # 5. Calculate TaqMan probe (20x) volume
        output$calc5.1 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\frac{Working\\ master\\ mix\\ vol.\\ (µL)}{Dilution\\ factor\\ (x)}\\ \\cdot\\ k_{Conc}$$")),
                helpText(sprintf("$$ = TaqMan\\ probe\\ vol.\\ (µL)$$"))
            )
        })

        output$calc5.2 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\frac{%0.1f\\ µL\\ master\\ mix}{%0.1f}\\ \\cdot\\ \\frac{%0.1f}{%0.1f}$$", total_vol(), input$probe_x, input$well_vol, mmix_vol_per_well())),
                helpText(sprintf("$$= %0.1f\\ µL\\ of\\ each\\ TaqMan\\ probe\\ stock$$", probe_vol()))
            )
        })

        # 6. Calculate master mix stock (2x) volume.
        output$calc6.1 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\frac{Working\\ master\\ mix\\ vol.\\ (µL)}{Dilution\\ factor\\ (x)}\\ \\cdot\\ k_{Conc}$$")),
                helpText(sprintf("$$ = Master\\ mix\\ vol.\\ (µL)$$"))
            )
        })

        output$calc6.2 <- renderUI({
            withMathJax(
                helpText(sprintf("$$\\frac{%0.1f\\ µL\\ master\\ mix}{%0.1f}\\ \\cdot\\ \\frac{%0.1f}{%0.1f}$$", total_vol(), input$mmix_x, input$well_vol, mmix_vol_per_well())),
                helpText(sprintf("$$= %0.1f\\ µL\\ of\\ master\\ mix\\ stock$$", mmix_vol()))
            )
        })

        # 7. Calculate molecular grade water volume.
        output$calc7.1 <- renderUI({
            withMathJax(
                helpText(sprintf("$$Working\\ master\\ mix\\ vol. (µL)$$")),
                lapply(1:probe_count(), function(i) {helpText(sprintf("$$-\\ TaqMan\\ probe\\ %i\\ (20x)\\ vol.\\ (µL)$$",i))}),
                helpText(sprintf("$$-\\ Master\\ mix\\ stock\\ (2x)\\ vol.\\ (µL)$$")),
                helpText(sprintf("$$=\\ H_{2}O\\ vol.\\ (µL)$$"))
            )
        })

        output$calc7.2 <- renderUI({
            withMathJax(
                helpText(sprintf("$$%0.1f\\ µL\\ of\\ working\\ master\\ mix$$", total_vol())),
                lapply(1:probe_count(), function(i) {helpText(sprintf("$$-\\ %0.1f\\ µL\\ of\\ TaqMan\\ probe\\ %i\\ (20x)$$", probe_vol(),i))}),
                helpText(sprintf("$$-\\ %0.1f\\ µL\\ of\\ master\\ mix\\ stock\\ (2x)$$", mmix_vol())),
                helpText(sprintf("$$= %0.1f\\ µL\\ of\\ H_{2}O$$", h2o_vol()))
            )
        })
    }

    # LAUNCH GUI
    shinyApp(ui,server)

}