library(shiny)
library(bslib)

ui <- page_navbar(
  title = "Dice Experiment",
  theme = bs_theme(bootswatch = "flatly"),
  nav_panel(title = "Single",
            icon = icon("dice"),
            h5(
              "Explore the uncertainty in estimating probability from various sample sizes, 
     using the example of a 6-sided dice."
            ),
            layout_sidebar(
              sidebar = sidebar(
                title = "Single Experiment",
                numericInput(
                  "num_rolls",
                  "Number of Rolls: 1 - 10,000",
                  value = 1,
                  min = 1,
                  step = 1
                ),
                actionButton("roll_button", "Roll")
              ),
              layout_columns(
                col_widths = c(3, 9),
                card(
                  card_header("Outcome of Each Roll"),
                  card_body(
                    tableOutput("roll_table")
                  ),
                  full_screen = TRUE
                ),
                layout_columns(
                  col_widths = 12,
                  style = "gap: 0.5rem;",
                  card(
                    card_header("Dice Roll Outcomes"),
                    card_body(class = "p-1", plotOutput("bar_chart")),
                    full_screen = TRUE
                  ),
                  card(
                    card_header("Summary of All Rolls"),
                    card_body(
                      tableOutput("summary_table"),
                      uiOutput("equation1")
                    )
                  )
                )
              )
            )
  ),
  nav_panel(title = "Multiple",
            icon = icon("clipboard-list"),
            h5(
              "Explore the uncertainty in estimating probability from various sample sizes, 
     using the example of a 6-sided dice."
            ),
            layout_sidebar(
              sidebar = sidebar(
                title = "Multiple Experiments",
                numericInput(
                  "num_rolls_right",
                  "Number of rolls: (1 - 10,000)",
                  value = 1,
                  min = 1,
                  step = 1,
                ),
                numericInput(
                  "num_experiments",
                  "Number of experiments: (1 - 100)",
                  value = 1,
                  min = 1,
                  step = 1
                ),
                actionButton("run_button", "Run")
              ),
              layout_columns(
                col_widths = c(3, 9),
                card(
                  card_header("Estimated Probability of Rolling a 4 for Each Experiment"),
                  card_body(
                    tableOutput("experiment_table")
                  )
                ),
                layout_columns(
                  col_widths = 12,
                  style = "gap: 0.5rem;",
                  card(
                    card_header("Probability of Rolling a 4 Across Experiments"),
                    card_body(class = "p-1", plotOutput("line_plot")),
                    full_screen = TRUE
                  ),
                  card(
                    card_header("Summary of Rolling a 4 Across All Experiments"),
                    card_body(
                      tableOutput("experiment_summary_table"),
                      uiOutput("equation2"),
                      uiOutput("equation3"),
                      uiOutput("equation4")
                    )
                  )
                )
              )
            )
  )
)

server <- function(input, output) {
  rolls <- reactiveVal(data.frame(Roll = integer(0), Outcome = integer(0)))
  experiments <- reactiveVal(data.frame(Experiment = integer(0),
                                        `Number of 4s Rolled` = integer(0),
                                        Probability = numeric(0),
                                        `Square Error` = numeric(0),
                                        check.names = FALSE))
  
  observeEvent(input$roll_button, {
    num_rolls <- as.integer(input$num_rolls)
    if (!is.na(num_rolls) && num_rolls > 10000) {
      showNotification("Number of rolls cannot exceed 10,000", type = "error")
    } else if (!is.na(num_rolls) && num_rolls > 0) {
      outcomes <- sample(1:6, num_rolls, replace = TRUE)
      roll_data <- data.frame(Roll = seq_len(num_rolls), Outcome = outcomes)
      rolls(roll_data)
    }
  })
  
  output$roll_table <- renderTable({
    rolls()
  })
  
  output$summary_table <- renderTable({
    roll_data <- rolls()
    summary <- data.frame(Outcome = 1:6, Count = 0)
    if (nrow(roll_data) > 0) {
      roll_summary <- as.data.frame(table(factor(roll_data$Outcome, levels = 1:6)))
      colnames(roll_summary) <- c("Outcome", "Count")
      summary$Count <- roll_summary$Count
    }
    summary$`Relative Frequency` <- formatC(summary$Count / sum(summary$Count), format = "f", digits = 3)
    summary
  })
  
  output$equation1 <- renderUI({
    withMathJax(
      "$$\\text{Relative Frequency} = \\frac{\\text{Number of Occurrences}}{\\text{Number of Independent Trials}} = \\text{Estimated Probability}$$"
    )
  })
  
  output$bar_chart <- renderPlot({
    roll_data <- rolls()
    summary <- data.frame(Outcome = 1:6, Count = 0)
    if (nrow(roll_data) > 0) {
      roll_summary <- as.data.frame(table(factor(roll_data$Outcome, levels = 1:6)))
      colnames(roll_summary) <- c("Outcome", "Count")
      summary$Count <- roll_summary$Count
    }
    par(mar = c(4.5, 4.5, 1.5, 1.5))
    bar <- barplot(
      summary$Count,
      names.arg = summary$Outcome,
      xlab = "Outcome",
      ylab = "Count",
      ylim = c(0, input$num_rolls),
      col = ifelse(summary$Outcome == 4, "dodgerblue3", "salmon2"),
      border = "black"
    )
    abline(h = (input$num_rolls / 6), col = "red", lty = 2)
    text(
      x = bar,
      y = summary$Count,
      labels = ifelse(summary$Count > 0, formatC(summary$Count / sum(summary$Count), format = "f", digits = 3), ""),
      pos = 3
    )
  })
  
  observeEvent(input$run_button, {
    num_experiments <- as.integer(input$num_experiments)
    num_rolls <- as.integer(input$num_rolls_right)
    if (!is.na(num_rolls) && num_rolls > 10000) {
      showNotification("Number of rolls cannot exceed 10,000", type = "error")
    } else if (!is.na(num_experiments) && num_experiments > 100) {
      showNotification("Number of experiments cannot exceed 100", type = "error")
    } else if (!is.na(num_experiments) && num_experiments > 0 && !is.na(num_rolls) && num_rolls > 0) {
      experiment_data <- data.frame(Experiment = seq_len(num_experiments),
                                    `Number of 4s Rolled` = integer(num_experiments),
                                    Probability = numeric(num_experiments),
                                    `Square Error` = numeric(num_experiments),
                                    check.names = FALSE)
      for (i in seq_len(num_experiments)) {
        outcomes <- sample(1:6, num_rolls, replace = TRUE)
        num_fours <- sum(outcomes == 4)
        experiment_data$`Number of 4s Rolled`[i] <- num_fours
        probability <- as.numeric(num_fours / num_rolls)
        experiment_data$Probability[i] <- formatC(probability, format = "f", digits = 3)
        experiment_data$`Square Error`[i] <- formatC((probability - (1 / 6))^2, format = "e", digits = 2)
      }
      experiments(experiment_data)
    }
  })
  
  output$experiment_table <- renderTable({
    experiments()
  })
  
  output$experiment_summary_table <- renderTable({
    experiment_data <- experiments()
    if (nrow(experiment_data) > 0) {
      avg_probability <- mean(as.numeric(experiment_data$Probability))
      true_probability <- 1 / 6
      standard_error <- sd(experiment_data$Probability) / sqrt(nrow(experiment_data))
      data.frame(
        `Average Probability` = formatC(avg_probability, format = "f", digits = 3),
        `True Probability` = formatC(true_probability, format = "f", digits = 3),
        `Standard Error` = formatC(standard_error, format = "f", digits = 4),
        check.names = FALSE
      )
    }
  })
  
  output$equation2 <- renderUI({
    withMathJax(
      "$$\\text{Square Error} = (\\text{Observed Value} - \\text{Predicted Value})^2$$"
    )
  })
  
  output$equation3 <- renderUI({
    withMathJax(
      "$$\\text{Standard Deviation (sample)} = s = \\sqrt{\\frac{\\sum_{i=1}^n (\\text{Observed}_i - \\text{Mean})^2}{n-1}}$$"
    )
  })
  
  output$equation4 <- renderUI({
    withMathJax(
      "$$\\text{Standard Error} = SE = \\frac{s}{\\sqrt{n}}$$"
    )
  })
  
  output$line_plot <- renderPlot({
    experiment_data <- experiments()
    if (nrow(experiment_data) > 0) {
      par(mar = c(4.5, 4.5, 1.5, 1.5))
      plot(
        experiment_data$Experiment, experiment_data$Probability,
        type = "o",
        pch = 16,
        cex = 1.75,
        xlab = "Experiment",
        ylab = "Probability of Rolling a 4",
        ylim = c(0, 0.5),
        col = "dodgerblue3"
      )
      abline(h = 1 / 6, col = "red", lty = 2)
    }
  })
}

shinyApp(ui, server)