#######################################
## Visualize SPR workflow and status ##
#######################################
plotWF <- function(sysargs,
                   width = NULL, height = NULL,
                   elementId = NULL,
                   responsive = TRUE,
                   branch_method = "auto",
                   branch_no = NULL,
                   layout = "compact",
                   no_plot = FALSE,
                   plot_method = "svg",
                   out_format = "plot",
                   out_path = NULL,
                   show_legend = TRUE,
                   mark_main_branch = FALSE,
                   rstudio = FALSE,
                   in_log = FALSE,
                   rmarkdown = "detect",
                   verbose = FALSE,
                   show_warns = FALSE,
                   plot_ctr = TRUE,
                   pan_zoom = FALSE,
                   exit_point = 0) {
    if (!is.null(width)) stopifnot(is.character(width) && length(width) == 1)
    if (!is.null(height)) stopifnot(is.character(height) && length(height) == 1)
    stopifnot(is.logical(responsive) && length(responsive) == 1)
    stopifnot(is.logical(rstudio) && length(rstudio) == 1)
    stopifnot(is.logical(show_warns) && length(show_warns) == 1)
    stopifnot(is.logical(plot_ctr) && length(plot_ctr) == 1)
    stopifnot(is.logical(pan_zoom) && length(pan_zoom) == 1)
    stopifnot(is.character(rmarkdown) || is.logical(rmarkdown) && length(rmarkdown) == 1)
    out_format <- match.arg(out_format, c("plot", "html", "dot", "dot_print"))
    if (!out_format %in% c("plot", "dot_print")) stopifnot(is.character(out_path) && length(out_path) == 1)
    plot_method <- match.arg(plot_method, c("svg", "png"))
    plot_method <- switch(plot_method,
        "svg" = "renderSVGElement",
        "png" = "renderImageElement"
    )
    msg <- "" # additional msg to display on plot
    if (verbose) message("Converting SYSargsList to df...")
    if (inherits(sysargs, "data.frame")) {
        df <- sysargs
        if (!all(col_names <- c(
            "step_name", "dep", "spr", "req", "session", "has_run", "success",
            "sample_pass", "sample_warn", "sample_error",
            "sample_total", "log_path", "time_start", "time_end"
        ) %in%
            names(df))) {
            stop("If sysargs is a dataframe, it must contain these columns:\n", paste(col_names, collapse = ", "))
        }
        if (nrow(df) < 1) stop("plotWF: empty dataframe")
    } else if (inherits(sysargs, "SYSargsList")) {
        df <- .buildDF(sysargs)
        if (nrow(df) == 1 && df$step_name[1] == "Empty_workflow") {
            show_legend <- FALSE
            branch_method <- "auto"
        }
    } else {
        stop("`sysargs` can only be a dataframe or a SYSargsList object")
    }
    if (verbose) message("Translating to DOT format...")
    dot_vector <- makeDot(
        df, branch_method, branch_no, layout, show_legend,
        mark_main_branch, in_log, verbose, exit_point, msg,
        show_warns
    )
    dot <- dot_vector[1]
    msg <- dot_vector[2]
    dot <- gsub(x = dot, "'", "\"")
    # if exit point
    if (exit_point > 0) {
        return(dot)
    }
    # if dot or dot_print
    if (out_format == "dot") {
        return(writeLines(dot, out_path))
    }
    if (out_format == "dot_print") {
        return(cat(dot))
    }
    # Decide if in Rmarkdown rendering
    if (is.character(rmarkdown) && rmarkdown != "detect") stop("rmarkdown can only be 'detect', TRUE or FALSE")
    if (rmarkdown == "detect") rmarkdown <- isTRUE(getOption("knitr.in.progress"))
    legend_uri <- if (rmarkdown && show_legend) .addDotLegendUri() else ""
    # forward options using x
    if (verbose) message("Making the plot...")
    if (pan_zoom && responsive) {
        warning("Pan-zoom and responsive cannot be used together. Pan-zoom has priority, now `responsive` has set to FALSE")
        responsive <- FALSE
    }
    x <- list(
        dot = dot,
        plotid = paste0("sprwf-", paste0(sample(8), collapse = "")),
        responsive = responsive,
        width = width,
        height = height,
        plot_method = plot_method,
        rmd = rmarkdown,
        msg = msg,
        plot_ctr = plot_ctr,
        pan_zoom = pan_zoom,
        legend_uri = legend_uri
    )
    # create widget
    grviz <- htmlwidgets::createWidget(
        name = "plotwf",
        x,
        width = width,
        height = height,
        package = "systemPipeR",
        elementId = elementId
    )
    # if html out
    if (out_format == "html") {
        return(htmlwidgets::saveWidget(widget = grviz, file = out_path, selfcontained = TRUE))
    }
    if (no_plot) {
        return(invisible(grviz))
    }
    # force to open browser tab instead of viewer in Rstudio
    if ((!rstudio || Sys.getenv("RSTUDIO") != "1") && !rmarkdown) {
        viewer <- getOption("viewer")
        on.exit(options(viewer = viewer), add = TRUE)
        options(viewer = NULL)
        return(print(grviz))
    } else {
        return(grviz)
    }
}

##################
## plotwfOutput ##
##################
plotwfOutput <- function(outputId, width = "100%", height = "400px") {
    htmlwidgets::shinyWidgetOutput(outputId, "plotwf", width, height, package = "systemPipeR")
}

# NOTE: To use `plotWF` in `renderPlotwf` in Shiny apps, always turn on the option `rstudio = TRUE`.

##################
## renderPlotwf ##
##################
renderPlotwf <- function(expr, env = parent.frame(), quoted = FALSE) {
    if (!quoted) {
        expr <- substitute(expr)
    } # force quoted
    htmlwidgets::shinyRenderWidget(expr, plotwfOutput, env, quoted = TRUE)
}

#############
## makeDot ##
#############
#' Translate a workflow structure and status into a dot language string----
makeDot <- function(df,
                    branch_method = "auto",
                    branch_no = NULL,
                    layout = "compact",
                    show_legend = TRUE,
                    mark_main_branch = TRUE,
                    in_log = FALSE,
                    verbose = FALSE,
                    exit_point = 0,
                    msg = "",
                    show_warns = TRUE) {
    # check
    stopifnot(is.logical(verbose) && length(verbose) == 1)
    stopifnot(is.logical(show_legend) && length(show_legend) == 1)
    if (verbose) message("Workflow inputs pre-checking ...")
    stopifnot(is.character(df$step_name))
    stopifnot(is.logical(in_log) && length(in_log) == 1)
    # early exit for linear method
    layout <- match.arg(layout, c("compact", "vertical", "horizontal", "execution"))
    if (layout == "execution") {
        return(.WFlinear(df, show_legend, in_log))
    }
    stopifnot(is.list(df$dep))
    lapply(seq_along(df$dep), function(i) {
        if (!is.character(df$dep[[i]])) stop("No.", i, " item in dep is not a character vector")
    })
    branch_method <- match.arg(branch_method, c("auto", "choose"))
    stopifnot(is.numeric(exit_point) && length(exit_point) == 1)
    # find all possible branches
    if (verbose) message("Looking for possible branches ...")
    step_names <- df$step_name
    deps <- df$dep
    if (sum(starting_root <- df$dep == "") > 1) {
        if (show_warns) {
            message(
                "More than 1 step has no dependency. They all will be treated as starting point:\n",
                paste0(names(df$dep)[starting_root], collapse = ", ")
            )
        }
        step_names <- c("root_step0", step_names)
        deps <- df$dep
        deps[starting_root] <- "root_step0"
        deps <- append(deps, list(root_step0 = ""), after = 0)
    }
    tree <- .findBranch(step_names, deps)
    if (sum(starting_root) > 1) tree <- lapply(tree, function(x) x[!x == "root_step0"])
    # debug exit point
    if (exit_point == 1) {
        return(tree)
    }
    if (branch_method == "choose" && interactive() && is.null(branch_no) || verbose) {
        # list all branches
        invisible(lapply(seq_along(tree), function(i) {
            cat(crayon::blue$bold("Possible branch"), i, ":\n")
            cat(paste0(tree[[i]], collapse = " -> "), "\n")
        }))
    }
    # choose branch
    if (!is.null(branch_no)) {
        stopifnot(is.numeric(branch_no) && length(branch_no) == 1)
        if (branch_no > length(tree)) stop("Branch number is larger than possible branches:", length(tree))
    } else if (branch_method == "choose" && interactive()) {
        branch_no <- as.numeric(menu(paste("Branch", seq_along(tree)), title = "Choose a main branch to plot workflow"))
    } else {
        branch_no <- .recommendBranch(tree, df$step_name, verbose, show_warns)
        branch_msg <- names(branch_no)[1]
        if (stringr::str_starts(branch_msg, "Workflow's first step is") && show_warns) {
            msg <- branch_msg
            # df <- df[df$step_name %in% tree[[branch_no]], ]
        }
    }
    if (verbose) message("Build the workflow tree ...")
    nodes <- .buildTree(tree, branch_no)
    if (exit_point == 2) {
        return(nodes)
    }
    # organize all node attach point
    node_attach <- unique(unlist(lapply(nodes, `[[`, "attach_point")))
    # organize all side branch
    node_side <- unique(unlist(lapply(nodes, `[[`, "branch_side")))
    # connecting the main branch
    branch_trans <- paste(tree[[branch_no]], collapse = " -> ")
    # translation
    trans_func <- switch(layout,
        "compact" = .WFcompact,
        "vertical" = .WFvert,
        "horizontal" = .WFhori
    )
    if (verbose) message("Translate tree  to DOT language...")
    # build the skeleton
    p_main <- trans_func(branch_trans, node_attach, node_side, mark_main_branch)
    if (exit_point == 3) {
        return(paste0(p_main, "\n}\n"))
    }
    # add node decoration
    p_main <- paste0(p_main, .addNodeDecor(
        df$step_name, df$status_summary, df$has_run, df$success, df$spr, df$sample_pass,
        df$sample_warn, df$sample_error, df$sample_total, df$log_path,
        df$time_start, df$time_end, df$req, df$session, in_log
    ))
    # add legend
    if (show_legend) p_main <- paste0(p_main, .addDotLegend(mark_main_branch), collapse = "\n")
    # close the plot
    # return plot and additional msg
    c(paste0(p_main, "\n}\n"), msg)
}

##################
## .findBranch ##
#################
.findBranch <- function(steps, deps, step_n = 1,
                        dep_chain = list(
                            root = steps[1]
                        ),
                        branch_name = "root") {
    if (step_n > length(steps)) {
        return(dep_chain)
    }
    current_step <- steps[step_n]
    # find steps depend on this step
    new_step_n <- lapply(deps, function(x) {
        current_step %in% x
    }) %>%
        unlist() %>%
        which()
    # cat(current_step, new_step_n, "\n")
    # make a backup if new branch need to be created
    if (length(new_step_n) > 1) deps_backup <- dep_chain[[branch_name]]
    # loop through these new steps to append dep chain
    for (i in seq_along(new_step_n)) {
        # if more than 1 dependent step, create a new branch
        if (i != 1) {
            # cat("branch step is: ", new_step_n[i], "\n")
            branch_name <- paste0(step_n, "_", new_step_n[i])
            dep_chain[[branch_name]] <- deps_backup
            # dep_chain[[as.character(chain_num)]][["branch_point"]] <- current_step
            # cat("chain no is: ", branch_name, "\n")
        }
        # cat("chain num is", chain_num, "\n")
        dep_chain[[branch_name]] <- c(dep_chain[[branch_name]], steps[new_step_n[i]])
        dep_chain <- .findBranch(steps, deps, new_step_n[i], dep_chain, branch_name)
    }
    dep_chain
}

######################
## .recommendBranch ##
######################
.recommendBranch <- function(tree, steps, verbose, show_warns) {
    branch_complete <- lapply(tree, function(x) {
        all(c(steps[1], steps[length(steps)]) %in% x)
    }) %>%
        unlist() %>%
        which()
    if (length(branch_complete) == 0) {
        msg <- "Workflow's first step is not connected to the last step, something wrong? This may cause workflow plot display issues"
        warning(msg)
        return(structure(c(1), .Names = msg))
    } else {
        if (verbose) cat("**********\n")
        if (verbose) {
            cat(
                "Find", length(branch_complete), "branch(es) connecting first and last step:",
                paste0(branch_complete, collapse = ", "),
                ".\n"
            )
        }
        tree_complete <- tree[branch_complete]
    }
    branch_len <- unlist(lapply(tree, length))
    branch_long <- which(branch_len == max(branch_len))
    if (verbose) cat("Find branch(es)", paste0(branch_long, collapse = ", "), ": with the largest number of steps", max(branch_len), "\n")
    branch_recommand <- base::intersect(branch_long, branch_complete)
    branch_recommand <- tree[branch_recommand %in% tree]
    # use first complete branch if no intersection
    if (length(branch_recommand) == 0) {
        branch_recommand <- branch_complete[1]
        # if still empty, use longest branch
        if (length(branch_recommand) == 0) {
            branch_recommand <- branch_long[1]
        }
    }
    if (verbose) cat("**********\n")
    if (verbose) cat("Based on the detection, branch(es):", paste0(branch_recommand, collapse = ", "), "is (are) recommended\n")
    if (verbose) cat("Branch", crayon::yellow$bold(branch_recommand[1]), "will be used as the main branch\n")
    return(branch_recommand[1])
}

################
## .buildTree ##
################
.buildTree <- function(tree, branch_no) {
    branch <- tree[[branch_no]]
    # reconstruct side branches based on the selected main branch
    lapply(tree[-branch_no], function(x) {
        # see if other branches has node in common with main branch
        in_main <- x %in% branch
        # find where these node need to attach to mian branch
        current_state <- in_main[1]
        attach_point <- c()
        for (i in seq_along(in_main)) {
            if (current_state != in_main[i]) {
                attach_point <- c(attach_point, paste(x[i - 1], "->", x[i]))
                current_state <- in_main[i]
            }
        }
        # find the chain of new side branches
        in_branch <- which(!in_main)
        branch_side <- list(x[in_branch[1]])
        list_pos <- 1
        for (i in seq_along(in_branch)) {
            if (i == 1) next
            if (in_branch[i] - in_branch[i - 1] == 1) {
                branch_side[[list_pos]] <- c(branch_side[[list_pos]], x[in_branch[i]])
            } else {
                list_pos <- list_pos + 1
                branch_side[[list_pos]] <- x[in_branch[i]]
            }
        }
        branch_side <- lapply(branch_side, function(branches) {
            nodes <- c()
            for (n in seq_along(branches)) {
                if (n >= length(branches)) next
                nodes <- c(nodes, paste(branches[n], branches[n + 1], sep = " -> "))
            }
            nodes
        })
        list(attach_point = attach_point, branch_side = branch_side)
    })
}

################
## .WFcompact ##
################
.WFcompact <- function(branch_trans, node_attach, node_side, mark_main_branch) {
    paste0(
        "digraph {
    node[fontsize=20];
    subgraph {\n    ",
        if (mark_main_branch) '    node[color="dodgerblue"];\n        ' else "    ",
        paste0(branch_trans, if (mark_main_branch) '[color="dodgerblue"]' else "", collapse = ""),
        "\n   }\n    ",
        paste0(node_attach, collapse = "\n    "),
        "\n    ",
        paste0(node_side, collapse = "\n    "),
        "\n"
    )
}

#############
## .WFvert ##
#############
.WFvert <- function(branch_trans, node_attach, node_side, mark_main_branch) {
    paste0(
        'digraph {
    node[fontsize=20];
    subgraph {
        rankdir="TB";\n        ',
        if (mark_main_branch) '    node[color="dodgerblue"];\n        ' else "    ",
        paste0(branch_trans, if (mark_main_branch) '[color="dodgerblue"]' else "", collapse = ""),
        "\n   }\n    ",
        paste0(
            "subgraph {\n",
            '        rank="same";\n',
            "        ",
            node_attach,
            "\n    }\n"
        ) %>%
            paste0(collapse = "\n    "),
        "\n    ",
        paste0(node_side, collapse = "\n    "),
        "\n"
    )
}

#############
## .WFhori ##
#############
.WFhori <- function(branch_trans, node_attach, node_side, mark_main_branch) {
    paste0(
        'digraph {
    node[fontsize=20];
    subgraph {
        rank="same";\n                ',
        if (mark_main_branch) '    node[color="dodgerblue"];\n        ' else "    ",
        paste0(branch_trans, if (mark_main_branch) '[color="dodgerblue"]' else "", collapse = ""),
        "\n   }\n    ",
        paste0(
            "subgraph {\n",
            '        rankdir="TB";\n',
            "        ",
            node_attach,
            "\n    }\n"
        ) %>%
            paste0(collapse = "\n    "),
        "\n    ",
        paste0(node_side, collapse = "\n    "),
        "\n"
    )
}

###############
## .WFlinear ##
###############
.WFlinear <- function(df, show_legend, in_log) {
    steps_trans <- paste0(df$step_name, collapse = " -> ")
    paste0(
        'digraph {
    node[fontsize=20];
    subgraph {
        rank="TB";\n        ',
        steps_trans,
        "\n   }\n",
        .addNodeDecor(
            df$step_name, df$status_summary, df$has_run, df$success, df$spr, df$sample_pass,
            df$sample_warn, df$sample_error, df$sample_total, df$log_path,
            df$time_start, df$time_end, df$req, df$session, in_log
        ),
        if (show_legend) .addDotLegend(FALSE),
        "\n}\n"
    )
}

###################
## .addDotLegend ##
###################
.addDotLegend <- function(show_main = TRUE) {
    paste0(
        '        subgraph cluster_legend {
        rankdir=TB;
        color="#eeeeee";
        style=filled;
        ranksep =1;
        label="Legends";
        fontsize = 30;
        node [style=filled, fontsize=10];
        legend_img-> step_state[color="#eeeeee"];

        legend_img[shape=none, image="plotwf_legend-src.png", label = " ", height=1, width=3, style=""];

        step_state[style="filled", shape="box" color=white, label =<
            <table>
            <tr><td><b>Step Colors</b></td></tr>
            <tr><td><font color="black">Pending steps</font>; <font color="#5cb85c">Successful steps</font>; <font color="#d9534f">Failed steps</font></td></tr>
            <tr><td><b>Targets Files / Code Chunk </b></td></tr><tr><td><font color="#5cb85c">0 (pass) </font> | <font color="#f0ad4e">0 (warning) </font> | <font color="#d9534f">0 (error) </font> | <font color="blue">0 (total)</font>; Duration</td></tr></table>
            >];

    }\n'
    )
}

######################
## .addDotLegendUri ##
######################
.addDotLegendUri <- function() {
    # to update the URI, run
    # paste0("data:image/svg+xml;base64,", base64enc::base64encode("inst/htmlwidgets/plotwf_legend.svg"))
    "data:image/svg+xml;base64,[encoded data, 23764 characters]"
}

###################
## .addNodeDecor ##
###################
.addNodeDecor <- function(steps, status_summary, has_run, success, spr, sample_pass, sample_warn,
                          sample_error, sample_total, log_path, time_start, time_end,
                          req, session, in_log = FALSE) {
    node_text <- c()
    for (i in seq_along(steps)) {
        step_color <- switch(status_summary[i],
            "Success" = "#5cb85c",
            "Warning" = "#f0ad4e",
            "Error" = "#d9534f",
            "black"
        )
        duration <- .stepDuration(steps[i], time_start[i], time_end[i])
        node_text <- c(node_text, paste0(
            "    ", steps[i], "[",
            if (req[i] == "mandatory") 'fillcolor="#d3d6eb" ' else "",
            if (req[i] == "mandatory" && session[i] == "compute") {
                'style="filled, dashed, '
            } else if (req[i] == "mandatory" && session[i] != "compute") {
                'style="filled, '
            } else if (req[i] != "mandatory" && session[i] == "compute") {
                'style="dashed, '
            } else {
                'style="solid, '
            },
            if (spr[i] == "sysargs") 'rounded" ' else '"',
            "label=<<b>",
            '<font color="', step_color, '">', steps[i], "</font><br></br>",
            '<font color="#5cb85c">', sample_pass[i], "</font>/",
            '<font color="#f0ad4e">', sample_warn[i], "</font>/",
            '<font color="#d9534f">', sample_error[i], "</font>/",
            '<font color="blue">', sample_total[i], "</font></b>; ",
            '<font color="black">', duration$short, "</font>",
            "> ",
            if (spr[i] == "sysargs") ', shape="box" ' else "",
            if (in_log) paste0('href="', log_path[i], '" ') else " ",
            'tooltip="step ', steps[i], ": ",
            sample_pass[i], " samples passed; ",
            sample_warn[i], " samples have warnings; ",
            sample_error[i], " samples have errors; ",
            sample_total[i], " samples in total; ",
            "Start time: ", time_start[i], "; ",
            "End time: ", time_end[i], "; ",
            "Duration: ", duration$long,
            '"',
            "]\n"
        ))
    }
    paste0(node_text, collapse = "")
}

###################
## .stepDuration ##
###################
.stepDuration <- function(step, start, end) {
    duration <- round(as.numeric(difftime(end, start, units = "sec")), 1)
    if (duration < 0) {
        duration <- 0
        warning(
            "In step: ", step, "\n",
            "Starting time: ", start, "\n",
            "Ending time: ", end, "\n",
            "Ending time is before starting time, something wrong?\n",
            "Duration of this step is treated as 0 for now.",
            immediate. = TRUE,
            call. = FALSE
        )
    }
    secs <- duration %% 60
    mins_raw <- duration %/% 60
    mins <- mins_raw %% 60
    hrs <- mins_raw %/% 60
    return(list(
        long = paste(stringr::str_pad(hrs, 2, pad = "0"), stringr::str_pad(mins, 2, pad = "0"), stringr::str_pad(secs, 2, pad = "0"), sep = ":"),
        short = if (hrs > 0) paste0(hrs, "h") else if (mins > 0) paste0(mins, "m") else paste0(secs, "s")
    ))
}

###################
## .buildDF ##
###################
.buildDF <- function(sal) {
    sal_temp <- sal
    if (length(sal_temp) == 0) {
        warning("Workflow has no steps. Please make sure to add a step to the workflow before plotting.", call. = FALSE)
        return_df <- data.frame(
            step_name = "Empty_workflow",
            dep = NA,
            spr = "sysargs",
            req = "mandatory",
            session = "management",
            has_run = FALSE,
            success = FALSE,
            sample_pass = 0,
            sample_warn = 0,
            sample_error = 0,
            sample_total = 0,
            log_path = "",
            time_start = Sys.time(),
            time_end = Sys.time()
        )
        return_df$dep <- list("")
        return(return_df)
    }
    df <- data.frame(step_name = stepName(sal_temp))
    dep <- dependency(sal_temp)
    for (i in seq_along(dep)) {
        if (any(is.na(dep[i]))) {
            dep[[i]] <- ""
        }
    }
    df$dep <- dep
    df$spr <- ifelse(sapply(df$step_name, function(x) inherits(stepsWF(sal_temp)[[x]], "SYSargs2")), "sysargs", "r")
    df$req <- sapply(df$step_name, function(x) sal_temp$runInfo$runOption[[x]]$run_step)
    df$session <- sapply(df$step_name, function(x) sal_temp$runInfo$runOption[[x]]$run_session)
    df$status_summary <- sapply(df$step_name, function(x) sal_temp$statusWF[[x]]$status.summary)
    df$has_run <- ifelse(!sapply(df$step_name, function(x) sal_temp$statusWF[[x]]$status.summary) == "Pending", TRUE, FALSE)
    df$success <- ifelse(sapply(df$step_name, function(x) sal_temp$statusWF[[x]]$status.summary) == "Success", TRUE, FALSE)
    df <- cbind(df, data.frame(
        sample_pass = 0,
        sample_warn = 0,
        sample_error = 0,
        sample_total = 0,
        time_start = Sys.time(),
        time_end = Sys.time()
    ))
    for (i in seq_along(df$step_name)) {
        if (inherits(stepsWF(sal_temp)[[i]], "SYSargs2")) {
            sample_df <- as.list(colSums(sal_temp$statusWF[[i]][[2]][2:4]))
            df$sample_pass[i] <- sample_df$Existing_Files
            df$sample_total[i] <- sample_df$Total_Files
            if (all(sample_df$Missing_Files > 0 && sal_temp$statusWF[[i]]$status.summary == "Warning")) {
                df$sample_warn[i] <- sample_df$Missing_Files
            } else if (all(sample_df$Missing_Files > 0 && sal_temp$statusWF[[i]]$status.summary == "Error")) {
                df$sample_error[i] <- sample_df$Missing_Files
            }
            if (!is.null(sal_temp$statusWF[[i]]$total.time)) {
                df$time_start[i] <- sal_temp$statusWF[[i]]$total.time$time_start
                df$time_end[i] <- sal_temp$statusWF[[i]]$total.time$time_end
            }
        } else if (inherits(stepsWF(sal_temp)[[i]], "LineWise")) {
            df$sample_total[i] <- 1
            if (sal_temp$statusWF[[i]]$status.summary == "Success") {
                df$sample_pass[i] <- 1
            } else if (sal_temp$statusWF[[i]]$status.summary == "Warning") {
                df$sample_warn[i] <- 1
            } else if (sal_temp$statusWF[[i]]$status.summary == "Error") {
                df$sample_error[i] <- 1
            }
            if (length(sal_temp$statusWF[[i]]$status.time) > 0) {
                df$time_start[i] <- sal_temp$statusWF[[i]]$total.time$time_start
                df$time_end[i] <- sal_temp$statusWF[[i]]$total.time$time_end
            }
        }
    }
    df$log_path <- paste0("#", tolower(df$step_name))
    df <- rbind(df[which(df$dep == ""), ], df[which(!df$dep == ""), ])
    return(df)
}
