inst/shiny-apps/scaffoldR/server.R

source("./globals/functions.R")
source("./globals/helpers.R")

#library(readxl)
#anchored_contigs <- read_excel("/Volumes/workspace/hrpazs/bilberry_genome/chr_bilberry.xlsx", 
#                               sheet =  "Billberry (2)") %>%
#  as.data.table(.) %>%
#  data.table::melt("No.", measure = 2:ncol(.), variable.name = "chr_no", value.name = "name", na.rm = TRUE) %>%
#  .[, ':='(name = substr(name, 2, length(name)),
#           rc=ifelse(grepl("\\+", name), 0, 1),
#           q = ".", gap_size = ".")]

#groups <- as.character(unique(anchored_contigs$chr_no))
#out_dir <- "/Volumes/workspace/hrpazs/bilberry_genome/groups/"
#dir.create(out_dir)

#for (i in groups){
#  data <- anchored_contigs %>%
#    .[chr_no == i, .(name, rc, q , gap_size)]
#  file = paste0(out_dir, i,".ordering")
#  write.table(data, file, row.names = FALSE, col.names = FALSE, quote = FALSE) 
#}
server <- function(input, output, session) {
  shinyOptions(progress.style = "old")
  rv <- reactiveValues(chr = NULL, s2.1 = NULL, cut_pos = 0, 
                       intramap_range = NULL,  btn_val2 = c(0,0), btn_val3 = c(0,0))
  
  ## shiny files set up
  ##volumes <- c(Home = fs::path_home(), getVolumes()(), `Test Data Directory` = "../../../")
  
  ##shinyDirChoose(input, 'directory', roots = volumes, session = session,
  ##               restrictions = system.file(package = "base"))
  
  observe({
    shiny::validate(need(input$directory != "",""))
    
   # rv$projDir <- parseDirPath(volumes, input$directory)
    rv$projDir <- input$directory
    print(rv$projDir)
    rds_list <- list.files(rv$projDir, pattern = ".rds$|.bin|.txt|.csv", 
                           recursive = TRUE, ignore.case = TRUE)
    txt_list <- list.files(rv$projDir, pattern = ".csv$|.txt$|.tab$", 
                           recursive = TRUE, ignore.case = TRUE)
    fa_list <- list.files(rv$projDir, pattern = ".fa$|.fasta$", 
                          recursive = TRUE, ignore.case = TRUE)
    
    
    updatePickerInput(session, "mapFile", choices = rds_list,                           
                      choicesOpt = list(style = paste(
                        rep_len("font-size: 12px; line-height: 1.5; 
                      margin-left: -10px; border-bottom: 1px solid gray;", 
                                length(rds_list)), "background-color: lightgray;")))
    updatePickerInput(session, "lenFile", choices = txt_list,                           
                      choicesOpt = list(style = paste(
                        rep_len("font-size: 12px; line-height: 1.5; 
                      margin-left: -10px; border-bottom: 1px solid gray;", 
                                length(txt_list)), "background-color: lightgray;")))
    
    updatePickerInput(session, "faFile", choices = fa_list,                           
                      choicesOpt = list(style = paste(
                        rep_len("font-size: 12px; line-height: 1.5; 
                      margin-left: -10px; border-bottom: 1px solid gray;", 
                                length(fa_list)), "background-color: lightgray;")))
    
    updatePickerInput(session, "lnkFile", choices = rds_list,                           
                      choicesOpt = list(style = paste(
                        rep_len("font-size: 12px; line-height: 1.5; 
                      margin-left: -10px; border-bottom: 1px solid gray;", 
                                length(rds_list)), "background-color: lightgray;")))
  })
  
  
  ## Import contact map genearted using the jupyternote book script from BAM file
  observeEvent(input$import_data,{
    withBusyIndicatorServer("import_data", {
      withProgress(message = 'Loading in progress',
                   detail = 'Please wait...', value = 1,{
                     #updatePickerInput(session, "seq1", selected = "")
                     
                     if(grepl(".rds",input$mapFile, ignore.case = TRUE)){
                       rv$contact_data <- as.data.table(as.data.frame(
                         readRDS(file.path(rv$projDir, input$mapFile))
                       ))
                     } else {
                       rv$contact_data <- fread(file.path(rv$projDir, input$mapFile), 
                                                stringsAsFactors = FALSE)
                     }
                     
                     #names(rv$contact_data) <- c("rname","pos","mrnm","mpos","n")
                     
                     binsize_ini <- sort.int(unique(rv$contact_data$pos), partial = 1:2)[1:2]
                     binsize_ini = as.integer(binsize_ini[2] - binsize_ini[1])
                     
                     ## calculate or import length of sequences
                     if(input$loadSeqLen){
                       rv$seq_len <- fread(file.path(rv$projDir,input$lenFile), 
                                           col.names = c("rname","rlen"), 
                                           select = c(1, 2)) %>%
                         .[order(-rlen),]
                     } else {
                       rv$seq_len <- rv$contact_data[,.(rlen = max(pos)), by = .(rname)][order(-rlen),]
                     }
                     
                     
                     updateNumericInput(session, "binsize2", value = binsize_ini, 
                                        min = binsize_ini,  
                                        step = binsize_ini , max = 500000)
                     
                     updateNumericInput(session, "edgeSize1",  value = 20 * binsize_ini, 
                                        min = binsize_ini, 
                                        step = binsize_ini , max = 500000)
                     
                     updateNumericInput(session, "edgeSize3",  value = 20 * binsize_ini, 
                                        min = binsize_ini, 
                                        step = binsize_ini , max = 500000)
                     ##---------------------------------------------------------
                     #[rname %in% unique(rv$contact_data[, rname]), ]
                     rv$choices <- rv$seq_len[, rname]
                     choices_opt <- list(style = paste(rep_len("font-size: 12px; line-height: 1.5; margin-left: -10px; 
                                                        border-bottom: 1px solid gray;", length(rv$choices)), 
                                                       "background-color: lightgray;"))
                     
                     updatePickerInput(session, "seq", choices = rv$choices, 
                                       choicesOpt = choices_opt)
                     updatePickerInput(session, "seq1", choices = rv$choices, 
                                       choicesOpt = choices_opt)
                     updatePickerInput(session, "seq2", choices = c( "", rv$choices), 
                                       selected = input$seq2, 
                                       choicesOpt = choices_opt)
                     
                     rv$binsize_ini <- binsize_ini
                     rv$binsize <- binsize_ini
                     rv$contact_data2 <- rv$contact_data
                   })
    })
  })
  
  ## Import assembled scaffolds 
  observeEvent(input$import_fasta,{
    withBusyIndicatorServer("import_fasta", {
      withProgress(message = 'Loading in progress',
                   detail = 'Please wait...', value = 1,{
                     
                     rv$fasta <-  readDNAStringSet(file.path(rv$projDir, input$faFile))
                   })
    })
  })
  
  
  
  ##----------------------------------------------------------------------------
  ##----------------------------------------------------------------------------
  ## Update Bin Size -----------------------------------------------------------
  observe({
    updateSliderInput(session, "binsize", value = input$binsize2, 
                      step = rv$binsize_ini , max = input$binsize2)
  })
  
  observeEvent(input$updateBinning, {
    withBusyIndicatorServer("updateBinning", {
      withProgress(message = 'Binning in progress',
                   detail = 'please wait ...', value = 1, {
                     rv$binsize <- input$binsize
                     
                     rmax <- max(rv$contact_data$pos)
                     mmax <- max(rv$contact_data$mpos)
                     rv$max <- max(rmax, mmax)
                     
                     if(rv$binsize_ini != rv$binsize){
                       bins =  seq(rv$binsize , rv$max, rv$binsize)
                       breaks =  c(0, bins, (length(bins) + 1) * rv$binsize)
                       
                       rv$contact_data2 <- rv$contact_data %>%
                         .[, ':='(rbin = cut(pos,  breaks, include.lowest = TRUE),
                                  mbin = cut(mpos,  breaks, include.lowest = TRUE))] %>%
                         .[, .(n = sum(n)), by = .(rname, mrnm, rbin, mbin)] %>%
                         .[, pos :=  as.numeric(substr(rbin, 2, gregexpr(",", rbin)[[1]][1]-1)), by = "rbin"] %>%
                         .[,mpos :=  as.numeric(substr(mbin, 2, gregexpr(",", mbin)[[1]][1]-1)), by = "mbin"] %>%
                         .[, .(rname,pos, mrnm, mpos, n)]
                     } else {
                       rv$contact_data2 <- rv$contact_data
                     }
                   })
    })
  })
  
  ## ---------------------------------------------------------------------------
  
  ## Output contig length to the browser ---------------------------------------
  output$seqLen <- DT::renderDT({
    shiny::validate(need(rv$seq_len, ""))
    
    datatable(rv$seq_len,
              rownames = FALSE, class = 'display compact row-border', 
              selection = 'single', filter = 'bottom', colnames = c("Sequence", "Length"), 
              options = list(
                pageLength = 15, dom = 'lti', autoWidth = TRUE,
                lengthMenu = list(c(5, 15, 25, -1), c('5', '15', '25', 'All')),
                scrollY = "400px",
                scrollX = TRUE,
                autoWidth= TRUE,
                initComplete = JS(
                  "function(settings, json) {",
                  "$(this.api().table().header()).css({'background-color': 'lightgray', 'color': '#000'});",
                  "}")
              )
    )
  })
  ## ---------------------------------------------------------------------------
  
  ## Output contact data to the browser ---------------------------------------
  output$contactData <- DT::renderDT({
    shiny::validate(need(rv$seq_len, ""))
    
    seq_index <- input$seqLen_rows_selected
    
    if(is.null(seq_index)) return(NULL)
    
    seq_selected <- rv$seq_len$rname[seq_index]
    
    datatable(
      rv$contact_data2[rname == seq_selected | mrnm == seq_selected,],
      rownames = FALSE, class = 'display compact cell-border', filter = 'bottom',
      options = list(
        pageLength = 15, dom = 'ltip', 
        autoWidth = TRUE,
        initComplete = JS(
          "function(settings, json) {",
          "$(this.api().table().header()).css({'background-color': '#F0F0F0', 'color': '#000'});",
          "}")
      )
    )
  })
  ## ---------------------------------------------------------------------------
  
  
  ## Contig length histogram -------------------------------------------------
  output$lenHist <- renderPlot({
    shiny::validate(need(rv$seq_len, ""))
    
    rv$seq_len %>%
      ggplot(aes(x = rlen))+
      geom_histogram(bins = 50, 
                     color = "black", 
                     fill = "dodgerblue",
                     alpha = 0.5) +
      geom_vline(aes(xintercept = mean(rlen)),
                 color="red",  size=1) 
  })
  
  ##----------------------------------------------------------------------------
  
  ## capture click events on intra-contig map view -------------------------------------
  observe({
    shiny::validate(need(rv$contact_data2, ""))
    if(is.null(input$intramap_dblclick)) 
      return(NULL)
    
    xy_str <- function(e) {
      if(is.null(e)) return(NULL)
      round(e$x/rv$binsize, 0) * rv$binsize
    }
    
    updateRadioGroupButtons(session, 'action', selected = "Cut")
    updateNumericInput(session, "cutPos", value = xy_str(input$intramap_dblclick))
  })
  ##----------------------------------------------------------------------------
  
  ## capture brush event -------------------------------------------------------
  observe({
    shiny::validate(need(rv$binsize > 0, ""))
    
    if(is.null(input$intramap_brush)) 
      return(NULL)
    
    xy_range_str <- function(e) {
      if(is.null(e)) return(NULL)
      xmin <- round(e$xmin, 1)
      range <- c(ifelse(xmin < 0, 0, round(xmin/rv$binsize, 0) * rv$binsize),
                 round(e$xmax/rv$binsize, 0) * rv$binsize)
      return(range)
    }
    rv$intramap_range <- xy_range_str(input$intramap_brush)
  })
  

  ##----------------------------------------------------------------------------
  
  ##------------------- cut sequence--------------------------------------------
  observeEvent(input$cut1 + input$cut2,{
    shiny::validate(need(input$cutPos > 0, ""))
    
    withBusyIndicatorServer("cut1", {
      withProgress(message = 'Cutting the sequence',
                   detail = 'please wait ...', value = 1,{
                     
                     cut_pos <- input$cutPos
                     tgt_contig <- input$seq
                     
                     if(grepl("_fragment_\\d+:", tgt_contig)){
                       frag1_start <- as.numeric(sub(".*_fragment_(\\d+):\\d+", "\\1", tgt_contig, perl = TRUE))
                       frag1_end <- frag1_start + cut_pos + rv$binsize - 1
                       frag2_start <- frag1_end + 1
                       frag2_end <- as.numeric(sub(".*_fragment_\\d+:", "", tgt_contig))
                     } else {
                       frag1_start <- 1
                       frag1_end <- cut_pos + rv$binsize
                       frag2_start <- frag1_end + 1
                       frag2_end <- rv$seq_len[rname == tgt_contig, rlen]
                     }
                     
                     
                     if(input$frag2Name == "Default"){
                       contig_name <- sub("_fragment_\\d+:\\d+", "", tgt_contig)
                       frag1_name <- paste0(contig_name, "_fragment_", frag1_start, ":", frag1_end)
                       frag2_name <- paste0(contig_name, "_fragment_", frag2_start, ":", frag2_end)
                     } else {
                       frag1_name <- input$frag1Name
                       frag2_name <- input$frag2Name
                     }
                     
                     
                     rv$tgt_contig_data <- rv$contact_data2 %>%
                       .[(rname == tgt_contig | mrnm == tgt_contig),] %>%
                       .[,':='(rname = ifelse(rname == tgt_contig & pos >  cut_pos, frag2_name, 
                                              ifelse(rname == tgt_contig & pos <=  cut_pos, frag1_name, rname)),
                               mrnm = ifelse(mrnm == tgt_contig & mpos >  cut_pos , frag2_name, 
                                             ifelse(mrnm == tgt_contig & mpos <=  cut_pos, frag1_name, mrnm)))] %>%
                       .[, ':='(pos = ifelse(rname == frag2_name,  pos - (cut_pos + rv$binsize), pos ),
                                mpos = ifelse(mrnm == frag2_name , mpos - (cut_pos + rv$binsize) , mpos))]
                     
                     rv$contact_data2 <- rv$contact_data2 %>%
                       .[(rname != tgt_contig & mrnm != tgt_contig),] %>%
                       rbind(., rv$tgt_contig_data)
                     
                     ## Get new sequence length---------------------------------
                     rv$seq_len <- rv$contact_data2[,.(rlen = max(pos)), by = .(rname)] %>%
                       .[order(rname),]
                     
                     #rv$seq_len <- rv$seq_len[rname == tgt_contig, rlen := cut_pos] %>%
                     #  rbind(.,list(frag2_name, tgt_len - cut_pos)) %>%
                     #  .[order(-rlen),]
                     
                     rv$choices <- rv$seq_len[["rname"]]
                     updatePickerInput(session, "seq", choices = rv$choices, selected = tgt_contig,
                                       choicesOpt = list(style = paste(
                                         rep_len("font-size: 12px; line-height: 1.5; 
                                       margin-left: -10px; border-bottom: 1px solid gray;", 
                                                 length(rv$choices)), "background-color: lightgray;")))
                     
                     ## Set plot size to default
                     rv$intramap_range <- NULL
                     
                     ## Set break position to 0
                     updateNumericInput(session, "cutPos", value = 0)
                     
                     ## Update the seq dropdown with fragment name 
                     updatePickerInput(session, "seq", selected = frag1_name)
                   })
    })
  })
  ## -----------------------------------------------------------------------------  
  
  observe({
    updateSliderInput(session, "edgeSize2", value = input$edgeSize1, 
                      step = rv$binsize_ini , max = input$edgeSize1)
  })
  
  observe({
    updateSliderInput(session, "edgeSize4", value = input$edgeSize3, 
                      step = rv$binsize_ini , max = input$edgeSize3)
  })
  
  ##----------------------------------------------------------------------------
  ## Calculate inter-sequence interactions
  observeEvent(input$calcIntraction1 + input$calcIntraction2, {
    shiny::validate(need(rv$contact_data2, ""))
    
    ## figure out which button is pressed --------------------------------------
    btn_id <- c("calcIntraction1" , "calcIntraction2")
    btn_val <- c(input$calcIntraction1, input$calcIntraction2) - rv$btn_val2 
    i <- which(btn_val == max(btn_val))
    rv$btn_val2 <- btn_val
    
    
    withBusyIndicatorServer(btn_id[i], {
      withProgress(message = 'Calculating interaction number',
                   detail = 'please wait ...', value = 1, {
                     
                     if(btn_id[i] == "calcIntraction1"){
                       shinyjs::hide("IntConfig2")
                       edge_slc <- input$edgeSize2
                       updateSliderInput(session, "edgeSize3", 
                                         value = input$edgeSize2, 
                                         step = rv$binsize_ini , max = 1000000)
                     } else {
                       shinyjs::hide("IntConfig1")
                       edge_slc <- input$edgeSize4
                       updateSliderInput(session, "edgeSize1", 
                                         value = input$edgeSize4, 
                                         step = rv$binsize_ini , max = 1000000)
                     }
                     
                     rv$interaction_counts <-  rv$seq_len[rv$contact_data2[rname != mrnm,], on = c("rname")] %>%
                       .[rv$seq_len, on = c("mrnm" = "rname"), ':='(mrnm_len = i.rlen)] %>%
                       .[((pos < edge_slc | pos > rlen - edge_slc) & (mpos < edge_slc | mpos > mrnm_len - edge_slc)),] %>%
                       .[, ':='(rname_strand = ifelse(pos < edge_slc, "-", 
                                                      ifelse(pos > rlen - edge_slc, "+", "M")),
                                mrnm_strand = ifelse(mpos < edge_slc, "+", 
                                                     ifelse(mpos > mrnm_len - edge_slc, "-", "M")),
                                edge_rname = ifelse(rlen < edge_slc, rlen, edge_slc),
                                edge_mrnm = ifelse(mrnm_len < edge_slc, mrnm_len, edge_slc))] %>%
                       .[order(rlen, decreasing = TRUE), .(link_no = .N, sum = sum(n), 
                                                           edge_rname = max(edge_rname), 
                                                           edge_mrnm = max(edge_mrnm)), 
                         by = .(rname, mrnm, rname_strand, mrnm_strand, rlen, mrnm_len)] %>%
                       .[,.(link_density = round(link_no/(ceiling(edge_rname/rv$binsize) * 
                                                            ceiling(edge_mrnm/rv$binsize)),2), 
                            link_no, sum, avg = sum/link_no), 
                         by = .(rname, mrnm, rname_strand, mrnm_strand, rlen, mrnm_len)]
                     
                     ## UI updates ---------------------------------------------
                     #bin_rname =(link_no/ceiling(min(edge_rname,edge_mrnm)/rv$binsize))/10
                     rv$choices <- unique(rv$interaction_counts$rname)
                     choices_opt <- list(style = paste(rep_len("font-size: 12px; line-height: 1.5; margin-left: -10px; 
                                                               border-bottom: 1px solid gray;", length(rv$choices)), 
                                                       "background-color: lightgray;"))
                     
                     updatePickerInput(session, "seq", 
                                       choices = c( "", rv$choices), selected = isolate(input$seq),
                                       choicesOpt = choices_opt)
                     
                     updatePickerInput(session, "seq1", 
                                       choices = c( "", rv$choices), selected = isolate(input$seq1),
                                       choicesOpt = choices_opt)
                     
                     updatePickerInput(session, "seq2", 
                                       choices = c( "", rv$choices), selected = isolate(input$seq2),
                                       choicesOpt = choices_opt)
                     
                     updateNumericInput(session, "edgeSize3", min = rv$binsize, value = edge_slc, 
                                        step = rv$binsize , max = 500000)
                     
                     #3 set plot size back to default
                     rv$edge_slc <- edge_slc
                     shinyjs::hide("IntConfig1", anim = TRUE)
                     shinyjs::hide("IntConfig2", anim = TRUE)
                     shinyjs::show("ScafConfig", anim = TRUE)
                   })
    })
  })
  
  ##----------------------------------------------------------------------------
  ## Or import pre-calculated interaction file
  observeEvent(input$importLnkFile, {
    withBusyIndicatorServer("importLnkFile", {
      withProgress(message = 'Importing interactions file',
                   detail = 'please wait ...', value = 1, {
                     
                     if(grepl(".rds",input$lnkFile, ignore.case = TRUE)){
                       interaction_counts <- readRDS(file.path(rv$projDir, input$lnkFile))
                     } else {
                       interaction_counts <- fread(file.path(rv$projDir,input$lnkFile))
                     }
                     
                     # These columns are being used for downstream calculations
                     required_cols <- c("rname", "mrnm", "mrnm_strand", "rname_strand", "mrnm_len", "rlen", "link_density")
                     
                     if(any(required_cols %!in% names(interaction_counts)))
                       stop(paste("Columns", paste0(required_cols, collapse = ", "), 
                                  "must be present in the file"))
                     
                     rv$interaction_counts <- interaction_counts
                     shinyjs::hide("IntConfig1", anim = TRUE)
                     shinyjs::hide("IntConfig2", anim = TRUE)
                   })
    })
  })
  
  output$interactionCounts <- DT::renderDataTable({
    shiny::validate(need(rv$interaction_counts,""))
    
    DT::datatable(rv$interaction_counts[link_no > 10,],
                  escape = FALSE, filter = 'bottom', rownames= FALSE, 
                  class = 'nowrap display compact order-column cell-border stripe', 
                  extensions = c('Buttons', 'ColReorder'), selection = "single",
                  options = list(dom = 'lBfrtip',
                                 buttons = list(list(
                                   extend  = 'collection',
                                   buttons = c('excel', 'copy'),
                                   text    = 'Download shown entries'
                                 ), 'colvis'),
                                 colReorder = list(realtime = FALSE),
                                 lengthMenu = list(c(10, 15, 25, 50, 100, 1000), c('10','15', '25', '50', '100', '1000')),
                                 pageLength = 15,
                                 fixedColumns = list(leftColumns = 1),
                                 searchHighlight = TRUE,
                                 autoWidth = FALSE,
                                 scrollX = TRUE,
                                 initComplete = htmlwidgets::JS(
                                   "function(settings, json) {",
                                   "$(this.api().table().header()).css({'background-color': '#C8C8C8', 'color': '#000'});",
                                   "}"))
    )
  })
  
  
  output$interactionCountsExp <- downloadHandler(
    filename = function() {
      paste(Sys.Date(), '-interaction_counts-',rv$edge_slc, '.csv', sep='')
    },
    content = function(con) {
      fwrite(rv$interaction_counts , con, row.names = FALSE)
    }
  )
  
  observeEvent(input$svInteractionCounts, {
    withBusyIndicatorServer("svInteractionCounts", {
      withProgress(message = 'Saving link data',
                   detail = 'please wait ...', value = 1, {
                     base_name <- sub("\\d+-\\d+-\\d+_(\\w+).rds|.bin|.txt*", "\\1",input$mapFile, 
                                      ignore.case = TRUE)
                     
                     file_name <- paste0(Sys.Date(),"_", base_name, '_linkCounts_',rv$edge_slc, '.rds')
                     
                     saveRDS(rv$interaction_counts, 
                             file.path(rv$projDir, file_name)
                     )
                   })
    })
  })
  ##------------------------------------------------------------------------------------------------------------
  ##------------------------------------------------------------------------------------------------------------
  observe({
    shiny::validate(need(input$seq1, ""))
    shiny::validate(need(rv$interaction_counts, ""))
    
    seq <- input$seq1
    
    if(input$dir1 == "Backward"){
      subseq1 <- rv$interaction_counts[mrnm %in% seq & (!rname %in% seq),] %>%
        .[order(link_density, link_no, avg, decreasing = TRUE), .(Subsequent_seq = rname, Strand = rname_strand)] 
    } else {
      subseq1 <- rv$interaction_counts[rname %in% seq & (!mrnm %in% seq),] %>%
        .[order(link_density, link_no, avg, decreasing = TRUE), .(Subsequent_seq = mrnm, Strand = mrnm_strand)]
    }
    
    choices <- unique(subseq1$Subsequent_seq)
    updatePickerInput(session, "subseq1", choices = choices,
                      choicesOpt = list(style = paste(
                        rep_len("font-size: 12px; line-height: 1.5; 
                                 margin-left: -10px; border-bottom: 1px solid gray;", 
                                length(choices)), 
                        "background-color: lightgray; z-index: 9999999;")))
  })
  
  ##------------------------------------------------------------------------------------------------------------
  ##------------------------------------------------------------------------------------------------------------
  
  # click event to link density
  observe({
    shiny::validate(need(rv$contact_data2, ""))
    if(is.null(input$intramap_click)) return(NULL)
    
    xy <- function(e) {
      if(is.null(e)) return(NULL)
      x <- round(e$x/rv$binsize, 0)
      y <- round(e$y/rv$binsize, 0)
      
      if(abs(x -y) > 5) return(NULL)
      
      pos <- min(x, y) * rv$binsize
      return(pos)
    }
    
    rv$tgt_pos = xy(input$intramap_click)
  })
  
  observe({
    shiny::validate(need(rv$tgt_pos, ""))
    
    tgt_contig <- input$seq1
    edge_slc <- input$edgeSize1
    
    tgt_linkDen <- rv$contact_data2[rname == tgt_contig & mrnm == tgt_contig,] %>%
      .[(pos >= rv$tgt_pos - edge_slc & pos <= rv$tgt_pos & mpos > rv$tgt_pos &  mpos <= rv$tgt_pos + edge_slc),] %>%
      .[, .(link_no = .N, sum = sum(n), 
            min_pos = min(pos), min_mpos = min(mpos), 
            max_pos = max(pos), max_mpos = max(mpos)), 
        by = .(rname)] %>%
      .[,.(link_density = round(link_no/(ceiling((max_pos - min_pos)/rv$binsize) * ceiling((max_mpos - min_mpos)/rv$binsize)),2), 
           link_no, sum, avg = sum/link_no), 
        by = .(rname)]
    print(tgt_linkDen)
  })
  
  ##------------------------------------------------------------------------------------------------------------
  ##------------------------------------------------------------------------------------------------------------
  #output first map
  
  # move up and down the options list ---------------
  observeEvent(input$down,{
    shiny::validate(need(rv$choices, ""))
    
    i <- which(rv$choices == input$seq)
    i = i + 1
    
    if(i > length(rv$choices)){
      return(NULL)
    } else {
      updatePickerInput(session, "seq",  
                        selected = rv$choices[i])
    }
    
  })
  
  observeEvent(input$up, {
    shiny::validate(need(rv$choices, ""))
    
    i <- which(rv$choices == input$seq)
    i = i - 1
    
    if(i < 1){
      return(NULL)
    } else {
      updatePickerInput(session, "seq",  
                        selected = rv$choices[i])
    }
  })
  
  ##-------------------------------------------------------
  ##-------------------------------------------------------
  observeEvent(input$exitZoom1, {
    session$resetBrush("intramap_brush")
    rv$intramap_range <- NULL
  })
  
  observeEvent(input$clearBrush1, {
    session$resetBrush("intramap_brush")
    
    if(input$cut_pos > 0){
      updateNumericInput(session, "cutPos", value = 0)
    }
  })
  
  observe({
    shiny::validate(need(input$seq, ""))
    input$cut1
    input$cut2
    
    updateNumericInput(session, "cutPos", value = 0)
    
    withProgress(message = 'Preparing plot',
                 detail = 'please wait ...', value = 1,{
                   p <- rv$contact_data2[rname == input$seq & mrnm == input$seq, ]
                   
                   if(nrow(p) == 0) 
                     return(NULL)
                   
                   p <- p %>% ggplot(aes(x = pos, y = mpos, fill=log10(n/2))) +
                     geom_tile(color = "red", size = 0.1) +
                     scale_fill_gradient(low = "#ffe6e6", high = "red") +
                     scale_x_continuous(expand=c(0,0)) +
                     scale_y_continuous(expand=c(0,0)) +
                     labs(title = input$seq , subtitle = paste0("Size: ", 
                                                                max(p$pos/1000000), " Mb"), x = "", y = "") +
                     #coord_cartesian(ylim=c(-12,12)) +
                     theme(axis.text = element_blank(),
                           axis.ticks = element_blank(), 
                           legend.position = "none",
                           panel.border = element_rect(colour = "gray", fill = NA),
                           panel.background = element_rect(fill = "white", colour = "white"))
                   
                   rv$intramap_range <- NULL
                   rv$intramap_plot <- p
                 })
    
  })
  
  output$intramap <- renderPlot({
    shiny::validate(need(rv$intramap_plot, ""))
    
    if(!is.null(rv$intramap_range) && diff(rv$intramap_range) != 0){
      p <- rv$intramap_plot +
        coord_cartesian(ylim = rv$intramap_range, 
                        xlim = rv$intramap_range) 
      
      session$resetBrush("intramap_brush")
    } else {
      p <- rv$intramap_plot 
    }
    
    cut_pos <- input$cutPos
    if(cut_pos > 0){
      p + geom_vline(xintercept = cut_pos, color = "blue", size = 0.3) +
        geom_hline(yintercept = cut_pos, color = "blue", size = 0.3)
    } else {
      p
    }
    
  }, height = function(){
    if(is.null(input$dimension[2])){
      400
    } else {
      0.798 * input$dimension[2]
    }
  }, width = function(){
    if(is.null(input$dimension[2])){
      400
    } else {
      0.798 * input$dimension[2]
    }
  })
  
  
  ###--------------------------------
  ##----------------join-------------
  ###--------------------------------
  
  joinedmap_data <- reactive({
    shiny::validate(need(input$action == "Join", ""))
    shiny::validate(need(input$seq1, ""))
    shiny::validate(need(input$subseq1, ""))
    shiny::validate(need(rv$interaction_counts, ""))
    
    strand <- ifelse(input$subseq1_strand, "+", "-")
    subseq = paste0(strand, input$subseq1)
    
    join_maps(rv$contact_data2, seq = input$seq1, subseq = subseq, 
              direction = input$dir1, 
              binsize = isolate(rv$binsize))
  })
  
  
  output$joinedmap <- renderPlot({
    shiny::validate(need(joinedmap_data(), ""))
    
    len <- joinedmap_data()[, .(len= max(pos)), 
                            by = .(rname)] %>%
      .[len == min(len), ]
    
    ggplot(joinedmap_data(),aes(x = pos, y = mpos, fill=log10(n))) +
      geom_tile(color = "red", size = 0.1) +
      scale_fill_gradient(low = "#ffe6e6", high = "red") +
      labs(title = paste("Total size:", max(len$len)/1000000, "MB"), 
           subtitle = len$rname[1], x = "", y = "") +
      scale_x_continuous(expand=c(0,0)) +
      scale_y_continuous(expand=c(0,0)) +
      geom_vline(xintercept = len$len	, color = "blue", size = 0.1) +
      geom_hline(yintercept = len$len	, color = "blue", size = 0.1) +
      theme(axis.text = element_blank(),
            axis.ticks = element_blank(), 
            legend.position = "none",
            panel.border = element_rect(colour = "gray", fill = NA),
            panel.background = element_rect(fill = "white", colour = "white"))
    
  }, height = function(){
    if(is.null(input$dimension[2])){
      400
    } else {
      0.798 * input$dimension[2]
    }
  }, width = function(){
    if(is.null(input$dimension[2])){
      400
    } else {
      0.798 * input$dimension[2]
    }
  })
  
  
  
  
  
  observeEvent(input$join,{
    shiny::validate(need(input$subseq1, ""))
    
    withBusyIndicatorServer("join", {
      withProgress(message = 'Joining the sequences',
                   detail = 'please wait ...', value = 1, {
                     
                     tgt_contig <- input$seq1
                     subseq_contig <- input$subseq1
                     
                     strand <- ifelse(input$subseq1_strand, "+", "-")
                     
                     if(input$dir1 == "Forward"){
                       new_scaffold <- paste0("+", tgt_contig," & ",strand, subseq_contig)
                     } else {
                       new_scaffold <- paste0(strand, subseq_contig, " & +",tgt_contig)
                     }
                     
                     tgt_len <- max(rv$contact_data2[rname == tgt_contig, pos])
                     subseq_len <- max(rv$contact_data2[rname == subseq_contig, pos])
                     new_contig_len <- tgt_len + subseq_len
                     subseq_start <- tgt_len + rv$binsize
                     
                     rv$contact_data2 <- rv$contact_data2 %>%
                       #.[(rname %in% tgt_contig | mrnm %in% tgt_contig),] %>%
                       .[, ':='(pos = ifelse(rname == subseq_contig,  pos + subseq_start, pos ),
                                mpos = ifelse(mrnm == subseq_contig , mpos + subseq_start , mpos))] %>%
                       .[,':='(rname = ifelse(rname %in% c(tgt_contig, subseq_contig), new_scaffold, rname),
                               mrnm = ifelse(mrnm %in% c(tgt_contig, subseq_contig), new_scaffold, mrnm))]
                     
                     #rv$seq_len <- rv$seq_len %>%
                     #  rbind(.,list(new_scaffold, new_contig_len))
                     
                     rv$seq_len <- rv$contact_data2[,.(rlen = max(pos)), by = .(rname)] %>%
                       .[order(-rlen),]
                     
                     rv$choices <- rv$contact_data2[,.(rname, rlen = max(pos)), 
                                                    by = .(rname)][order(-rlen), rname]
                     choices_style <- list(style = 
                                             paste(rep_len("font-size: 12px; line-height: 1.5; margin-left: -10px; 
                                                                 border-bottom: 1px solid gray;", 
                                                           length(rv$choices)
                                             ), 
                                             "background-color: lightgray;")
                     )
                     
                     updatePickerInput(session, "seq", choices = rv$choices, 
                                       selected = new_scaffold,
                                       choicesOpt = choices_style)
                     updatePickerInput(session, "seq1", choices = rv$choices,
                                       choicesOpt = choices_style)
                     updatePickerInput(session, "subseq1", selected = "")
                     
                     #set plot size to default
                     rv$intramap_range <- NULL
                   })
    })
  })
  

  ###--------save changes to disk with new name -------------------------------- 
  observeEvent(input$saveBinData1 + input$saveBinData2, {
    
    ## figure out which button is pressed --------------------------------------
    btn_id <- c("saveBinData1" , "saveBinData2")
    btn_val <- c(input$saveBinData1, input$saveBinData2) - rv$btn_val3 
    i <- which(btn_val == max(btn_val))
    rv$btn_val3 <- btn_val
    
    
    withBusyIndicatorServer(btn_id[i], {

      withProgress(message = 'Saving contact data',
                   detail = 'please wait ...', value = 1, {
                     base_name <- sub("\\d+-\\d+-\\d+_(\\w+).rds|.bin|.txt*", "\\1",input$mapFile, 
                                      ignore.case = TRUE)
                     
                     saveRDS(rv$contact_data2, 
                             file.path(rv$projDir, paste0(Sys.Date(), "_", base_name, ".rds"))
                     )
                   })
    })
  })
  
  ##------------------------------------------------------------------------------------------------------------
  ##------------------------------------------------------------------------------------------------------------

  # plot hi-c map for whole scaffold or genome
  
  observeEvent(input$combineMaps,{
    withBusyIndicatorServer("combineMaps",{
      withProgress(message = 'Preparing contact data', value = 1, 
                   detail = "please be patient ...", {
                     #chr <- if(input$inputType == "Manual"){base::strsplit(input$scaf_man, "\n")[[1]]} else {input$anchored_seqs}
                     #chr <- input$anchored_seqs
                     chr <- base::strsplit(input$seqList, "\n")[[1]]
                     
                     rv$combined_maps <- join_maps_plus(mat = rv$contact_data2, 
                                                        seq = chr, direction = "Forward", 
                                                        binsize = isolate(rv$binsize))
                     
                     #fwrite(chr, paste0("./","chr",chr_no,"_",n, ".csv"), row.names = FALSE)
                     #saveRDS(chr, paste0("chr1",chr_no,".rds"))
                     #chr <- fread(paste0("chr6-", 16,".csv"), stringsAsFactors = FALSE)
                   })
    })
  })
  
  output$map2 <- renderPlot({
    shiny::validate(need(rv$combined_maps, ""))
    
    len <- rv$combined_maps[, .(len = max(pos)), by = .(rname)] %>%
      .[order(len), ]
    #rv$combined_maps[rname != mrnm | pos != mpos, ], 
    p <- ggplot(rv$combined_maps[rname != mrnm | pos != mpos, ], 
           aes(x = pos, y = mpos, fill=log10(n/2))) +
      geom_tile() +
      scale_fill_gradient(low = "white", high = "red") +
      labs(title = isolate(input$titleMap2), 
           subtitle = paste("Total size:", max(len$len)/1000000, "MB"), x = "", y = "") +
      scale_x_continuous(expand=c(0,0)) +
      scale_y_continuous(expand=c(0,0)) +
      theme(axis.text = element_blank(),
            axis.ticks = element_blank(), 
            legend.position = "none",
            panel.border = element_rect(colour = "gray", fill = NA),
            panel.background = element_rect(fill = "white", colour = "white"))
    
    ggsave(file.path(isolate(rv$projDir), "map2.png"), dpi = 320)
    
    p
    
    
  }, height = function() {
    if(is.null(input$dimension[2])){
      400
    } else {
      0.8 * input$dimension[2]
    }
  }, width = function() {
    if(is.null(input$dimension[2])){
      400
    } else {
      0.8 * input$dimension[2]
    }
  })
  
  output$map2_hoverinfo <- renderTable({
    shiny::validate(need(rv$combined_maps, ""))
    
    res <- nearPoints(rv$combined_maps, input$map2_hover, "pos", "mpos", threshold = 1, maxpoints = 1)
    
    if(nrow(res) == 0)
      return()
    res
  })
  
  
  
  
  ##------------------------------------------------------------------------------------------------------------
  ##------------------------------------------------------------------------------------------------------------
  observe({
    shiny::validate(need(input$seq2, ""))
    shiny::validate(need(rv$interaction_counts, ""))
    #shiny::validate(need(input$nrSeq > 0, ""))
    
    ## react when refresh button is pressed 
    input$refresh
    
    ##--------------------------------------------------------------------------
    len <- length(isolate(input$anchored_seqs))
    seq2 <- input$seq2
    leading_seq <- seq2[length(seq2)]
    leadingSeq_len <-  max(rv$contact_data2[rname == leading_seq, pos])
    maxLinkDen <- input$maxLinkDen
    minSeqLen <- input$minSeqLen * 1000000
    rv$nr_seq <- isolate(input$nrSeq)
    
    
    if(len > 1 & rv$nr_seq > 1){
      if(leadingSeq_len < 20 * rv$binsize){
        leading_seq <- gsub("^[-+]", "",rv$s1)
      } else {
        leading_seq <- leading_seq
      }
    }
    
    if(input$dir2 == "Forward"){
      rv$subseq2 <- rv$interaction_counts[rname %in% leading_seq & 
                                              (!mrnm %in% leading_seq) & 
                                              link_density <= maxLinkDen &  mrnm_len >= minSeqLen,] %>%
        .[order(link_density, link_no, avg, decreasing = TRUE), 
          .(Subsequent_seq = mrnm, Length = mrnm_len, Strand = mrnm_strand, link_no, link_density)]
    } else {
      rv$subseq2 <- rv$interaction_counts[mrnm %in% leading_seq & 
                                              (!rname %in% leading_seq) & 
                                              link_density <= maxLinkDen & 
                                              rlen >= minSeqLen,] %>%
        .[order(link_density, link_no, avg, decreasing = TRUE), 
          .(Subsequent_seq = rname, Length = rlen, Strand = rname_strand, link_no, link_density)] 
    }
  })
  
  observe({
    shiny::validate(need(rv$subseq2, ""))
    
    used_seqs <- gsub("^[-+]", "", c(input$anchored_seqs, base::strsplit(input$excluded_seqs, "\n")[[1]]))
    
    rv$subseq2.1 <- rv$subseq2[(!(Subsequent_seq %in% used_seqs)), ]
    rv$choices2 <- unique(rv$subseq2.1$Subsequent_seq)
    
    updatePickerInput(session, "subseq2", choices =  rv$choices2,
                      choicesOpt = list(
                        style = paste(
                          rep_len("font-size: 12px; line-height: 1.5; 
                                  margin-left: -10px; border-bottom: 1px solid gray;", 
                                  length(rv$choices2)), "background-color: lightgray;")))
    shinyWidgets::updateSwitchInput(session, "strand_3", value = rv$subseq2.1$Strand[1] == "+")
  })
  
  ##----------------------------------------------------------------------------
  ##----------------------------------------------------------------------------
  output$Subsequent <- DT::renderDT({
    datatable(rv$subseq2.1,
              rownames = FALSE, class = 'display compact row-border', 
              selection = 'single', filter = 'bottom',
              options = list(
                pageLength = 15, dom = 'lti', 
                lengthMenu = list(c(5, 15, -1), c('5', '15', 'All')),
                ScrollY = "500px",
                scrollX = TRUE,
                autoWidth= TRUE,
                initComplete = JS(
                  "function(settings, json) {",
                  "$(this.api().table().header()).css({'background-color': '#F0F0F0', 'color': '#000'});",
                  "}")
              )
    )
  })
  ##----------------------------------------------------------------------------  
  ##---------------------------------------------------------------------------- 
  
  # move up and down the options list ------------------------------------------
  observeEvent(input$down1,{
    shiny::validate(need(rv$choices2, ""))
    
    i <- which(rv$choices2 == input$subseq2)
    i = i + 1
    
    if(i > length(rv$choices2)) return(NULL)
    s2 <- rv$choices2[i]
    strand_3 <- rv$subseq2.1[Subsequent_seq ==  s2, Strand][1]
    
    updatePickerInput(session, "subseq2",  selected = rv$choices2[i])
    shinyWidgets::updateSwitchInput(session, "strand_3", value = strand_3 == "+")
  })
  
  observeEvent(input$up1, {
    i <- which(rv$choices2 == input$subseq2)
    i = i - 1
    
    if(i < 1) return(NULL)
    s2 <- rv$choices2[i]
    strand_3 <- rv$subseq2.1[Subsequent_seq ==  s2, Strand][1]
    updatePickerInput(session, "subseq2",  selected = rv$choices2[i])
    shinyWidgets::updateSwitchInput(session, "strand_3", value = strand_3 == "+")
  })
  #-----------------------------------------------------------------------------
  
  scaffold_map <- reactive({
    shiny::validate(need(input$subseq2, ""))
    shiny::validate(need(input$seq2, ""))
    shiny::validate(need(rv$nr_seq > 0, ""))
    
    withBusyIndicatorServer("add", {
      strand_2 <- ifelse(input$strand_2, "+", "-")
      strand_3 <- ifelse(input$strand_3, "+", "-")
      rv$s2.1 <- paste0(strand_3, input$subseq2)
      chr <- isolate(input$anchored_seqs)
      direction <- isolate(input$dir2)
      
      if(rv$nr_seq == 1 | is.null(chr)){
        rv$s1 <- paste0(strand_2, input$seq2)
      } else {
        len <- length(chr)
        nrSeq <- min(len, rv$nr_seq)
        
        if(len == 1){
          rv$s1 <- paste0(strand_2, input$seq2)
        } else {
          if(direction == "Forward"){
            rv$s1 <- chr[((len-nrSeq) + 1):len]
          } else {
            rv$s1 <- chr[1:nrSeq]
          }
        }
      }
      
      join_maps_plus(rv$contact_data2, seq = rv$s1, subseq = rv$s2.1, 
                     binsize = isolate(rv$binsize),  
                     direction = direction)
    })
  })
  
  
  
  ##----------------------------------------------------------------------------  
  ## Hi-C map-------------------------------------------- 
  observe({
    shiny::validate(need(scaffold_map(), ""))
    
    withBusyIndicatorServer("add", {
      
      # get total length
      total_len <- max(scaffold_map()[["pos"]])
      
      # get joining points
      len <- scaffold_map()[,.(len = min(pos)), by = .(rname)][len != 0, ]
      
      join_col <- if(nrow(len) == 1){"blue"} else {c(rep_len("gray", nrow(len)-1),"blue")}
      
      if(isolate(input$dir2) == "Backward"){
        join_col <- sort.default(join_col, decreasing = FALSE)
      }
      
      
      # plot
      rv$map1_plot <- ggplot(scaffold_map(), 
                             aes(x = pos, y = mpos, fill=log10(n/2))) +
        geom_tile(color = "red", size = 0.1) +
        scale_fill_gradient(low = "#ffe6e6", high = "red") +
        labs(title = isolate(rv$s2.1) , subtitle = 
               paste0("Total size: ", total_len/1000000, " Mb"), 
             x = "", y = "") +
        scale_x_continuous(expand=c(0,0)) +
        scale_y_continuous(expand=c(0,0)) +
        geom_vline(xintercept = len$len, color = join_col, size = 0.3) +
        geom_hline(yintercept = len$len, color = join_col, size = 0.3) +
        theme(axis.text = element_blank(),
              axis.ticks = element_blank(), 
              legend.position = "none",
              panel.border = element_rect(colour = "gray", fill = NA),
              panel.background = element_rect(fill = "white", colour = "white"))
    })
  })
  
  
  output$map1 <- renderPlot({
    shiny::validate(need(rv$map1_plot, ""))
    
    if(!is.null(rv$map1_range)){
      p <- rv$map1_plot + coord_cartesian(ylim = rv$map1_range, xlim = rv$map1_range) 
      session$resetBrush("map1_brush")
    } else {
      p <- rv$map1_plot
    }
    
    p
    
  }, height = function(){
    if(is.null(input$dimension[2])){
      400
    } else {
      0.798 * input$dimension[2]
    }
  }, width = function(){
    if(is.null(input$dimension[2])){
      400
    } else {
      0.798 * input$dimension[2]
    }
  })
  
  observe({
    shiny::validate(need(scaffold_map(), ""))
    
    if(is.null(input$map1_brush)) 
      return(NULL)
    
    xy_range_str <- function(e) {
      if(is.null(e)) return(NULL)
      xmin <- round(e$xmin, 1)
      range <- c(ifelse(xmin < 0, 0, round(xmin/rv$binsize, 0) * rv$binsize),
                 round(e$xmax/rv$binsize, 0) * rv$binsize)
      
      return(range)
    }
    rv$map1_range <- xy_range_str(input$map1_brush)
  })
  
  observeEvent(input$exitZoom2, {
    session$resetBrush("map1_brush")
    rv$map1_range <- NULL
  })
  
  observeEvent(input$clearBrush2, {
    session$resetBrush("map1_brush")
  })
  
  output$map1_hoverinfo <- renderTable({
    shiny::validate(need(rv$combined_maps, ""))
    # For base graphics, we need to specify columns, though for ggplot2,
    # it's usually not necessary.
    res <- nearPoints(scaffold_map(), input$map_hover, "pos", "mpos", threshold = 1, maxpoints = 1)
    if (nrow(res) == 0)
      return()
    res
  })
  
  ##----------------------------------------------------------------------------
  ## Join sequences together ---------------------------------------------------
  observeEvent(input$add,{
    withBusyIndicatorServer("add", {
      
      direction <- input$dir2
      
      if(length(input$anchored_seqs) == 0){
        if (direction == 'Backward'){
          rv$chr <- c( rv$s2.1, rv$s1)
        } else {
          rv$chr <- c(rv$s1,  rv$s2.1)
        }
      } else {
        if (direction == 'Backward'){
          rv$chr <- c(rv$s2.1, input$anchored_seqs)
        } else {
          rv$chr <-  c(input$anchored_seqs, rv$s2.1)
        }
      }
      
      updateCheckboxGroupInput(session, "anchored_seqs", NULL, 
                               choices = unique(rv$chr), selected = unique(rv$chr))
      updateTextAreaInput(session, "scaf_edit", NULL, 
                          value = paste(unique(rv$chr), collapse = "\n"))
    })
  })
  
  observe({
    chr <- input$anchored_seqs
    
    if(length(chr) > 1){
      disable("seq2"); disable_switch = TRUE;
    } else {
      enable("seq2"); disable_switch = FALSE;
    }
    
    if(length(chr) == 0){
      return(NULL)
    }
    
    leading_seq <- ifelse(input$dir2 == 'Backward', chr[1], chr[length(chr)])
    
    updatePickerInput(session, "seq2", selected = gsub("^[-+]","",leading_seq))
    shinyWidgets::updateSwitchInput(session, "strand_2", 
                                    value = substr(leading_seq, 1, 1) == "+", 
                                    disabled = disable_switch)
    updateNumericInput(session, "n", value = 1)
  })
  
  ##----------------------------------------------------------------------------
  ## exclude sequences ---------------------------------------------------------
  
  observeEvent(input$excludeSeq,{
    withBusyIndicatorServer("excludeSeq", {
      
      seq_list <- c(base::strsplit(input$excluded_seqs, "\n")[[1]], input$subseq2)
      
      strsplit("Super-Scaffold_102\nSuper-Scaffold_366", "\n")[[1]]
      
      updateTextAreaInput(session, "excluded_seqs", NULL, 
                          value = paste(unique(seq_list), collapse = "\n"))
    })
  })
  
  ##----------------------------------------------------------------------------
  ##----------------------------------------------------------------------------
  ## Erase the list 
  observeEvent(input$erase,{ 
    updateCheckboxGroupInput(session, "anchored_seqs", NULL, 
                             choices =  "", selected = "")
    updateTextAreaInput(session, "scaf_edit", value = "")
  })
  
  ## Copy to clipboard
  observeEvent(input$clipbtn, {
    clipr::write_clip(input$anchored_seqs, 
                      allow_non_interactive = TRUE)
  })
  
  ## Edit the list manually 
  observeEvent(input$edit,{ 
    shinyjs::show("EditBox")
    hide("CheckBox")
    hide("edit")
    shinyjs::show("confirm")
  })
  
  ## confirm the edits
  observeEvent(input$confirm,{ 
    choices <- base::strsplit(input$scaf_edit, "\n")[[1]]
    choices <- trimws(choices[choices != ""])
    
    updateCheckboxGroupInput(session, "anchored_seqs", NULL, 
                             choices =  choices, selected =  choices)
   
    
    shinyjs::hide("EditBox")
    shinyjs::show("CheckBox")
    shinyjs::show("edit")
    shinyjs::hide("confirm")
  })
  
  ## reverse the scaffold 
  observeEvent(input$reverse,{ 
    #choices <- base::rev(chartr("+-", "-+", input$anchored_seqs))
    choices <- input$anchored_seqs
    choices <- base::rev(ifelse(grepl("^-", choices), 
                                sub("^-", "+", choices), sub("^\\+", "-", choices)))
    
    updateCheckboxGroupInput(session, "anchored_seqs", NULL, 
                             choices =  choices, selected =  choices)
    updateTextAreaInput(session, "scaf_edit", 
                        value = paste(unique(choices), collapse = "\n"))
  })
  
  ## ---------------------------------------------------------------------------
  ## output groups to build fasta file using CreateScaffoldFasta.pl script
  observeEvent(input$export,{
    shiny::validate(need(input$anchored_seqs, ""))
    
    out_dir <- file.path(rv$projDir,"groups")
    dir.create(out_dir, showWarnings = FALSE)
    
    rv$new_scaffold <- data.table(rname = sub("^[-+]","", input$anchored_seqs),
                                  rc = ifelse(grepl("^\\+", input$anchored_seqs), 0, 1)) %>%
      .[rv$seq_len, on = "rname", nomatch=0] %>%
      .[, ':='(start = ifelse(grepl("fragment", rname), as.numeric(gsub(".*_(\\d+):.*", "\\1", rname)), 0),
               end = ifelse(grepl("fragment", rname), as.numeric(gsub(".*:(\\d+)", "\\1", rname)), rlen),
               contig = sub("_fragment.*", "", rname),
               id = seq_len(.N),
               name = "")] %>%
      .[, .(name, contig, fragment = rname, id, rc, start, end)]
    
    # Get next group number
    n <- length(list.files(out_dir))
    
    file = paste0(out_dir, "/group",n,".ordering")
    fwrite(rv$new_scaffold, file, quote = FALSE) 
  })
  
  observeEvent(input$export, {
    # Create fasta file
    hic_scaffolds <- list()
    
    for (s in unique(groups$name))
    {
      # init vars
      scaffold_seq <- NULL
      gap <- NULL
      gap_size <- 100
      
      # filter data
      data <- groups[groups$name == s,]
      
      for (i in 1:nrow(data))
      {
        contig_seq <- subseq(fasta[[data$contig[i]]], start = data$start[i], end = data$end[i])
        
        if(data$rc[i]){contig_seq <- reverseComplement(contig_seq)}
        
        scaffold_seq <- paste0(scaffold_seq, gap ,as.character(contig_seq))
        gap <- paste(rep("N", gap_size), collapse = '')
      }
      hic_scaffolds[[s]] <- scaffold_seq
    }
    
    hic_scaffolds <- DNAStringSet(sapply(hic_scaffolds, `[[`, 1))
    
    Biostrings::writeXStringSet(hic_scaffolds, "C:/Users/saeia/OneDrive - AgResearch/shared_with_andrew/T.repens_genome/hic_assembly.fasta")
  })

}
AliSaei/HiCSuite documentation built on June 15, 2024, 8:26 p.m.