library(shiny)
library(reactable)
library(ggrain)
library(raincloudplots)
library(lubridate)
library(tidyverse)
library(colorspace)
library(markdown)


data_dir <- "/var/www/html/repos/simplechoicertt/data"  # Specify the directory

# Preprocess a PsychoPy output file
# There will be separate files for separate attempts,
# so only grouping by task is required
do_preprocessing <- function(file_path) {
  
  data <- read_csv(file_path, show_col_types = FALSE) %>%
    as_tibble()
  
  # Check if the "exclude" column is present
  if (!"exclude" %in% colnames(data)) {
    return(NULL)
  }
  
  # If the exclude column is there, check if data should be excluded
  if (any(data$exclude == 1, na.rm = TRUE)) {
    return(NULL)
  }
  
  # Ensure that the Nickname column is treated as a character
  if ("Nickname" %in% colnames(data)) {
    data <- data %>%
      mutate(Nickname = as.character(Nickname))
  }
  
  # Ensure that the chooseNickBox.text is treated as a character
  if ("chooseNickBox.text" %in% colnames(data)) {
    data <- data %>%
      mutate(`chooseNickBox.text` = as.character(`chooseNickBox.text`))
  }
  
  # Ensure that the Participant column is treated as a character
  if ("Participant" %in% colnames(data)) {
    data <- data %>%
      mutate(Participant = as.character(Participant))
  }
  
  # Ensure that the Participant column is treated as a character
  if ("Gender" %in% colnames(data)) {
    data <- data %>%
      mutate(Gender = as.character(Gender))
  }
  
  # Ensure that the "Age (years)" column is treated as a character
  if ("Age (years)" %in% colnames(data)) {
    data <- data %>%
      mutate(`Age (years)` = as.character(`Age (years)`))
  }
  
  # Extract nickname and attempt before filtering
  # pull required as info is not in first row
  nickname <- data %>% 
    filter(!is.na(Nickname)) %>% 
    pull(Nickname) %>% 
    first()
  # if Attempt is empty, it's the first attempt
  # else get the attempt from the data
  attempt <- if(all(is.na(data$Attempt))) 1 else {
    data %>% 
      filter(!is.na(Attempt)) %>% 
      pull(Attempt) %>% 
      first()
  }
  
  # Preprocessing data (filtering steps)
  preproc_data <- data %>%
    # Ensure RTs are not missing
    filter(!is.na(resp.rt)) %>%
    mutate(rt_ms = resp.rt * 1000) %>%
    filter(rt_ms >= 100) %>%
    mutate(task = case_when(
      trialsSimple.ran == 1 ~ "SRT",
      trialsChoice.ran == 1 ~ "CRT",
      TRUE ~ NA_character_
    )) %>%
    filter(!is.na(task)) %>%
    group_by(task) %>%
    filter(
      (task == "SRT" & rt_ms <= 1500) |
        (task == "CRT" & rt_ms <= 2000)
    ) %>%
    filter(task == "SRT" | resp.corr == 1) %>%
    filter(
      rt_ms >= mean(rt_ms, na.rm = TRUE) - 2*sd(rt_ms, na.rm = TRUE) &
        rt_ms <= mean(rt_ms, na.rm = TRUE) + 2*sd(rt_ms, na.rm = TRUE)
    ) %>%
    ungroup()
  
  # Add nickname and attempt to every row
  preproc_data <- preproc_data %>%
    mutate(nickname = nickname, attempt = attempt, task = task)
  
  return(preproc_data)
}

# Calculate means and choice cost
compute_means <- function(myData) {
  
  mean_data <- myData %>%
    group_by(Participant, attempt, task) %>%
    summarise(
      mean_rt = round(mean(rt_ms, na.rm = TRUE), digits = 0),
      attempt = first(attempt),
      nickname = first(nickname),
      date = first(date),
      .groups = "drop"
    ) %>%
    pivot_wider(
      names_from = task,
      values_from = mean_rt,
      names_prefix = "mean_"
    ) %>%
    mutate(
      choice_cost = mean_CRT - mean_SRT,
    ) %>%
    select(Participant, nickname, attempt, mean_SRT, mean_CRT, choice_cost, date)
  
  return(mean_data)
}

# Function to create column definition
create_number_rank_colDef <- function(myData, col_name, rank_col_name, displayname) {
  colDef(
    name = paste(displayname, "(Rank)"),
    cell = function(value, index) {
      sprintf("%.0f (%.0f)", value, myData[[rank_col_name]][index])
    },
    sortable = TRUE,
    defaultSortOrder = "asc",
    sortNALast = TRUE
  )
}

# Convert date to posix date
convert_to_posixct <- function(datetime_string) {
  # Replace the underscores and "h" for a more standard format
  datetime_string <- gsub("_", " ", datetime_string)
  datetime_string <- gsub("h", ":", datetime_string)
  # Convert to POSIXct
  as.POSIXct(datetime_string, format = "%Y-%m-%d %H:%M.%OS")
}

ui <- fluidPage(
  titlePanel("Simple reaction time, choice reaction time and choice costs"),
  
  tabsetPanel(
    tabPanel("Leaderboard",
             
             #verbatimTextOutput("debug_output"),
             
             br(),
             includeMarkdown("leaderboard.md"),
             br(),
             radioButtons("date_filter", "Date Range:",
                          choices = c("Past 10 minutes", "Past hour", "Today", "Past week", "Past month", "All time"),
                          selected = "Today"),
             selectizeInput("nickname_filter", "Filter by Nickname:",
                            choices = c("All Participants" = ""),
                            options = list(create = FALSE)),
             reactableOutput("leaderboard")
    ),
    
    tabPanel("Group Data",
             #textInput("highlight_nickname", "Highlight Nickname:"), 
             #actionButton("reset_highlight", "Reset Highlight"),
             
             br(),
             includeMarkdown("group_data.md"),
             br(),
             plotOutput("choiceCostRain"),
             br(),
             plotOutput("simpleChoicePlot")
    ),
    
    tabPanel("Individual Data",
             br(),
             includeMarkdown("indiv_data.md"),
             br(),
             selectizeInput("individual_nickname", "Filter by Nickname:",
                            choices = c(""),
                            options = list(create = FALSE)),
             uiOutput("attempt_selector"),
             plotOutput("individual_plot"),
             uiOutput("individual_stats")
    )
  )
)

server <- function(input, output, session) {
  
  files <- list.files(data_dir, pattern = "*.csv", full.names = TRUE)
  
  # Create preproc_data for all files
  trialwise_data <- map_dfr(files, do_preprocessing)
  
  #View(trialwise_data)
  #write_csv(trialwise_data, "tmp.csv")
  #print(trialwise_data)
  
  # Create mean_data from the preprocessed data
  mean_data <- compute_means(trialwise_data) %>%
    filter(!is.na(mean_SRT) & !is.na(mean_CRT))
  
  #print(mean_data)
  
  # Data processing
  filtered_data <- reactive({
    
    data <- mean_data
    
    # Apply date filter
    if (input$date_filter != "All time") {
      end_date <- Sys.time()
      start_date <- switch(input$date_filter,
                           "Past 10 minutes" = end_date - minutes(10),
                           "Past hour" = end_date - hours(1),
                           "Today" = as.Date(end_date),
                           "Past week" = end_date - days(7),
                           "Past month" = end_date - months(1))
      
      #data <- data %>% filter(date >= start_date & date <= end_date)
      
      # Convert your data's datetime column to POSIXct
      data <- data %>%
        mutate(date = convert_to_posixct(date)) %>%  # Ensure the 'date' column is correctly parsed
        filter(date >= start_date & date <= end_date)
    }
    
    # Calculate ranks for all data
    # Doing this here ensures that the rank depends on the time range chosen
    # I.e., someone who's Nr. 1 today might not be the all time Nr. 1
    data <- data %>% 
      mutate(
        srt_rank = rank(mean_SRT, na.last = "keep", ties.method = "min"),
        crt_rank = rank(mean_CRT, na.last = "keep", ties.method = "min"),
        cc_rank = rank(choice_cost, na.last = "keep", ties.method = "min")
      )
    
    # A nickname has been chosen, only display this participant's data
    # Do not update the rank in this case
    if (!is.null(input$nickname_filter) && input$nickname_filter != "") {
      data <- data %>% filter(nickname == input$nickname_filter)
    }
    
    # output$debug_output <- renderPrint({
    #   list(
    #     filtered_data = data,
    #     srt_ranks = data$srt_rank,
    #     crt_ranks = data$crt_rank,
    #     cc_ranks = data$cc_rank
    #   )
    # })
    
    return(data)
      
  })
  
  # Update selectize input choices
  observe({
    nickname_choices <- c("All Participants" = "", sort(unique(mean_data$nickname)))
    updateSelectizeInput(session, "nickname_filter",
                         choices = nickname_choices,
                         selected = "All Participants",
                         server = TRUE)
  })
  
  # Custom CSS to left-align all options
  insertUI(selector = "head", where = "beforeEnd",
           ui = tags$style(HTML("
    .selectize-dropdown-content .option {
      text-align: left !important;
    }
  "))
  )
  
  #################
  # Leaderboard tab
  #################
  
  output$leaderboard <- renderReactable({
    data <- filtered_data()
    reactable(
      data,
      columns = list(
        nickname = colDef(name = "Nickname", sortable = FALSE),
        attempt = colDef(name = "Attempt", sortable = FALSE),
        
        mean_SRT = create_number_rank_colDef(data, "mean_SRT", "srt_rank", "Mean SRT"),
        mean_CRT = create_number_rank_colDef(data, "mean_CRT", "crt_rank", "Mean CRT"),
        choice_cost = create_number_rank_colDef(data, "choice_cost", "cc_rank", "Choice Cost"),
        
        Participant = colDef(show = FALSE),
        srt_rank = colDef(show = FALSE),
        crt_rank = colDef(show = FALSE),
        cc_rank = colDef(show = FALSE),
        date = colDef(show = FALSE)
      ),
      defaultSorted = "mean_SRT",
      defaultSortOrder = "asc",
      filterable = FALSE,
      showSortable = TRUE,
      showPageSizeOptions = TRUE,
      pageSizeOptions = c(10, 25, 50, 100),
      defaultPageSize = 25
    )
  })
  
  ################
  # Group data tab
  ################
  
  # choice cost plot
  output$choiceCostRain <- renderPlot({

    myData <- mean_data
    
    ggplot(myData, aes(x = 1, y = choice_cost)) +
      geom_rain(fill = "darkolivegreen3", colour = "darkolivegreen4") +
      scale_x_continuous(name = "") +
      scale_y_continuous(
        name = "Choice Cost (ms)"
      ) +
      theme_classic() +
      theme(axis.text.y = element_blank(), # hide y-axis tick labels
            axis.text.x = element_text(margin = margin(t = 5)), # adjust margins
            axis.ticks.y = element_blank(), # hide y-axis text
            axis.ticks.length = unit(.2, "cm"), # adjust length of all axis ticks
            axis.title.x = element_text(margin = margin(t = 12)), # adjust margins
            axis.title.y = element_text(margin = margin(l = 40)),
            text = element_text(size = 20),
            plot.title = element_text(hjust = 0.5, face = "bold")) +
      labs(title = "Choice Costs", subtitle = "", title.position = "plot") +
      coord_flip()

  })
  
  # simple and choice RTs
  output$simpleChoicePlot <- renderPlot({
    
    myData <- mean_data
  
    col1 = "#52D1F4"
    col2 = '#0B8AAD'
    
    dataset_plot <- data_1x1( 
      array_1 = myData$mean_SRT, #first set of values
      array_2 = myData$mean_CRT, #second set of values
      jit_distance = .09,
      jit_seed = 321) 
    
    plot_w_ties <- raincloud_1x1_repmes(
      data = dataset_plot,
      colors = (c(col1, col2)), 
      fills = (c(col1, col2)), 
      line_color = 'gray',
      line_alpha = .3,
      size = 1.5,
      alpha = .5,
      align_clouds = FALSE) +
      scale_x_continuous(name = "Task", breaks=c(1,2), labels=c("Simple", "Choice"), limits=c(0, 3)) +
      scale_y_continuous(
        name = "Reaction Time (ms)",
        breaks = seq(from = 0, to = max(myData$mean_CRT), by = 50) # add axis ticks every 50ms
      ) +
      labs(title = "Reaction Times for Simple and Choice Task", subtitle = "", title.position = "plot") +
      theme_classic() +
      theme(axis.title.x = element_text(margin = margin(t = 12)),  # adjust margins
            axis.title.y = element_text(margin = margin(r = 12)),
            axis.ticks.length = unit(.2, "cm"), # adjust length of all axis ticks
            axis.text.x = element_text(margin = margin(t = 5)),
            axis.text.y = element_text(margin = margin(r = 5)),
            text = element_text(size = 20),
            plot.title = element_text(hjust = 0.5, face = "bold"))
    plot_w_ties
  })

  #####################
  # Individual Data tab
  #####################
  
  observe({
    nickname_choices_ind <- c(sort(unique(mean_data$nickname)))
    updateSelectizeInput(session, "individual_nickname",
                         choices = nickname_choices_ind,
                         selected = "",
                         server = TRUE)
  })
  
  output$attempt_selector <- renderUI({
    req(input$individual_nickname)
    attempts <- mean_data %>%
      filter(nickname == input$individual_nickname) %>%
      pull(attempt) %>%
      unique() %>%
      sort()

    selectInput("selected_attempt", "Select Attempt:", choices = attempts)
  })

  output$individual_plot <- renderPlot({
    
    req(input$individual_nickname, input$selected_attempt)

    individual_data <- trialwise_data %>%
      filter(nickname == input$individual_nickname, attempt == input$selected_attempt)

    if (nrow(individual_data) == 0) return(NULL)

    # Get mean SRT and CRT
    mean_values <- mean_data %>%
      filter(nickname == input$individual_nickname, attempt == input$selected_attempt) %>%
      select(mean_SRT, mean_CRT)
    
    p <- ggplot(individual_data, aes(x = rt_ms, fill = task)) +
      stat_density(geom = "density", trim = TRUE, adjust = 1., alpha = .5, color = NA, position = "identity") +
      geom_rug(aes(colour = task), alpha = .5, size = 1.5, length = unit(0.035, "npc")) +
      scale_color_brewer(palette="Accent", guide = "none", limits = c("SRT", "CRT")) +  # change outline colours
      scale_fill_brewer(palette="Accent", limits = c("SRT", "CRT"), labels = c("Simple", "Choice")) +
      guides(fill = guide_legend(title = NULL)) +
      theme_classic() +
      theme(axis.title.x = element_text(margin = margin(t = 12)),  # adjust margins
            axis.title.y = element_text(margin = margin(r = 12)),
            axis.ticks.length = unit(.2, "cm"), # adjust length of all axis ticks
            axis.text.x = element_text(margin = margin(t = 5)),
            axis.text.y = element_text(margin = margin(r = 5)),
            text = element_text(size = 20),
            plot.title = element_text(hjust = 0.5, face = "bold"),
            legend.position = c(0.95, 0.95),
            legend.justification = c("right", "top"),
            legend.box.just = "right",
            legend.margin = margin(6, 6, 6, 6)
      ) +
      labs(x = "Reaction Time (ms)", y = "Density",
           title = paste0("Reaction times for participant ", input$individual_nickname, " (Attempt ", input$selected_attempt, ")"))

    # Add vertical lines for mean SRT and CRT
    p <- p +
      geom_segment(aes(x = mean_values$mean_SRT, xend = mean_values$mean_SRT, y = 0, yend = Inf), 
                   color = scales::brewer_pal(palette = "Accent")(2)[1], 
                   linetype = "solid", size = 1.5) +
      geom_segment(aes(x = mean_values$mean_CRT, xend = mean_values$mean_CRT, y = 0, yend = Inf), 
                   color = scales::brewer_pal(palette = "Accent")(2)[2], 
                   linetype = "solid", size = 1.5)
    
    # Calculate the maximum density
    max_density <- max(ggplot_build(p)$data[[1]]$density)
    
    # Add horizontal arrow from mean SRT to mean CRT with a label
    p <- p + 
      annotate("segment", x = mean_values$mean_SRT, xend = mean_values$mean_CRT, 
               y = max_density * 0.8, yend = max_density * 0.8, 
               arrow = arrow(type = "closed", length = unit(0.25, "cm")), 
               color = "black", size = 1.5) +
      annotate("text", x = (mean_values$mean_SRT + mean_values$mean_CRT) / 2, 
               y = max_density * 0.75, label = "Choice Cost", 
               color = "black", size = 6, hjust = 0.5)
    
    # Get the original colors
    original_colors <- scales::brewer_pal(palette = "Accent")(2)
    
    # Darken the colors
    darker_colors <- colorspace::darken(original_colors, amount = 0.3)
    
    # Add labels to the vertical lines
    p <- p +
      annotate("text", x = mean_values$mean_SRT, y = max_density * 0.9, 
               label = "Mean Simple", color = darker_colors[1], 
               size = 6, hjust = -0.1, vjust = -0.5, fontface = "bold") +
      annotate("text", x = mean_values$mean_CRT, y = max_density * 0.9, 
               label = "Mean Choice", color = darker_colors[2], 
               size = 6, hjust = -0.1, vjust = -0.5, fontface = "bold")
    
    p
  })

  output$individual_stats <- renderUI({
    req(input$individual_nickname, input$selected_attempt)
    
    # Filter mean_data based on nickname and attempt
    filtered_data <- mean_data %>%
      filter(nickname == input$individual_nickname,
             attempt == input$selected_attempt)
    
    if (nrow(filtered_data) == 0) return("No data available for this selection.")
    
    HTML(paste0("Mean simple reaction time: ", filtered_data$mean_SRT, " ms<br>",
           "Mean choice reaction time: ", filtered_data$mean_CRT, " ms<br>",
           "Choice Cost: ", filtered_data$choice_cost, " ms"))
  })

}

# Run the application
shinyApp(ui = ui, server = server)
    
