# ============================================================================
# CLDedgelister - RStudio addin (Shiny gadget)
# ============================================================================

#' Import a model and convert it to an edge list (RStudio addin)
#'
#' Opens a small window to pick a Vensim, Stella, Powersim, AnyLogic or
#' GoldSim model, preview the resulting INPUT -> OUTPUT edge list, and
#' optionally save it as CSV or Excel. The chosen edge list is returned as a
#' data.frame when the gadget closes, so it can be captured with
#' \code{el <- cld_edgelist_addin()}.
#'
#' @return A data.frame of INPUT/OUTPUT edges (invisibly \code{NULL} if
#'   cancelled). When launched from the RStudio Addins menu the result is
#'   returned to the console.
#' @export
cld_edgelist_addin <- function() {
  for (pkg in c("shiny", "miniUI"))
    if (!requireNamespace(pkg, quietly = TRUE))
      stop("The addin needs the '", pkg, "' package. ",
           "install.packages('", pkg, "')")

  ui <- miniUI::miniPage(
    miniUI::gadgetTitleBar("SDM Edge List - model importer"),
    miniUI::miniContentPanel(
      shiny::fillCol(
        flex = c(NA, NA, NA, 1),
        shiny::selectInput("fmt", "Model format:", choices = cld_formats(),
                           width = "100%"),
        shiny::fileInput(
          "file", "Choose model file:", width = "100%",
          accept = c(".mdl", ".2mdl", ".stmx", ".itmx", ".xmile",
                     ".alp", ".xml")),
        shiny::fillRow(
          height = "40px",
          shiny::textOutput("status")),
        {
          if (requireNamespace("DT", quietly = TRUE))
            DT::dataTableOutput("preview")
          else
            shiny::tableOutput("preview")
        }
      )
    ),
    miniUI::miniButtonBlock(
      shiny::downloadButton("save_csv", "Save CSV"),
      shiny::downloadButton("save_xlsx", "Save Excel")
    )
  )

  server <- function(input, output, session) {
    rv <- shiny::reactiveValues(df = NULL, name = NULL)

    shiny::observeEvent(input$file, {
      shiny::req(input$file)
      res <- tryCatch(
        model_to_edgelist(input$file$datapath, input$fmt),
        error = function(e) {
          rv$df <- NULL
          output$status <- shiny::renderText(
            paste("Error:", conditionMessage(e)))
          NULL
        })
      if (!is.null(res)) {
        rv$df <- res
        rv$name <- tools::file_path_sans_ext(input$file$name)
        output$status <- shiny::renderText(
          sprintf("Imported '%s' - %d edges extracted.",
                  input$file$name, nrow(res)))
      }
    })

    output$preview <- if (requireNamespace("DT", quietly = TRUE)) {
      DT::renderDataTable({
        shiny::req(rv$df)
        DT::datatable(rv$df, rownames = FALSE,
                      options = list(pageLength = 15, scrollY = "300px"))
      })
    } else {
      shiny::renderTable({ shiny::req(rv$df); utils::head(rv$df, 50) })
    }

    output$save_csv <- shiny::downloadHandler(
      filename = function()
        paste0(if (is.null(rv$name)) "model" else rv$name, "_edgelist.csv"),
      content = function(file) {
        shiny::req(rv$df); utils::write.csv(rv$df, file, row.names = FALSE)
      })

    output$save_xlsx <- shiny::downloadHandler(
      filename = function()
        paste0(if (is.null(rv$name)) "model" else rv$name, "_edgelist.xlsx"),
      content = function(file) {
        shiny::req(rv$df)
        if (requireNamespace("writexl", quietly = TRUE))
          writexl::write_xlsx(rv$df, file)
        else
          utils::write.csv(rv$df, sub("xlsx$", "csv", file),
                           row.names = FALSE)
      })

    # Done -> return the edge list to the caller. The gadget's return value is
    # the edge list data.frame, so `el <- cld_edgelist_addin()` captures it.
    # (No assignment into the global environment: that is disallowed by CRAN
    # policy and unnecessary, since stopApp() already returns the result.)
    shiny::observeEvent(input$done, {
      shiny::stopApp(rv$df)
    })
    shiny::observeEvent(input$cancel, shiny::stopApp(invisible(NULL)))
  }

  viewer <- shiny::dialogViewer("SDM Edge List", width = 720, height = 640)
  shiny::runGadget(ui, server, viewer = viewer)
}
