inst/biblioshiny/utils_old.R

### COMMON FUNCTIONS ####

# Number format abbreviated
format_abbreviated <- function(x) {
  if (is.na(x)) {
    return("--")
  }
  if (x >= 1e6) {
    return(paste0(format(round(x / 1e6, 2), nsmall = 2), "M"))
  } else if (x >= 1e3) {
    return(paste0(format(round(x / 1e3, 0), nsmall = 0), "K"))
  } else {
    return(as.character(x))
  }
}

# total package download
total_downloads <- function(
  pkg_name = "bibliometrix",
  from = "2016-01-01",
  to = Sys.Date()
) {
  # Function to get total downloads of a package from CRAN logs
  # Args:
  #   pkg_name: Name of the package as a string
  # Returns:
  #   Total number of downloads as an integer

  if (!is_Online()) {
    return(NA)
  }

  #today <- Sys.Date()
  if (!is.character(pkg_name) || length(pkg_name) != 1) {
    stop("pkg_name must be a single string.")
  }

  url <- paste0(
    "https://cranlogs.r-pkg.org/downloads/total/",
    from,
    ":",
    to,
    "/",
    pkg_name
  )

  if (!is_Online(timeout = 1, url)) {
    return(NA)
  }

  json_text <- tryCatch(
    {
      readLines(url, warn = FALSE)
    },
    error = function(e) {
      return(NA)
    }
  )

  # Se già nel tryCatch è tornato "NA", esci subito
  if (identical(json_text, "NA")) {
    return(NA)
  }

  # Extract the number manually (not robust)
  txt <- unlist(strsplit(json_text, ","))
  txt <- txt[grepl("downloads", txt)]

  if (length(txt) == 0) {
    return(NA)
  }

  downloads <- gsub("[^0-9]", "", txt)

  return(as.integer(downloads))
}

# FILTER FUNCTIONS ----
read_journal_ranking <- function(file_path) {
  ext <- tools::file_ext(file_path)

  suppressMessages(
    journals <- switch(
      tolower(ext),
      "csv" = read.csv(file_path, header = TRUE, stringsAsFactors = FALSE),
      "xlsx" = {
        readxl::read_excel(file_path, col_names = TRUE)
      },
      stop(
        "Unsupported file format. Please upload a .csv, .txt, or .xlsx file."
      )
    )
  )
  journals <- journals %>% select(1, 2)
  # journals <- journals[!is.na(journals)]
  # journals <- toupper(trimws(journals))
  names(journals) <- c("SO", "Ranking")
  journals <- journals %>%
    mutate(SO = toupper(trimws(SO)))
  return(journals)
}

read_journal_list <- function(file_path) {
  ext <- tools::file_ext(file_path)

  suppressMessages(
    journals <- switch(
      tolower(ext),
      "csv" = read.csv(file_path, header = FALSE, stringsAsFactors = FALSE)[[
        1
      ]],
      "txt" = readLines(file_path, warn = FALSE),
      "xlsx" = {
        readxl::read_excel(file_path, col_names = FALSE)[[1]]
      },
      stop(
        "Unsupported file format. Please upload a .csv, .txt, or .xlsx file."
      )
    )
  )

  journals <- journals[!is.na(journals)]
  journals <- toupper(trimws(journals))
  return(journals)
}

wcTable <- function(M) {
  # Function to extract Science Category (WC) information from metadata
  if ("WC" %in% names(M)) {
    WC <- strsplit(M$WC, ";")
    df <- data.frame(
      SR = rep(M$SR, lengths(WC)),
      WC = unlist(WC),
      stringsAsFactors = FALSE
    )

    df$WC <- trimws(df$WC) # Remove leading and trailing whitespace
  } else {
    df <- data.frame(SR = M$SR, WC = "N.A.", stringsAsFactors = FALSE)
  }

  return(df)
}

countryTable <- function(M) {
  data("countries", envir = environment())
  # Function to extract country information from metadata
  if (!("AU_CO" %in% names(M))) {
    M <- metaTagExtraction(M, "AU_CO")
  }

  CO <- strsplit(M$AU_CO, ";")
  df <- data.frame(
    SR = rep(M$SR, lengths(CO)),
    CO = trimws(unlist(CO)),
    stringsAsFactors = FALSE
  )

  df$CO <- gsub("[[:digit:]]", "", df$CO)
  df$CO <- gsub(".", "", df$CO, fixed = TRUE)
  df$CO <- gsub(";;", ";", df$CO, fixed = TRUE)
  df$CO <- gsub("UNITED STATES", "USA", df$CO)
  df$CO <- gsub("RUSSIAN FEDERATION", "RUSSIA", df$CO)
  df$CO <- gsub("TAIWAN", "CHINA", df$CO)
  df$CO <- gsub("ENGLAND", "UNITED KINGDOM", df$CO)
  df$CO <- gsub("SCOTLAND", "UNITED KINGDOM", df$CO)
  df$CO <- gsub("WALES", "UNITED KINGDOM", df$CO)
  df$CO <- gsub("NORTH IRELAND", "UNITED KINGDOM", df$CO)
  df$CO <- gsub("UK", "UNITED KINGDOM", df$CO)
  #df$CO <- gsub("KOREA", "SOUTH KOREA", df$CO)

  df <- df %>%
    left_join(
      countries %>% select(countries, continent),
      by = c("CO" = "countries")
    ) %>%
    mutate(CO = ifelse(is.na(CO), "Unknown", CO)) %>%
    mutate(continent = ifelse(is.na(continent), "Unknown", continent)) %>%
    mutate(CO = ifelse(CO == "UNKNOWN", "Unknown", CO))
}

# LOAD FUNCTIONS -----

formatDB <- function(obj) {
  ext <- sub(".*\\.", "", obj[1])
  switch(
    ext,
    txt = {
      format <- "plaintext"
    },
    csv = {
      format <- "csv"
    },
    bib = {
      format <- "bibtex"
    },
    ciw = {
      format <- "endnote"
    },
    xlsx = {
      format <- "excel"
    }
  )
  return(format)
}

## smart_load function ----
smart_load <- function(file) {
  var <- load(file)
  n <- length(var)
  if (!"M" %in% var) {
    if (n == 1) {
      eval(parse(text = paste0("M <- ", var)))
    } else {
      stop("I could not find bibliometrixDB object in your data file: ", file)
    }
  }
  rm(list = var[var != "M"])
  if (("M" %in% ls()) & inherits(M, "bibliometrixDB")) {
    return(M)
  } else {
    stop(
      "Please make sure your RData/Rda file contains a bibliometrixDB object (M)."
    )
  }
}


## merge collections ----
merge_files <- function(files) {
  ## load xlsx or rdata bibliometrix files
  if ("datapath" %in% names(files)) {
    file <- files$datapath
    ext <- unlist(lapply(file, getFileNameExtension))
  }

  Mfile <- list()
  n <- 0
  for (i in 1:length(file)) {
    extF <- ext[i]
    filename <- file[i]

    switch(
      tolower(extF),
      xlsx = {
        Mfile[[i]] <- readxl::read_excel(filename, col_types = "text") %>%
          as.data.frame()
        Mfile[[i]]$PY <- as.numeric(Mfile[[i]]$PY)
        Mfile[[i]]$TC <- as.numeric(Mfile[[i]]$TC)
      },
      rdata = {
        Mfile[[i]] <- smart_load(filename)
      }
    )
    n <- n + nrow(Mfile[[i]])
  }

  # merge bibliometrix files
  M <- mergeDbSources(Mfile, remove.duplicated = T)

  # save original size as attribute
  attr(M, "nMerge") <- n

  return(M)
}

## dynamic watch emoji icons ---
watchEmoji <- function(i) {
  emoji <- c(
    "🕐",
    "🕑",
    "🕒",
    "🕓",
    "🕔",
    "🕕",
    "🕖",
    "🕗",
    "🕘",
    "🕙",
    "🕚",
    "🕛"
  )
  # i is a positive int number, reduce it to an int from 1 to 12
  multiple <- floor(i / 12)
  if (multiple > 0) {
    i <- i %% (12 * multiple)
    if (i == 0) {
      i <- 12
    }
  }
  emoji[i]
}

## RESET MODAL DIALOG INPUTS
resetModalButtons <- function(session) {
  session$sendCustomMessage("button_id", "null")
  session$sendCustomMessage("button_id2", "null")
  # session$sendCustomMessage("click", "null")
  # session$sendCustomMessage("click-dend", "null")
  # runjs("Shiny.setInputValue('plotly_click-A', null);")
  return(session)
}


# DATA TABLE FORMAT ----
DTformat <- function(
  df,
  nrow = 10,
  filename = "Table",
  pagelength = TRUE,
  left = NULL,
  right = NULL,
  numeric = NULL,
  dom = TRUE,
  size = "85%",
  filter = "top",
  columnShort = NULL,
  columnSmall = NULL,
  round = 2,
  title = "",
  button = FALSE,
  escape = FALSE,
  selection = FALSE,
  scrollX = FALSE,
  scrollY = FALSE,
  summary = FALSE
) {
  if ("text" %in% names(df)) {
    df <- df %>%
      mutate(text = gsub("<|>", "", text))
  }

  if (length(columnShort) > 0) {
    columnDefs <- list(
      list(
        className = "dt-center",
        targets = 0:(length(names(df)) - 1)
      ),
      list(
        targets = columnShort - 1,
        render = JS(
          "function(data, type, row, meta) {",
          "return type === 'display' && data.length > 500 ?",
          "'<span title=\"' + data + '\">' + data.substr(0, 500) + '...</span>' : data;",
          "}"
        )
      )
    )
  } else {
    columnDefs <- list(list(
      className = "dt-center",
      targets = 0:(length(names(df)) - 1)
    ))
  }

  initComplete <- NULL
  # Summary Button
  if (summary == "documents" & "Paper" %in% names(df)) {
    df <- df %>%
      mutate(
        Summary = paste0(
          '<div style="display: flex; justify-content: center; align-items: center; height: 100%; width: 100%;">',
          '<button id="custom_btn" style="',
          'width: 24px; height: 24px; ',
          'border-radius: 50%; ',
          'border: none; ',
          'background: linear-gradient(135deg, #4285f4 0%, #1976d2 100%); ',
          'color: white; ',
          'cursor: pointer; ',
          'display: flex; ',
          'align-items: center; ',
          'justify-content: center; ',
          'box-shadow: 0 2px 4px rgba(0,0,0,0.2); ',
          'transition: all 0.3s ease; ',
          '" ',
          'onmouseover="this.style.transform=\'scale(1.1)\'; this.style.boxShadow=\'0 4px 8px rgba(0,0,0,0.3)\';" ',
          'onmouseout="this.style.transform=\'scale(1)\'; this.style.boxShadow=\'0 2px 4px rgba(0,0,0,0.2)\';" ',
          'onclick="Shiny.onInputChange(\'button_id\', \'',
          Paper,
          '\')">',
          '<i class="fas fa-search-plus" style="font-size: 14px;"></i>',
          '</button>',
          '</div>'
        )
      ) %>%
      select(Summary, everything())
  } else if (summary == "historiograph" & "Paper" %in% names(df)) {
    df <- df %>%
      mutate(
        Summary = paste0(
          '<div style="display: flex; justify-content: center; align-items: center; height: 100%; width: 100%;">',
          '<button id="custom_btn" style="',
          'width: 32px; height: 32px; ',
          'border-radius: 50%; ',
          'border: none; ',
          'background: linear-gradient(135deg, #4285f4 0%, #1976d2 100%); ',
          'color: white; ',
          'cursor: pointer; ',
          'display: flex; ',
          'align-items: center; ',
          'justify-content: center; ',
          'box-shadow: 0 2px 4px rgba(0,0,0,0.2); ',
          'transition: all 0.3s ease; ',
          '" ',
          'onmouseover="this.style.transform=\'scale(1.1)\'; this.style.boxShadow=\'0 4px 8px rgba(0,0,0,0.3)\';" ',
          'onmouseout="this.style.transform=\'scale(1)\'; this.style.boxShadow=\'0 2px 4px rgba(0,0,0,0.2)\';" ',
          'onclick="Shiny.onInputChange(\'button_id\', \'',
          SR,
          '\')">',
          '<i class="fas fa-search-plus" style="font-size: 14px;"></i>',
          '</button>',
          '</div>'
        )
      ) %>%
      select(Summary, everything()) %>%
      select(-SR)
  } else if (summary == "authors" & "Author" %in% names(df)) {
    df <- df %>%
      # mutate(Bio = paste0('<button id="custom_btn2" onclick="Shiny.onInputChange(\'button_id2\', \'', Author, '\')">▶️</button>')) %>%
      # select(Bio, everything()) %>%
      mutate(
        Author = paste0(
          '<span class="author-link" onclick="show_author_modal(\'',
          gsub("'", "\\\\'", Author),
          '\')">',
          Author,
          '</span>'
        )
      )
    initComplete = JS(
      "function(settings, json) {",
      "  window.show_author_modal = function(author) {",
      "    Shiny.setInputValue('selected_author', author, {priority: 'event'});",
      "  };",
      "}"
    )
    escape = FALSE
  }

  if (isTRUE(button)) {
    if (isTRUE(pagelength)) {
      buttons <- list(
        list(extend = "pageLength"),
        list(
          extend = "excel",
          filename = paste0(filename, "_bibliometrix_", Sys.Date()),
          title = " ",
          header = TRUE,
          exportOptions = list(
            modifier = list(page = "all")
          )
        )
      )
    } else {
      buttons <- list(
        list(
          extend = "excel",
          filename = paste0(filename, "_bibliometrix_", Sys.Date()),
          title = " ",
          header = TRUE,
          exportOptions = list(
            modifier = list(page = "all")
          )
        )
      )
    }
  } else {
    buttons <- list(list(extend = "pageLength"))
  }

  if (isTRUE(dom)) {
    dom <- "Brtip"
  } else if (dom == FALSE) {
    dom <- "Bftp"
  } else {
    dom <- "t"
  }

  if (nchar(title) > 0) {
    caption <- htmltools::tags$caption(
      style = "caption-side: top; text-align: center; color:black;  font-size:140% ;",
      title
    )
  } else {
    caption <- htmltools::tags$caption(
      style = "caption-side: top; text-align: center; color:black;  font-size:140% ;",
      ""
    )
  }

  if (isTRUE(selection)) {
    extensions <- c("Buttons", "Select", "ColReorder", "FixedHeader")
    buttons <- c(buttons, c("selectAll", "selectNone"))
    select <- list(style = "multiple", items = "row", selected = 1:nrow(df))
    # selection = list(mode = 'multiple', selected = 1:nrow(df), target = 'row')
  } else {
    extensions <- c("Buttons", "ColReorder", "FixedHeader")
    select <- NULL
    # selection = "none"
  }

  tab <- DT::datatable(
    df,
    escape = escape,
    rownames = FALSE,
    caption = caption,
    selection = "none",
    extensions = extensions,
    filter = filter,
    options = list(
      headerCallback = DT::JS(
        "function(thead) {",
        "  $(thead).css('font-size', '1em');",
        "}"
      ),
      initComplete = initComplete,
      colReorder = TRUE,
      fixedHeader = TRUE,
      pageLength = nrow,
      autoWidth = TRUE,
      scrollX = scrollX,
      scrollY = scrollY,
      dom = dom,
      buttons = buttons,
      select = select,
      lengthMenu = list(
        c(10, 25, 50, -1),
        c("10 rows", "25 rows", "50 rows", "Show all")
      ),
      columnDefs = columnDefs
    ),
    class = "cell-border compact stripe"
  ) %>%
    DT::formatStyle(
      names(df),
      backgroundColor = "white",
      textAlign = "center",
      fontSize = size
    )

  ## left aligning

  if (!is.null(left)) {
    tab <- tab %>%
      DT::formatStyle(
        names(df)[left],
        backgroundColor = "white",
        textAlign = "left",
        fontSize = size
      )
  }

  # right aligning
  if (!is.null(right)) {
    tab <- tab %>%
      DT::formatStyle(
        names(df)[right],
        backgroundColor = "white",
        textAlign = "right",
        fontSize = size
      )
  }

  # numeric round
  if (!is.null(numeric)) {
    tab <- tab %>%
      formatRound(names(df)[c(numeric)], digits = round)
  }

  tab
}


authorNameFormat <- function(M, format) {
  if (format == "AF" & "AF" %in% names(M)) {
    M <- M %>%
      rename(
        AU_IN = AU,
        AU = AF
      )
  }
  return(M)
}

split_text_numbers <- function(input_str, UT) {
  # Split the string into components based on "; "
  components <- unlist(strsplit(input_str, "; ", fixed = TRUE))

  # Initialize two vectors to store the separated parts
  texts <- character(length(components))
  numbers <- numeric(length(components))

  # Iterate through each component to separate text and numbers
  for (i in seq_along(components)) {
    # Extract the text using regex, matching everything up to " ("
    texts[i] <- gsub("\\s\\(.*$", "", components[i])

    # Extract the numbers using regex, matching digits inside parentheses
    numbers[i] <- as.numeric(gsub(".*\\((\\d+)\\).*", "\\1", components[i]))
  }

  # Return a list with texts and numbers separated
  data.frame(Texts = texts, Numbers = numbers, UT = UT)
}


AuthorNameMerge <- function(M) {
  df_list <- list()
  for (i in 1:nrow(M)) {
    if (nchar(M$AU[i]) > 0) {
      df_list[[i]] <- split_text_numbers(M$AU[i], M$UT[i])
    }
  }

  df <- do.call(rbind, df_list)

  AU <- df %>%
    group_by(Numbers, Texts) %>%
    count() %>%
    group_by(Numbers) %>%
    arrange(desc(n)) %>%
    mutate(AU = Texts[1]) %>%
    select(-"n", -"Texts") %>%
    ungroup() %>%
    distinct()

  df <- df %>%
    left_join(AU, by = "Numbers") %>%
    group_by(UT) %>%
    summarize(
      AU = paste0(AU, collapse = ";"),
      AU_ID = paste0(Numbers, collapse = ";")
    )

  M <- M %>%
    rename(AU_original = AU) %>%
    left_join(df, by = "UT")
  return(M)
}

getFileNameExtension <- function(fn) {
  # remove a path
  splitted <- strsplit(x = fn, split = "/")[[1]]
  # or use .Platform$file.sep in stead of '/'
  fn <- splitted[length(splitted)]
  ext <- ""
  splitted <- strsplit(x = fn, split = "\\.")[[1]]
  l <- length(splitted)
  if (l > 1 && sum(splitted[1:(l - 1)] != "")) {
    ext <- splitted[l]
  }
  # the extention must be the suffix of a non-empty name
  ext
}

# Initial to upper case
firstup <- function(x) {
  x <- tolower(x)
  substr(x, 1, 1) <- toupper(substr(x, 1, 1))
  x
}


# string preview (stopwords)
strPreview <- function(string, sep = ",") {
  str1 <- unlist(strsplit(string, sep))
  str1 <- str1[1:min(c(length(str1), 5))]
  str1 <- paste(str1, collapse = sep)
  HTML(paste("<pre>", "File Preview: ", str1, "</pre>", sep = "<br/>"))
}

# string preview (synonyms)
strSynPreview <- function(string) {
  string <- string[1]
  str1 <- unlist(strsplit(string, ";"))
  str1 <- str1[1:min(c(length(str1), 5))]
  str1 <- paste(
    paste(str1[1], " <- ", collapse = ""),
    paste(str1[-1], collapse = ";"),
    collapse = ""
  )
  HTML(paste("<pre>", "File Preview: ", str1, "</pre>", sep = "<br/>"))
}


### LIFE CYCLE PLOTLY FUNCTION ----

#' Plot Life Cycle Analysis Results with ggplot2
#'
#' Creates ggplot2 plots from lifeCycle analysis results
#'
#' @param results Output from lifeCycle() function
#' @param plot_type Character, either "annual" or "cumulative" to specify which plot to generate
#'
#' @return A ggplot2 object
#'
#' @export
ggplotLifeCycle <- function(results, plot_type = c("annual", "cumulative")) {
  if (!requireNamespace("ggplot2", quietly = TRUE)) {
    stop("Package 'ggplot2' is required but not installed.")
  }

  plot_type <- match.arg(plot_type)

  # Extract data
  complete_curve <- results$complete_curve
  observed_data <- results$data
  params <- results$parameters_real_years
  metrics <- results$metrics
  K <- params$K
  tm_year <- params$tm_year
  R2 <- metrics$R_squared

  if (plot_type == "annual") {
    # Plot 1: Annual Publications
    max_annual <- max(complete_curve$annual, na.rm = TRUE)

    p <- ggplot() +
      geom_line(
        data = complete_curve,
        aes(x = year, y = annual),
        color = "blue",
        linewidth = 1
      ) +
      geom_point(
        data = observed_data,
        aes(x = PY, y = n),
        color = "blue",
        size = 3
      ) +
      geom_vline(
        xintercept = tm_year,
        linetype = "dashed",
        color = "red",
        linewidth = 0.7
      ) +
      annotate(
        "text",
        x = tm_year,
        y = max_annual * 0.95,
        label = sprintf("Peak: %.1f", tm_year),
        hjust = -0.1,
        color = "red",
        size = 3.5
      ) +
      annotate(
        "text",
        x = max(complete_curve$year),
        y = max_annual * 1.05,
        label = sprintf("R² = %.3f", R2),
        hjust = 1,
        size = 4
      ) +
      labs(
        title = "Life Cycle - Annual Publications",
        x = "Year",
        y = "Publications (Annual)"
      ) +
      scale_y_continuous(limits = c(0, max_annual * 1.1)) +
      theme_minimal(base_size = 12) +
      theme(
        plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
        panel.grid.minor = element_blank()
      )

    return(p)
  } else if (plot_type == "cumulative") {
    # Plot 2: Cumulative Publications
    reference_lines <- data.frame(
      y = c(K * 0.5, K * 0.9, K * 0.99),
      label = c("50%", "90%", "99%")
    )

    p <- ggplot() +
      geom_line(
        data = complete_curve,
        aes(x = year, y = cumulative),
        color = "darkgreen",
        linewidth = 1
      ) +
      geom_point(
        data = observed_data,
        aes(x = PY, y = cumulative),
        color = "darkgreen",
        size = 3
      ) +
      geom_hline(
        data = reference_lines,
        aes(yintercept = y),
        linetype = "dotted",
        color = "gray50",
        linewidth = 0.5
      ) +
      geom_text(
        data = reference_lines,
        aes(x = min(complete_curve$year), y = y, label = label),
        hjust = 0,
        vjust = -0.5,
        size = 3,
        color = "gray50"
      ) +
      labs(
        title = "Cumulative Growth Curve",
        x = "Year",
        y = "Cumulative Publications"
      ) +
      scale_y_continuous(limits = c(0, K * 1.05)) +
      theme_minimal(base_size = 12) +
      theme(
        plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
        panel.grid.minor = element_blank()
      )

    return(p)
  }
}

#' Plot Life Cycle Analysis with Plotly
#'
#' Creates interactive plotly visualizations from lifeCycle results
#' for use in biblioshiny
#'
#' @param results Output from lifeCycle() function
#' @param plot_type Character: "annual" or "cumulative" (default: "annual")
#'
#' @return A plotly object
#'
plotLifeCycle <- function(results, plot_type = c("annual", "cumulative")) {
  if (!requireNamespace("plotly", quietly = TRUE)) {
    stop(
      "Package 'plotly' is required. Please install it with: install.packages('plotly')"
    )
  }

  plot_type <- match.arg(plot_type)

  # Extract data
  df <- results$data
  complete <- results$complete_curve
  params <- results$parameters_real_years
  metrics <- results$metrics

  # Separate observed and forecast
  last_obs_year <- max(df$PY)
  observed <- complete[complete$year <= last_obs_year, ]
  forecast <- complete[complete$year > last_obs_year, ]

  if (plot_type == "annual") {
    # === ANNUAL PUBLICATIONS PLOT ===

    p <- plotly::plot_ly()

    # Observed curve (historical fit)
    p <- p %>%
      plotly::add_trace(
        data = observed,
        x = ~year,
        y = ~annual,
        type = 'scatter',
        mode = 'lines',
        name = 'Logistic fit',
        line = list(color = '#1f77b4', width = 2),
        hovertemplate = paste0(
          '<b>Year:</b> %{x}<br>',
          '<b>Annual:</b> %{y:.0f}<br>',
          '<extra></extra>'
        )
      )

    # Forecast curve
    if (nrow(forecast) > 0) {
      p <- p %>%
        plotly::add_trace(
          data = forecast,
          x = ~year,
          y = ~annual,
          type = 'scatter',
          mode = 'lines',
          name = 'Forecast',
          line = list(color = '#1f77b4', width = 2, dash = 'dash'),
          hovertemplate = paste0(
            '<b>Year:</b> %{x}<br>',
            '<b>Projected:</b> %{y:.0f}<br>',
            '<extra></extra>'
          )
        )
    }

    # Observed data points
    p <- p %>%
      plotly::add_trace(
        data = df,
        x = ~PY,
        y = ~n,
        type = 'scatter',
        mode = 'markers',
        name = 'Observed',
        marker = list(color = '#1f77b4', size = 8),
        hovertemplate = paste0(
          '<b>Year:</b> %{x}<br>',
          '<b>Publications:</b> %{y}<br>',
          '<extra></extra>'
        )
      )

    # Peak year line
    p <- p %>%
      plotly::add_trace(
        x = c(params$tm_year, params$tm_year),
        y = c(0, max(complete$annual) * 1.1),
        type = 'scatter',
        mode = 'lines',
        name = paste0('Peak year (', round(params$tm_year, 1), ')'),
        line = list(color = 'red', width = 1.5, dash = 'dash'),
        hoverinfo = 'name'
      )

    # Layout
    p <- p %>%
      plotly::layout(
        title = list(
          text = sprintf(
            "Life Cycle - Annual Publications<br><sup>R² = %.3f | Peak = %.0f publications in %.1f</sup>",
            metrics$R_squared,
            params$peak_annual,
            params$tm_year
          ),
          x = 0.5,
          xanchor = 'center'
        ),
        xaxis = list(
          title = "Year",
          showgrid = FALSE
        ),
        yaxis = list(
          title = "Publications (Annual)",
          showgrid = TRUE,
          gridcolor = '#f0f0f0',
          rangemode = 'tozero'
        ),
        hovermode = 'closest',
        showlegend = TRUE,
        legend = list(
          x = 0.02,
          y = 0.98,
          bgcolor = 'rgba(255, 255, 255, 0.8)',
          bordercolor = '#ddd',
          borderwidth = 1
        ),
        plot_bgcolor = 'white',
        paper_bgcolor = 'white'
      )
  } else {
    # === CUMULATIVE PUBLICATIONS PLOT ===

    K <- params$K

    p <- plotly::plot_ly()

    # Observed curve
    p <- p %>%
      plotly::add_trace(
        data = observed,
        x = ~year,
        y = ~cumulative,
        type = 'scatter',
        mode = 'lines',
        name = 'Logistic fit',
        line = list(color = '#2ca02c', width = 2),
        hovertemplate = paste0(
          '<b>Year:</b> %{x}<br>',
          '<b>Cumulative:</b> %{y:.0f}<br>',
          '<b>% of K:</b> %{customdata:.1f}%<br>',
          '<extra></extra>'
        ),
        customdata = ~ (cumulative / K * 100)
      )

    # Forecast curve
    if (nrow(forecast) > 0) {
      p <- p %>%
        plotly::add_trace(
          data = forecast,
          x = ~year,
          y = ~cumulative,
          type = 'scatter',
          mode = 'lines',
          name = 'Forecast',
          line = list(color = '#2ca02c', width = 2, dash = 'dash'),
          hovertemplate = paste0(
            '<b>Year:</b> %{x}<br>',
            '<b>Projected:</b> %{y:.0f}<br>',
            '<b>% of K:</b> %{customdata:.1f}%<br>',
            '<extra></extra>'
          ),
          customdata = ~ (cumulative / K * 100)
        )
    }

    # Observed data points
    p <- p %>%
      plotly::add_trace(
        data = df,
        x = ~PY,
        y = ~cumulative,
        type = 'scatter',
        mode = 'markers',
        name = 'Observed',
        marker = list(color = '#2ca02c', size = 8),
        hovertemplate = paste0(
          '<b>Year:</b> %{x}<br>',
          '<b>Cumulative:</b> %{y:.0f}<br>',
          '<b>% of K:</b> %{customdata:.1f}%<br>',
          '<extra></extra>'
        ),
        customdata = ~ (cumulative / K * 100)
      )

    # Reference lines (50%, 90%, 99%)
    ref_levels <- data.frame(
      level = c(0.5, 0.9, 0.99),
      label = c("50%", "90%", "99%")
    )

    for (i in 1:nrow(ref_levels)) {
      p <- p %>%
        plotly::add_trace(
          x = c(min(complete$year), max(complete$year)),
          y = c(K * ref_levels$level[i], K * ref_levels$level[i]),
          type = 'scatter',
          mode = 'lines',
          name = ref_levels$label[i],
          line = list(color = 'gray', width = 1, dash = 'dot'),
          hovertemplate = paste0(
            '<b>',
            ref_levels$label[i],
            ' of K</b><br>',
            '<b>Value:</b> ',
            round(K * ref_levels$level[i]),
            '<br>',
            '<extra></extra>'
          ),
          showlegend = FALSE
        )

      # Add annotations
      p <- p %>%
        plotly::add_annotations(
          x = min(complete$year),
          y = K * ref_levels$level[i],
          text = ref_levels$label[i],
          xanchor = 'left',
          yanchor = 'middle',
          showarrow = FALSE,
          font = list(size = 10, color = 'gray')
        )
    }

    # Layout
    p <- p %>%
      plotly::layout(
        title = list(
          text = sprintf(
            "Cumulative Growth Curve<br><sup>Saturation (K) = %.0f publications</sup>",
            K
          ),
          x = 0.5,
          xanchor = 'center'
        ),
        xaxis = list(
          title = "Year",
          showgrid = FALSE
        ),
        yaxis = list(
          title = "Cumulative Publications",
          showgrid = TRUE,
          gridcolor = '#f0f0f0',
          rangemode = 'tozero',
          range = c(0, K * 1.05)
        ),
        hovermode = 'closest',
        showlegend = TRUE,
        legend = list(
          x = 0.02,
          y = 0.98,
          bgcolor = 'rgba(255, 255, 255, 0.8)',
          bordercolor = '#ddd',
          borderwidth = 1
        ),
        plot_bgcolor = 'white',
        paper_bgcolor = 'white'
      )
  }

  return(p)
}

### AUTHOR BIO SKETCH ----

#### GLOBAL PROFILE ----
# Function to get all unique authors from papers with valid DOI only
get_all_authors <- function(df, separator = ";") {
  authors_column <- df$AU
  doi_column <- if ("DI" %in% names(df)) df$DI else NULL

  # If doi_column is provided, filter authors based on valid DOI
  if (!is.null(doi_column)) {
    # Identify entries with valid DOI (not NA and not "<NA>")
    valid_doi <- !is.na(doi_column) & doi_column != "<NA>" & doi_column != ""

    # Filter authors column based on valid DOI
    authors_column <- authors_column[valid_doi]
  }

  # Handle NA and empty strings in authors column
  valid_entries <- authors_column[!is.na(authors_column) & authors_column != ""]

  # Return empty character vector if no valid entries
  if (length(valid_entries) == 0) {
    return(character(0))
  }

  # Split all author strings using the specified separator
  all_authors <- unlist(strsplit(valid_entries, separator, fixed = TRUE))

  # Remove leading and trailing whitespace from each author name
  all_authors_clean <- trimws(all_authors)

  # Remove empty elements that might result from splitting
  all_authors_clean <- all_authors_clean[all_authors_clean != ""]

  # Remove [ANONYMOUS] entries
  all_authors_clean <- all_authors_clean[all_authors_clean != "[ANONYMOUS]"]

  # Return unique authors only
  return(unique(all_authors_clean %>% sort()))
}


authorCard <- function(selected_author, values) {
  req(selected_author)
  works_exact <- findAuthorWorks(
    selected_author,
    values$M,
    exact_match = TRUE
  ) %>%
    filter(!is.na(doi))
  if (nrow(works_exact) == 0) {
    return(HTML("No works found for this author.", type = "error"))
  }
  author_position <- works_exact$author_position[1]
  doi <- works_exact$doi[1]
  on_line <- check_online()

  if (on_line) {
    if (!is.null(values$author_data)) {
      author_data <- values$author_data
    } else {
      author_data <- tibble(AUid = character(), display_name = character())
    }

    if (selected_author %in% author_data$AUid) {
      AU_data <- author_data %>% dplyr::filter(AUid == selected_author)
    } else {
      suppressWarnings(
        AU_data <- tryCatch(
          {
            authorBio(author_position = author_position, doi = doi)
          },
          error = function(e) {
            NULL
          }
        )
      )
      # check if AUid is a tibble
      if (is.data.frame(AU_data)) {
        author_data <- bind_rows(
          author_data,
          AU_data %>%
            mutate(AUid = selected_author)
        )
        values$author_data <- author_data
      } else {
        return(HTML("No author data found.", type = "error"))
      }
    }
    # values$author_data <- author_data
    card <- create_author_bio_card(
      AU_data,
      width = "100%",
      show_trends = TRUE,
      show_topics = TRUE,
      max_topics = 20
    )
  } else {
    card <- HTML(
      "No internet connection. Unable to fetch author data.",
      type = "error"
    )
  }
}

create_empty_author_bio_card <- function(
  author_name = "Author Name",
  width = "100%",
  message = "Author data not found or not yet retrieved"
) {
  # Metrics cards with placeholder values
  metrics_cards <- fluidRow(
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "Publications",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "Citations",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "H-Index",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "2yr Mean Cit.",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    )
  )

  # Empty trend chart placeholder
  trend_chart <- tagList(
    h4(
      "Publication Trends (Last 10 Years)",
      style = "margin-top: 20px; color: #95A5A6;"
    ),
    div(
      style = "height: 200px; background: #f8f9fa; border-radius: 8px; padding: 15px; 
                 display: flex; align-items: center; justify-content: center;",
      div(
        style = "text-align: center; color: #95A5A6;",
        tags$i(
          class = "fa fa-chart-bar",
          style = "font-size: 48px; margin-bottom: 10px; opacity: 0.3;"
        ),
        br(),
        "No trend data available"
      )
    )
  )

  # Empty topics section
  topics_section <- tagList(
    h4("Main Research Topics", style = "margin-top: 20px; color: #95A5A6;"),
    div(
      class = "topics-container",
      style = "text-align: center; padding: 20px; 
                                            background: #f8f9fa; border-radius: 8px;",
      div(
        style = "color: #95A5A6;",
        tags$i(
          class = "fa fa-tags",
          style = "font-size: 36px; margin-bottom: 10px; opacity: 0.3;"
        ),
        br(),
        "No research topics available"
      )
    )
  )

  # Main card UI
  div(
    class = "author-bio-card-empty",
    style = paste0(
      "width: ",
      width,
      "; background: white; 
                   border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); 
                   padding: 25px; margin: 15px 0; font-family: 'Segoe UI', Tahoma, sans-serif;
                   opacity: 0.8; border: 2px dashed #bdc3c7;"
    ),

    # Header section
    div(
      class = "author-header",
      style = "border-bottom: 2px solid #ecf0f1; padding-bottom: 20px;",
      fluidRow(
        column(
          8,
          h2(
            author_name,
            style = "margin: 0 0 10px 0; color: #95A5A6; font-weight: 600;"
          ),
          h5(
            "Institution not available",
            style = "margin: 0 0 5px 0; color: #BDC3C7; font-weight: 400;"
          ),
          p(
            "📍 Country not available",
            style = "margin: 0 0 10px 0; color: #BDC3C7;"
          )
        ),
        column(
          4,
          div(
            style = "text-align: right; padding-top: 10px;",
            div(
              tags$span("ORCID not available", style = "color: #95A5A6;"),
              style = "margin-bottom: 8px;"
            ),
            div(tags$span(
              "OpenAlex Profile not available",
              style = "color: #95A5A6;"
            ))
          )
        )
      )
    ),

    # Info message
    div(
      style = "margin: 20px 0; padding: 15px; background: #fff3cd; border: 1px solid #ffeeba; 
                 border-radius: 8px; color: #856404;",
      tags$i(class = "fa fa-info-circle", style = "margin-right: 8px;"),
      strong("Information: "),
      message
    ),

    # Metrics section
    div(
      style = "margin: 20px 0;",
      h4(
        "Bibliometric Indicators",
        style = "margin-bottom: 15px; color: #95A5A6;"
      ),
      metrics_cards
    ),

    # Additional metrics
    div(
      style = "margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 8px;",
      fluidRow(
        column(6, strong("i10-Index: "), span("--", style = "color: #95A5A6;")),
        column(
          6,
          strong("OpenAlex ID: "),
          span(
            "Not available",
            style = "color: #95A5A6; font-family: monospace; font-size: 12px;"
          )
        )
      )
    ),

    # Trends and topics placeholders
    trend_chart,
    topics_section,

    # Footer
    div(
      style = "margin-top: 25px; padding-top: 15px; border-top: 1px solid #ecf0f1; 
                 font-size: 11px; color: #95A5A6; text-align: center;",
      paste(
        "Please retrieve author data to view bibliometric information -",
        format(Sys.time(), "%Y-%m-%d %H:%M")
      )
    )
  )
}

create_author_bio_card <- function(
  author_data,
  width = "100%",
  show_trends = TRUE,
  show_topics = TRUE,
  max_topics = 5
) {
  # Extract key information safely
  author_name <- author_data$display_name[1] %||% "Unknown Author"
  institution <- author_data$primary_affiliation[1] %||%
    author_data$last_known_institutions[[1]]$display_name[1] %||%
    "Institution not available"
  institution_ror <- author_data$primary_affiliation_ror[1] %||%
    author_data$last_known_institutions[[1]]$ror[1] %||%
    NA
  country <- author_data$primary_affiliation_country[1] %||%
    author_data$last_known_institutions[[1]]$country_code[1] %||%
    "Country not available"

  works_count <- author_data$works_count[1] %||% 0
  citations <- author_data$cited_by_count[1] %||% 0
  h_index <- author_data$h_index[1] %||% 0
  i10_index <- author_data$i10_index[1] %||% 0
  mean_citedness <- author_data$`2yr_mean_citedness`[1] %||% 0

  orcid <- author_data$orcid[1]
  position_type <- author_data$author_position_type[1] %||% "author"
  is_corresponding <- author_data$is_corresponding[1] %||% FALSE

  # Create ORCID link if available
  orcid_link <- if (!is.null(orcid) && !is.na(orcid)) {
    tags$a(
      href = orcid,
      target = "_blank",
      tags$img(src = "ORCID.jpg", style = "height: 16px; margin-right: 5px;"),
      "ORCID Profile",
      style = "text-decoration: none; color: #338B13;"
    )
  } else {
    tags$span("ORCID not available", style = "color: #666;")
  }

  # Create OpenAlex link
  openalex_id <- gsub("https://openalex.org/", "", author_data$id[1])
  openalex_link <- tags$a(
    href = paste0("https://openalex.org/", openalex_id),
    target = "_blank",
    tags$img(src = "openalex.jpg", style = "height: 16px; margin-right: 5px;"),
    "OpenAlex Profile",
    style = "text-decoration: none; color: #E74C3C;"
  )

  # Format numbers with thousands separator
  format_number <- function(x) {
    if (is.null(x) || is.na(x)) {
      return("0")
    }
    format(x, big.mark = ",", scientific = FALSE)
  }

  # Create metrics cards
  metrics_cards <- fluidRow(
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(format_number(works_count), style = "margin: 0; font-size: 24px;"),
        p(
          "Publications",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(format_number(citations), style = "margin: 0; font-size: 24px;"),
        p(
          "Citations",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(h_index, style = "margin: 0; font-size: 24px;"),
        p(
          "H-Index",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(round(mean_citedness, 1), style = "margin: 0; font-size: 24px;"),
        p(
          "2yr Mean Cit.",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    )
  )

  # Create publication trend chart if requested
  trend_chart <- if (show_trends && !is.null(author_data$counts_by_year[[1]])) {
    counts_data <- author_data$counts_by_year[[1]]
    recent_data <- counts_data[
      counts_data$year >= (max(counts_data$year) - 9),
    ]
    # Order by year from oldest to newest
    recent_data <- recent_data[order(recent_data$year), ]

    tagList(
      h4(
        "Publication Trends (Last 10 Years)",
        style = "margin-top: 20px; color: #2C3E50;"
      ),
      div(
        style = "height: 200px; background: #f8f9fa; border-radius: 8px; padding: 15px;",
        # Simple bar chart representation
        div(
          style = "display: flex; align-items: end; height: 170px; gap: 3px;",
          lapply(1:nrow(recent_data), function(i) {
            height_pct <- (recent_data$works_count[i] /
              max(recent_data$works_count, na.rm = TRUE)) *
              100
            div(
              style = paste0(
                "background: linear-gradient(to top, #3498db, #2980b9); 
                                   height: ",
                height_pct,
                "%; 
                                   width: ",
                100 / nrow(recent_data) - 1,
                "%; 
                                   border-radius: 3px 3px 0 0;
                                   position: relative;"
              ),
              div(
                style = "position: absolute; bottom: -20px; font-size: 10px; 
                               width: 100%; text-align: center; color: #666;",
                recent_data$year[i]
              ),
              div(
                style = "position: absolute; top: -15px; font-size: 9px; 
                               width: 100%; text-align: center; color: #333; font-weight: bold;",
                recent_data$works_count[i]
              )
            )
          })
        )
      )
    )
  } else {
    NULL
  }

  # Create research topics section if requested
  topics_section <- if (show_topics && !is.null(author_data$topics[[1]])) {
    topics_data <- author_data$topics[[1]]
    top_topics <- topics_data[topics_data$type == "topic", ][
      1:min(max_topics, sum(topics_data$type == "topic")),
    ] %>%
      sample_frac(size = 1)

    # Calculate font sizes based on counts
    if (nrow(top_topics) > 0) {
      min_count <- min(top_topics$count, na.rm = TRUE)
      max_count <- max(top_topics$count, na.rm = TRUE)
      min_font_size <- 10
      max_font_size <- 18

      # Calculate proportional font sizes
      font_sizes <- if (max_count == min_count) {
        rep(min_font_size, nrow(top_topics))
      } else {
        min_font_size +
          (top_topics$count - min_count) /
            (max_count - min_count) *
            (max_font_size - min_font_size)
      }
    }

    tagList(
      h4("Main Research Topics", style = "margin-top: 20px; color: #2C3E50;"),
      div(
        class = "topics-container",
        style = "text-align: center;",
        lapply(1:nrow(top_topics), function(i) {
          if (!is.na(top_topics$display_name[i]) && !is.na(top_topics$id[i])) {
            tags$a(
              href = top_topics$id[i],
              target = "_blank",
              class = "topic-badge",
              style = paste0(
                "display: inline-block; background: ",
                colorlist()[i],
                # sample(
                # c("#3498db", "#e74c3c", "#2ecc71", "#f39c12", "#9b59b6"),
                # 1),
                "; opacity: 0.7; color: white; padding: 5px 10px; margin: 3px; 
                            border-radius: 15px; font-weight: 500; text-decoration: none;
                            font-size: ",
                round(font_sizes[i], 1),
                "px;"
              ),
              paste0(top_topics$display_name[i], " (", top_topics$count[i], ")")
            )
          }
        })
      )
    )
  } else {
    NULL
  }

  institution_link <- tags$a(
    href = institution_ror,
    target = "_blank",
    institution
    # ,style = "text-decoration: none; color: #E74C3C;"
  )

  # Main card UI
  div(
    class = "author-bio-card",
    style = paste0(
      "width: ",
      width,
      "; background: white; 
                   border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); 
                   padding: 25px; margin: 15px 0; font-family: 'Segoe UI', Tahoma, sans-serif;"
    ),

    # Header section
    div(
      class = "author-header",
      style = "border-bottom: 2px solid #ecf0f1; padding-bottom: 20px;",
      fluidRow(
        column(
          8,
          h2(
            author_name,
            style = "margin: 0 0 10px 0; color: #2C3E50; font-weight: 600;"
          ),
          h5(
            institution_link,
            style = "margin: 0 0 5px 0; color: #7F8C8D; font-weight: 400;"
          ),
          p(paste("📍", country), style = "margin: 0 0 10px 0; color: #95A5A6;")
        ),
        column(
          4,
          div(
            style = "text-align: right; padding-top: 10px;",
            div(orcid_link, style = "margin-bottom: 8px;"),
            div(openalex_link)
          )
        )
      )
    ),

    # Metrics section
    div(
      style = "margin: 20px 0;",
      h4(
        "Bibliometric Indicators",
        style = "margin-bottom: 15px; color: #2C3E50;"
      ),
      metrics_cards
    ),

    # Additional metrics
    div(
      style = "margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 8px;",
      fluidRow(
        column(
          6,
          strong("i10-Index: "),
          span(i10_index, style = "color: #2980b9;")
        ),
        column(
          6,
          strong("OpenAlex ID: "),
          span(
            openalex_id,
            style = "color: #666; font-family: monospace; font-size: 12px;"
          )
        )
      )
    ),

    # Trends and topics
    trend_chart,
    topics_section,

    # Footer with source information
    div(
      style = "margin-top: 25px; padding-top: 15px; border-top: 1px solid #ecf0f1; 
                 font-size: 11px; color: #95A5A6; text-align: center;",
      paste(
        "Data retrieved from OpenAlex on",
        format(author_data$query_timestamp[1], "%Y-%m-%d %H:%M")
      )
    )
  )
}

# Wrapper function for use in Shiny renderUI

render_author_bio_card <- function(author_data, ...) {
  renderUI({
    create_author_bio_card(author_data, ...)
  })
}

#### LOCAL PROFILE ----
create_local_author_bio_card <- function(
  local_author_data,
  selected_author,
  max_py = 2024,
  width = "100%",
  show_trends = TRUE,
  show_keywords = TRUE,
  max_keywords = 8,
  max_works_display = 100
) {
  # Extract author information
  author_name <- to_title_case(selected_author)

  # Calculate metrics from local data
  works_count <- nrow(local_author_data)
  total_citations <- sum(as.numeric(local_author_data$TC), na.rm = TRUE)
  years <- as.numeric(local_author_data$PY)
  current_year <- as.numeric(format(Sys.Date(), "%Y"))

  # Calculate mean citations per year (weighted by age of publications)
  publication_ages <- current_year - years
  publication_ages[publication_ages <= 0] <- 1 # Avoid division by zero
  mean_citations_per_year <- mean(
    as.numeric(local_author_data$TC) / publication_ages,
    na.rm = TRUE
  )

  # Calculate additional metrics
  years_active <- max(years, na.rm = TRUE) - min(years, na.rm = TRUE) + 1
  avg_citations_per_work <- total_citations / works_count

  # Calculate h-index (simplified version)
  citations_sorted <- sort(as.numeric(local_author_data$TC), decreasing = TRUE)
  h_index <- sum(citations_sorted >= seq_along(citations_sorted))

  # Get most recent years for activity
  recent_years <- years[years >= (max_py - 4)]
  recent_productivity <- length(recent_years)

  # Format numbers with thousands separator
  format_number <- function(x) {
    if (is.null(x) || is.na(x)) {
      return("0")
    }
    format(round(x), big.mark = ",", scientific = FALSE)
  }

  # Create metrics cards
  metrics_cards <- fluidRow(
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(format_number(works_count), style = "margin: 0; font-size: 24px;"),
        p(
          "Publications",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(
          format_number(total_citations),
          style = "margin: 0; font-size: 24px;"
        ),
        p(
          "Total Citations",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(h_index, style = "margin: 0; font-size: 24px;"),
        p(
          "H-Index (Local)",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center;",
        h4(
          round(mean_citations_per_year, 1),
          style = "margin: 0; font-size: 24px;"
        ),
        p(
          "Cit./Year",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    )
  )

  # Create publication trend chart if requested
  trend_chart <- if (show_trends && length(years) > 0) {
    # Create yearly publication counts
    year_counts <- table(years)
    year_df <- data.frame(
      year = as.numeric(names(year_counts)),
      count = as.numeric(year_counts)
    )
    # Get recent 10 years
    recent_years_range <- max(year_df$year) - 9
    recent_data <- year_df[year_df$year >= recent_years_range, ]
    # Fill missing years with 0
    all_years <- recent_years_range:max(year_df$year)
    complete_data <- data.frame(year = all_years)
    complete_data <- merge(complete_data, recent_data, all = TRUE)
    complete_data$count[is.na(complete_data$count)] <- 0
    complete_data <- complete_data[order(complete_data$year), ]

    if (nrow(complete_data) > 0) {
      tagList(
        h4(
          "Publication Trends (Last 10 Years)",
          style = "margin-top: 20px; color: #2C3E50;"
        ),
        div(
          style = "height: 200px; background: #f8f9fa; border-radius: 8px; padding: 15px;",
          div(
            style = "display: flex; align-items: end; height: 170px; gap: 3px;",
            lapply(1:nrow(complete_data), function(i) {
              max_count <- max(complete_data$count, na.rm = TRUE)
              height_pct <- if (max_count > 0) {
                (complete_data$count[i] / max_count) * 100
              } else {
                0
              }
              div(
                style = paste0(
                  "background: linear-gradient(to top, #3498db, #2980b9); 
                                     height: ",
                  max(height_pct, 2),
                  "%; 
                                     width: ",
                  100 / nrow(complete_data) - 1,
                  "%; 
                                     border-radius: 3px 3px 0 0;
                                     position: relative;"
                ),
                div(
                  style = "position: absolute; bottom: -20px; font-size: 10px; 
                                 width: 100%; text-align: center; color: #666;",
                  complete_data$year[i]
                ),
                div(
                  style = "position: absolute; top: -15px; font-size: 9px; 
                                 width: 100%; text-align: center; color: #333; font-weight: bold;",
                  complete_data$count[i]
                )
              )
            })
          )
        )
      )
    }
  } else {
    NULL
  }

  # Create keywords section if requested
  keywords_section <- if (show_keywords && "DE" %in% names(local_author_data)) {
    # Extract and process keywords
    all_keywords <- unlist(strsplit(
      paste(local_author_data$DE, collapse = ";"),
      ";"
    ))
    all_keywords <- to_title_case(trimws(toupper(all_keywords)))
    all_keywords <- all_keywords[all_keywords != "" & !is.na(all_keywords)]

    if (length(all_keywords) > 0) {
      keyword_freq <- sort(table(all_keywords), decreasing = TRUE)
      top_keywords <- head(keyword_freq, max_keywords)
      top_keywords <- top_keywords[sample(length(top_keywords))] # Shuffle order

      # Calculate font sizes based on frequency
      if (length(top_keywords) > 0) {
        min_freq <- min(top_keywords)
        max_freq <- max(top_keywords)
        min_font_size <- 10
        max_font_size <- 18

        font_sizes <- if (max_freq == min_freq) {
          rep(min_font_size, length(top_keywords))
        } else {
          min_font_size +
            (top_keywords - min_freq) /
              (max_freq - min_freq) *
              (max_font_size - min_font_size)
        }
      }

      tagList(
        h4("Main Keywords", style = "margin-top: 20px; color: #2C3E50;"),
        div(
          class = "keywords-container",
          style = "text-align: center;",
          lapply(1:length(top_keywords), function(i) {
            colors <- c(
              "#3498db",
              "#e74c3c",
              "#2ecc71",
              "#f39c12",
              "#9b59b6",
              "#1abc9c",
              "#34495e",
              "#e67e22"
            )
            color <- colors[((i - 1) %% length(colors)) + 1]

            tags$span(
              class = "keyword-badge",
              style = paste0(
                "display: inline-block; background: ",
                color,
                "; opacity: 0.8; color: white; padding: 5px 12px; margin: 3px; 
                            border-radius: 15px; font-weight: 500;
                            font-size: ",
                round(font_sizes[i], 1),
                "px;"
              ),
              paste0(names(top_keywords)[i], " (", top_keywords[i], ")")
            )
          })
        )
      )
    }
  } else {
    NULL
  }

  # Create works list (scrollable)
  works_section <- if (nrow(local_author_data) > 0) {
    # Prepare works data
    works_to_show <- head(
      local_author_data[
        order(as.numeric(local_author_data$PY), decreasing = TRUE),
      ],
      max_works_display
    )

    works_list <- lapply(1:nrow(works_to_show), function(i) {
      work <- works_to_show[i, ]
      title <- work$TI %||% "Title not available"
      journal <- work$SO %||% "Journal not available"
      year <- work$PY %||% "Year not available"
      doi <- work$DI %||% ""
      citations <- as.numeric(work$TC) %||% 0

      # Create DOI link if available
      doi_link <- if (!is.null(doi) && !is.na(doi) && doi != "") {
        tags$a(
          href = paste0("https://doi.org/", doi),
          target = "_blank",
          style = "color: #3498db; text-decoration: none; font-size: 11px;",
          paste0("DOI: ", doi)
        )
      } else {
        tags$span(
          "DOI not available",
          style = "color: #95a5a6; font-size: 11px;"
        )
      }

      div(
        class = "work-item",
        style = "border-bottom: 1px solid #ecf0f1; padding: 12px 0; margin: 0;",
        div(
          style = "margin-bottom: 8px;",
          tags$h6(
            title,
            style = "margin: 0 0 5px 0; color: #2c3e50; font-weight: 600; line-height: 1.3;"
          )
        ),
        div(
          style = "margin-bottom: 8px;",
          tags$span(
            journal,
            style = "color: #7f8c8d; font-style: italic; font-size: 13px; margin-right: 15px;"
          ),
          tags$span(
            paste0("(", year, ")"),
            style = "color: #95a5a6; font-size: 13px; margin-right: 15px;"
          ),
          tags$span(
            paste0("Citations: ", format_number(citations)),
            style = "color: #e74c3c; font-weight: 500; font-size: 12px;"
          )
        ),
        div(style = "margin-top: 5px;", doi_link)
      )
    })

    tagList(
      h4(
        paste0("Publications (", nrow(local_author_data), " total)"),
        style = "margin-top: 20px; color: #2C3E50;"
      ),
      div(
        class = "works-container",
        style = "max-height: 400px; overflow-y: auto; background: #f8f9fa; 
                   border-radius: 8px; padding: 15px; border: 1px solid #e9ecef;",
        works_list
      )
    )
  } else {
    NULL
  }

  # Main card UI
  div(
    class = "local-author-bio-card",
    style = paste0(
      "width: ",
      width,
      "; background: white; 
                   border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); 
                   padding: 25px; margin: 15px 0; font-family: 'Segoe UI', Tahoma, sans-serif;"
    ),

    # Header section
    div(
      class = "author-header",
      style = "border-bottom: 2px solid #ecf0f1; padding-bottom: 20px;",
      h2(
        author_name,
        style = "margin: 0 0 10px 0; color: #2C3E50; font-weight: 600;"
      ),
      p(
        "📊 Local Collection Profile",
        style = "margin: 0 0 10px 0; color: #7F8C8D; font-style: italic;"
      )
    ),

    # Metrics section
    div(
      style = "margin: 20px 0;",
      h4(
        "Local Bibliometric Indicators",
        style = "margin-bottom: 15px; color: #2C3E50;"
      ),
      metrics_cards
    ),

    # Additional metrics
    div(
      style = "margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 8px;",
      fluidRow(
        column(
          4,
          strong("Years Active: "),
          span(years_active, style = "color: #2980b9;")
        ),
        column(
          4,
          strong("Avg Cit./Work: "),
          span(round(avg_citations_per_work, 1), style = "color: #27ae60;")
        ),
        column(
          4,
          strong("Recent Activity (5yr): "),
          span(recent_productivity, style = "color: #e74c3c;")
        )
      )
    ),

    # Trends and keywords
    trend_chart,
    keywords_section,

    # Works section
    works_section,

    # Footer with source information
    div(
      style = "margin-top: 25px; padding-top: 15px; border-top: 1px solid #ecf0f1; 
                 font-size: 11px; color: #95A5A6; text-align: center;",
      paste(
        "Local collection data analyzed on",
        format(Sys.time(), "%Y-%m-%d %H:%M")
      )
    )
  )
}

create_empty_local_author_bio_card <- function(
  author_name = "Author Name Not Available",
  width = "100%",
  message = "Local author data not found or not yet processed"
) {
  # Metrics cards with placeholder values
  metrics_cards <- fluidRow(
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "Publications",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "Total Citations",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "H-Index (Local)",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    ),
    column(
      3,
      div(
        class = "metric-card",
        style = "background: linear-gradient(135deg, #bdc3c7 0%, #95a5a6 100%); 
                        color: white; padding: 15px; border-radius: 8px; text-align: center; opacity: 0.7;",
        h4("--", style = "margin: 0; font-size: 24px;"),
        p(
          "Cit./Year",
          style = "margin: 5px 0 0 0; font-size: 12px; opacity: 0.9;"
        )
      )
    )
  )

  # Empty trend chart placeholder
  trend_chart <- tagList(
    h4(
      "Publication Trends (Last 10 Years)",
      style = "margin-top: 20px; color: #95A5A6;"
    ),
    div(
      style = "height: 200px; background: #f8f9fa; border-radius: 8px; padding: 15px; 
                 display: flex; align-items: center; justify-content: center; border: 1px solid #e9ecef;",
      div(
        style = "text-align: center; color: #95A5A6;",
        tags$div(
          "📊",
          style = "font-size: 48px; margin-bottom: 10px; opacity: 0.3;"
        ),
        br(),
        "No trend data available"
      )
    )
  )

  # Empty keywords section
  keywords_section <- tagList(
    h4("Main Keywords", style = "margin-top: 20px; color: #95A5A6;"),
    div(
      class = "keywords-container",
      style = "text-align: center; padding: 20px; 
                                             background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef;",
      div(
        style = "color: #95A5A6;",
        tags$div(
          "🏷️",
          style = "font-size: 36px; margin-bottom: 10px; opacity: 0.3;"
        ),
        br(),
        "No keywords available"
      )
    )
  )

  # Empty works section
  works_section <- tagList(
    h4("Publications (0 total)", style = "margin-top: 20px; color: #95A5A6;"),
    div(
      class = "works-container",
      style = "height: 200px; background: #f8f9fa; border-radius: 8px; padding: 15px; 
                 border: 1px solid #e9ecef; display: flex; align-items: center; justify-content: center;",
      div(
        style = "text-align: center; color: #95A5A6;",
        tags$div(
          "📄",
          style = "font-size: 48px; margin-bottom: 10px; opacity: 0.3;"
        ),
        br(),
        "No publications available",
        br(),
        tags$small(
          "Publications will appear here when local data is processed",
          style = "font-style: italic; opacity: 0.7;"
        )
      )
    )
  )

  # Main card UI
  div(
    class = "local-author-bio-card-empty",
    style = paste0(
      "width: ",
      width,
      "; background: white; 
                   border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); 
                   padding: 25px; margin: 15px 0; font-family: 'Segoe UI', Tahoma, sans-serif;
                   opacity: 0.8; border: 2px dashed #bdc3c7;"
    ),

    # Header section
    div(
      class = "author-header",
      style = "border-bottom: 2px solid #ecf0f1; padding-bottom: 20px;",
      h2(
        author_name,
        style = "margin: 0 0 10px 0; color: #95A5A6; font-weight: 600;"
      ),
      p(
        "📊 Local Collection Profile",
        style = "margin: 0 0 10px 0; color: #BDC3C7; font-style: italic;"
      )
    ),

    # Info message
    div(
      style = "margin: 20px 0; padding: 15px; background: #fff3cd; border: 1px solid #ffeeba; 
                 border-radius: 8px; color: #856404;",
      tags$span("ℹ️", style = "margin-right: 8px; font-size: 16px;"),
      strong("Information: "),
      message
    ),

    # Metrics section
    div(
      style = "margin: 20px 0;",
      h4(
        "Local Bibliometric Indicators",
        style = "margin-bottom: 15px; color: #95A5A6;"
      ),
      metrics_cards
    ),

    # Additional metrics
    div(
      style = "margin: 15px 0; padding: 15px; background: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef;",
      fluidRow(
        column(
          4,
          strong("Years Active: "),
          span("--", style = "color: #95A5A6;")
        ),
        column(
          4,
          strong("Avg Cit./Work: "),
          span("--", style = "color: #95A5A6;")
        ),
        column(
          4,
          strong("Recent Activity (5yr): "),
          span("--", style = "color: #95A5A6;")
        )
      )
    ),

    # Trends, keywords, and works placeholders
    trend_chart,
    keywords_section,
    works_section,

    # Footer
    div(
      style = "margin-top: 25px; padding-top: 15px; border-top: 1px solid #ecf0f1; 
                 font-size: 11px; color: #95A5A6; text-align: center;",
      paste(
        "Please process local collection data to view bibliometric information -",
        format(Sys.time(), "%Y-%m-%d %H:%M")
      )
    )
  )
}

# Helper function for safe extraction (null coalescing operator)
`%||%` <- function(x, y) if (is.null(x) || is.na(x) || length(x) == 0) y else x


# from igraph to png file
igraph2PNG <- function(x, filename, width = 10, height = 7, dpi = 75) {
  V(x)$centr <- centr_betw(x)$res
  df <- data.frame(
    name = V(x)$label,
    cluster = V(x)$color,
    centr = V(x)$centr
  ) %>%
    group_by(cluster) %>%
    slice_head(n = 3)
  V(x)$label[!(V(x)$label %in% df$name)] <- ""
  png(
    filename = filename,
    width = width,
    height = height,
    unit = "in",
    res = dpi
  )
  grid::grid.draw(plot(x))
  dev.off()
}

# from ggplot to plotly
plot.ly <- function(
  g,
  flip = FALSE,
  side = "r",
  aspectratio = 1,
  size = 0.15,
  data.type = 2,
  height = 0,
  customdata = NA
) {
  g <- g + labs(title = NULL)

  gg <- ggplotly(g, tooltip = "text") %>%
    config(
      displaylogo = FALSE,
      modeBarButtonsToRemove = c(
        "toImage",
        "sendDataToCloud",
        "pan2d",
        "select2d",
        "lasso2d",
        "toggleSpikelines",
        "hoverClosestCartesian",
        "hoverCompareCartesian"
      )
    )

  return(gg)
}

freqPlot <- function(
  xx,
  x,
  y,
  textLaby,
  textLabx,
  title,
  values,
  string.max = 70
) {
  xl <- c(
    max(xx[, x]) - 0.02 - diff(range(xx[, x])) * 0.125,
    max(xx[, x]) - 0.02
  ) +
    1
  yl <- c(1, 1 + length(unique(xx[, y])) * 0.125)

  Text <- paste(textLaby, ": ", xx[, y], "\n", textLabx, ": ", xx[, x])

  if (title == "Most Local Cited References" & values$M$DB[1] == "SCOPUS") {
    xx[, y] <- gsub(
      "^(.+?)\\.,.*\\((\\d{4})\\)$",
      paste0("\\1", "., ", "\\2"),
      xx[, y]
    )
  }

  xx[, y] <- substr(xx[, y], 1, string.max)

  g <- ggplot(xx, aes(x = xx[, x], y = xx[, y], label = xx[, x], text = Text)) +
    geom_segment(
      aes(x = 0, y = xx[, y], xend = xx[, x], yend = xx[, y]),
      color = "grey50"
    ) +
    geom_point(aes(color = -xx[, x], size = xx[, x]), show.legend = FALSE) +
    scale_radius(range = c(5, 12)) +
    geom_text(color = "white", size = 3) +
    scale_y_discrete(limits = rev(xx[, y])) +
    scale_fill_continuous(type = "gradient") +
    labs(title = title, y = textLaby) +
    labs(x = textLabx) +
    expand_limits(y = c(1, length(xx[, y]) + 1)) +
    theme_minimal() +
    theme(axis.text.y = element_text(angle = 0, hjust = 0)) +
    annotation_custom(
      values$logoGrid,
      xmin = xl[1],
      xmax = xl[2],
      ymin = yl[1],
      ymax = yl[2]
    )

  return(g)
}

emptyPlot <- function(errortext) {
  g <- ggplot() +
    theme_void() +
    theme(legend.position = "none") +
    annotate("text", x = 4, y = 25, label = errortext, size = 10)
  plot(g)
}

count.duplicates <- function(DF) {
  x <- do.call("paste", c(DF, sep = "\r"))
  ox <- order(x)
  rl <- rle(x[ox])
  cbind(DF[ox[cumsum(rl$lengths)], , drop = FALSE], count = rl$lengths)
}

reduceRefs <- function(A) {
  ind <- unlist(regexec("*V[0-9]", A))
  A[ind > -1] <- substr(A[ind > -1], 1, (ind[ind > -1] - 1))
  ind <- unlist(regexec("*DOI ", A))
  A[ind > -1] <- substr(A[ind > -1], 1, (ind[ind > -1] - 1))
  return(A)
}

check_online <- function(
  host = "8.8.8.8",
  timeout = 5,
  # min_success = 1,
  method = "ping" # method = c("ping", "socket", "http")
) {
  method <- match.arg(method)

  if (method == "ping") {
    # Usa solo il codice di ritorno, non analizza l'output
    ping_cmd <- if (.Platform$OS.type == "windows") {
      sprintf("ping -n 1 -w %d %s", timeout * 1000, host)
    } else {
      sprintf("ping -c 1 -W %d %s", timeout, host)
    }
    exit_code <- suppressWarnings(system(
      ping_cmd,
      ignore.stdout = TRUE,
      ignore.stderr = TRUE
    ))
    return(exit_code == 0)
  } else if (method == "socket") {
    # Connessione TCP a DNS Google (porta 53)
    tryCatch(
      {
        con <- socketConnection(
          host = host,
          port = 53,
          blocking = TRUE,
          open = "r+",
          timeout = timeout
        )
        close(con)
        return(TRUE)
      },
      error = function(e) {
        return(FALSE)
      }
    )
  } else if (method == "http") {
    # Richiesta HTTP
    tryCatch(
      {
        con <- url("https://www.google.com", open = "rb")
        on.exit(close(con))
        readLines(con, n = 1, warn = FALSE)
        return(TRUE)
      },
      error = function(e) {
        return(FALSE)
      }
    )
  }
}

# check_online <- function(host = "8.8.8.8", min_success = 1) {
#   # Use ping command to test connectivity (works on Windows, Linux, Mac)
#   ping_cmd <- if (.Platform$OS.type == "windows") {
#     sprintf("ping -n %d %s", min_success, host)
#   } else {
#     sprintf("ping -c %d %s", min_success, host)
#   }
#
#   result <- suppressWarnings(system(
#     ping_cmd,
#     intern = TRUE,
#     ignore.stderr = TRUE
#   ))
#
#   success <- any(grepl("time=", result, ignore.case = TRUE))
#
#   if (success) {
#     # message("✅ Host is reachable.")
#     # Extract average latency (optional)
#     latency_line <- result[grepl("time=", result)]
#     times <- as.numeric(sub(".*time=([0-9.]+).*", "\\1", latency_line))
#     avg_time <- mean(times, na.rm = TRUE)
#     # message(sprintf("📶 Average latency: %.1f ms", avg_time))
#     if (avg_time < 200) {
#       return(TRUE)
#     } else {
#       return(FALSE)
#     }
#     #return(TRUE)
#   } else {
#     #message(FALSE)
#     return(FALSE)
#   }
# }

notifications <- function() {
  ## check connection and download notifications
  #online <- is_online()
  online <- check_online(host = "www.bibliometrix.org")
  location <- "https://www.bibliometrix.org/bs_notifications/biblioshiny_notifications.csv"
  notifOnline <- NULL
  if (isTRUE(online)) {
    notifOnline <- read.csv(location, header = TRUE, sep = ",")
    # ## add check to avoid blocked app when internet connection is to slow

    if (is.null(notifOnline)) {
      online <- FALSE
    } else {
      notifOnline$href[nchar(notifOnline$href) < 6] <- NA
    }
  }

  ## check if a file exists on the local machine and load it
  home <- homeFolder()

  file <- paste(home, "/biblioshiny_notifications.csv", sep = "")
  fileTrue <- file.exists(file)
  if (isTRUE(fileTrue)) {
    suppressWarnings(notifLocal <- read.csv(file, header = TRUE, sep = ","))
  }

  A <- c("noA", "A")
  B <- c("noB", "B")
  status <- paste(A[online + 1], B[fileTrue + 1], sep = "")

  switch(
    status,
    # missing both files (online and local)
    noAnoB = {
      notifTot <- data.frame(
        nots = "No notifications",
        href = NA,
        status = "info"
      ) %>%
        mutate(status = "info")
    },
    # missing online file. The local one exists.
    noAB = {
      notifTot <- notifLocal %>%
        filter(action == TRUE) %>%
        mutate(status = "info")
    },
    # missing the local file. The online one exists.
    AnoB = {
      # notifOnline <- notifOnline %>%
      #   dplyr::slice_head(n = 5)
      notifTot <- notifOnline %>%
        filter(action == TRUE) %>%
        mutate(status = "danger") %>%
        dplyr::slice_head(n = 5)
      notifOnline %>%
        filter(action == TRUE) %>%
        write.csv(file = file, quote = FALSE, row.names = FALSE)
    },
    # both files exist.
    AB = {
      notifTot <- left_join(
        notifOnline %>% mutate(status = "danger"),
        notifLocal %>% mutate(status = "info"),
        by = "nots"
      ) %>%
        mutate(status = tidyr::replace_na(status.y, "danger")) %>%
        rename(
          href = href.x,
          action = action.x
        ) %>%
        select(nots, href, action, status) %>%
        arrange(status) %>%
        filter(action == TRUE) %>%
        dplyr::slice_head(n = 5)
      notifTot %>%
        select(-status) %>%
        write.csv(file = file, quote = FALSE, row.names = FALSE)
    }
  )

  return(notifTot)
}

is_Online <- function(timeout = 3, url = "https://www.bibliometrix.org") {
  RCurl::url.exists(url, timeout = timeout)
}

initial <- function(values) {
  values$results <- list("NA")
  values$log <- "working..."
  values$load <- "FALSE"
  values$field <- values$cocngrams <- "NA"
  values$citField <- values$colField <- values$citSep <- "NA"
  values$NetWords <- values$NetRefs <- values$ColNetRefs <- matrix(NA, 1, 1)
  values$Title <- "Network"
  values$Histfield <- "NA"
  values$histlog <- "working..."
  values$kk <- 0
  values$histsearch <- "NA"
  values$citShortlabel <- "NA"
  values$S <- list("NA")
  values$GR <- "NA"
  values$nMerge <- NULL
  values$collection_description <- NULL
  ### column to export in TALL
  values$corpusCol <- c(
    "Title" = "TI",
    "Abstract" = "AB",
    "Author's Keywords" = "DE"
  )
  values$metadataCol <- c(
    "Publication Year" = "PY",
    "Document Type" = "DT",
    "DOI" = "DI",
    "Open Access" = "OA",
    "Language" = "LA",
    "First Author" = "AU1"
  )

  # Chrome enviroment variable
  if (inherits(try(pagedown::find_chrome(), silent = T), "try-error")) {
    values$Chrome_url <- NULL
  } else {
    values$Chrome_url <- pagedown::find_chrome()
  }

  return(values)
}

resetAnalysis <- function() {
  # reset menus
  output$lifeCycleSummaryUIid <- renderUI({
    div()
  })
  output$DLCPlotYear <- renderUI({
    div()
  })

  output$DLCPlotCum <- renderUI({
    div()
  })
}

### TALL Export functions ----
tallExport <- function(M, tallFields, tallMetadata, metadataCol) {
  corpus <- NULL
  ## Corpus Fields ##
  if ("Abstract" %in% tallFields) {
    if (!"AB_raw" %in% names(M)) {
      M <- M %>%
        mutate(AB_raw = sapply(AB, capitalize_after_dot, USE.NAMES = FALSE))
    }
    corpus <- c(corpus, "AB_raw")
  }

  if ("Title" %in% tallFields) {
    if (!"TI_raw" %in% names(M)) {
      M <- M %>%
        mutate(TI_raw = sapply(TI, capitalize_after_dot, USE.NAMES = FALSE))
    }
    corpus <- c(corpus, "TI_raw")
  }

  if ("Author's Keywords" %in% tallFields) {
    if (!"DE_raw" %in% names(M)) {
      M <- M %>%
        mutate(DE_raw = sapply(DE, capitalize_after_dot, USE.NAMES = FALSE))
    }
    corpus <- c(corpus, "DE_raw")
  }

  corpus <- c(corpus, as.character(metadataCol[tallMetadata]))

  M <- M %>%
    select(SR, any_of(corpus)) %>%
    rename(doc_id = SR)
  names(M) <- gsub("_raw", "", names(M))

  return(M)
}

capitalize_after_dot <- function(text) {
  # Tutto minuscolo
  text <- tolower(text)

  # Prima lettera della stringa maiuscola
  text <- paste0(toupper(substr(text, 1, 1)), substr(text, 2, nchar(text)))

  # Maiuscola dopo punto (o ! o ?) + spazio
  text <- gsub("([\\.\\!\\?]\\s*)([a-z])", "\\1\\U\\2", text, perl = TRUE)

  return(text)
}


### ANALYSIS FUNCTIONS ####
### Descriptive functions ----

ValueBoxes <- function(M) {
  # calculate statistics for Biblioshiny ValueBoxes
  df <- data.frame(Description = rep(NA, 12), Results = rep(NA, 12))

  ## VB  1 - Time span
  df[1, ] <- c("Timespan", paste(range(M$PY, na.rm = T), collapse = ":"))

  ## VB  2 - Authors
  listAU <- (strsplit(M$AU, ";"))
  nAU <- lengths(listAU)
  listAU <- unique(trimws((unlist(listAU))))
  listAU <- listAU[!is.na(listAU)]
  df[2, ] <- c("Authors", length(listAU))

  ## VB  3 - Author's Keywords (DE)
  if (!"DE" %in% names(M)) {
    M$DE <- ""
  }
  DE <- unique(trimws(gsub("\\s+|\\.|\\,", " ", unlist(strsplit(M$DE, ";")))))
  DE <- DE[!is.na(DE)]

  df[3, ] <- c("Author's Keywords (DE)", length(DE))

  ## VB  4 - Sources
  df[4, ] <- c("Sources (Journals, Books, etc)", length(unique(M$SO)))

  ## VB  5 - Authors of single-authored docs

  df[5, ] <- c(
    "Authors of single-authored docs",
    length(unique(M$AU[nAU == 1]))
  )

  ## VB  6 - References
  CR <- trimws(gsub("\\s+|\\.|\\,", " ", unlist(strsplit(M$CR, ";"))))
  CR <- CR[nchar(CR) > 0 & !is.na(CR)]
  nCR <- length(unique(CR))
  if (nCR == 1) {
    nCR <- 0
  }
  df[6, ] <- c("References", nCR)

  ## VB  7 - Documents
  df[7, ] <- c("Documents", nrow(M))

  ## VB  8 - International Co-Authorship
  if (!"AU_CO" %in% names(M)) {
    M <- metaTagExtraction(M, "AU_CO")
  }
  AU_CO <- strsplit(M$AU_CO, ";")
  Coll <- unlist(lapply(AU_CO, function(l) {
    length(unique(l)) > 1
  }))
  Coll <- sum(Coll) / nrow(M) * 100
  df[8, ] <- c("International co-authorships %", format(Coll, digits = 4))

  ## VB  9 - Document Average Age
  age <- as.numeric(substr(Sys.Date(), 1, 4)) - M$PY
  df[9, ] <- c(
    "Document Average Age",
    format(mean(age, na.rm = TRUE), digits = 3)
  )

  ## VB 10 - Annual Growth Rate
  Y <- table(M$PY)
  ny <- diff(range(M$PY, na.rm = TRUE))
  CAGR <- as.numeric(round(((Y[length(Y)] / Y[1])^(1 / (ny)) - 1) * 100, 2))
  df[10, ] <- c("Annual Growth Rate %", CAGR)

  ## VB 11 - Co-Authors per Doc
  df[11, ] <- c("Co-Authors per Doc", format(mean(nAU, na.rm = T), digit = 3))

  ## VB 12 - Average citations per doc
  df[12, ] <- c(
    "Average citations per doc",
    format(mean(M$TC, na.rm = T), digit = 4)
  )

  DT <- M %>%
    mutate(DT = tolower(DT)) %>%
    count(DT) %>%
    rename(
      Description = DT,
      Results = n
    )

  # Indexed Keywords (ID)
  ID <- unique(trimws(gsub("\\s+|\\.|\\,", " ", unlist(strsplit(M$ID, ";")))))
  ID <- ID[!is.na(ID)]
  df[nrow(df) + 1, ] <- c("Keywords Plus (ID)", length(ID))

  # Single authored docs

  df[nrow(df) + 1, ] <- c("Single-authored docs", sum(nAU == 1))

  df2 <- data.frame(
    Description = c(
      "MAIN INFORMATION ABOUT DATA",
      "Timespan",
      "Sources (Journals, Books, etc)",
      "Documents",
      "Annual Growth Rate %",
      "Document Average Age",
      "Average citations per doc",
      "References",
      "DOCUMENT CONTENTS",
      "Keywords Plus (ID)",
      "Author's Keywords (DE)",
      "AUTHORS",
      "Authors",
      "Authors of single-authored docs",
      "AUTHORS COLLABORATION",
      "Single-authored docs",
      "Co-Authors per Doc",
      "International co-authorships %",
      "DOCUMENT TYPES"
    )
  )

  df <- left_join(df2, df, by = "Description") %>%
    rbind(DT) %>%
    mutate(Results = replace_na(Results, ""))

  return(df)
}

countryCollab <- function(M) {
  sep <- ";"
  if (!("AU_CO" %in% names(M))) {
    M <- metaTagExtraction(M, Field = "AU_CO", sep)
  }
  if (!("AU1_CO" %in% names(M))) {
    M <- metaTagExtraction(M, Field = "AU1_CO", sep)
  }

  M$nCO <- as.numeric(unlist(lapply(strsplit(M$AU_CO, ";"), function(l) {
    length(unique(l)) > 1
  })))

  M$AU1_CO <- trim(gsub("[[:digit:]]", "", M$AU1_CO))
  M$AU1_CO <- gsub("UNITED STATES", "USA", M$AU1_CO)
  M$AU1_CO <- gsub("RUSSIAN FEDERATION", "RUSSIA", M$AU1_CO)
  M$AU1_CO <- gsub("TAIWAN", "CHINA", M$AU1_CO)
  M$AU1_CO <- gsub("ENGLAND", "UNITED KINGDOM", M$AU1_CO)
  M$AU1_CO <- gsub("SCOTLAND", "UNITED KINGDOM", M$AU1_CO)
  M$AU1_CO <- gsub("WALES", "UNITED KINGDOM", M$AU1_CO)
  M$AU1_CO <- gsub("NORTH IRELAND", "UNITED KINGDOM", M$AU1_CO)

  df <- M %>%
    group_by(AU1_CO) %>%
    select(AU1_CO, nCO) %>%
    summarize(
      Articles = n(),
      SCP = sum(nCO == 0),
      MCP = sum(nCO == 1)
    ) %>%
    rename(Country = AU1_CO) %>%
    arrange(desc(Articles))

  return(df)
}

Hindex_plot <- function(values, type, input) {
  hindex <- function(values, type, input) {
    switch(
      type,
      author = {
        # AU <- trim(gsub(",","",names(tableTag(values$M,"AU"))))
        values$H <- Hindex(
          values$M,
          field = "author",
          elements = NULL,
          sep = ";",
          years = Inf
        )$H %>%
          arrange(desc(h_index))
      },
      source = {
        # SO <- names(sort(table(values$M$SO),decreasing = TRUE))
        values$H <- Hindex(
          values$M,
          field = "source",
          elements = NULL,
          sep = ";",
          years = Inf
        )$H %>%
          arrange(desc(h_index))
      }
    )

    return(values)
  }

  values <- hindex(values, type = type, input)

  xx <- values$H
  if (type == "author") {
    K <- input$Hkauthor
    measure <- input$HmeasureAuthors
    title <- "Authors' Local Impact"
    xn <- "Authors"
  } else {
    K <- input$Hksource
    measure <- input$HmeasureSources
    title <- "Sources' Local Impact"
    xn <- "Sources"
  }
  if (K > dim(xx)[1]) {
    k <- dim(xx)[1]
  } else {
    k <- K
  }

  switch(
    measure,
    h = {
      m <- 2
    },
    g = {
      m <- 3
    },
    m = {
      m <- 4
      xx[, m] <- round(xx[, m], 2)
    },
    tc = {
      m <- 5
    }
  )
  xx <- xx[order(-xx[, m]), ]
  xx <- xx[1:k, c(1, m)]

  g <- freqPlot(
    xx,
    x = 2,
    y = 1,
    textLaby = xn,
    textLabx = paste("Impact Measure:", toupper(measure)),
    title = paste(title, "by", toupper(measure), "index"),
    values
  )

  res <- list(values = values, g = g)
  return(res)
}

descriptive <- function(values, type) {
  switch(
    type,
    "tab2" = {
      TAB <- values$M %>%
        group_by(PY) %>%
        count() %>%
        rename(
          Year = PY,
          Articles = n
        ) %>%
        right_join(
          data.frame(
            Year = seq(
              min(values$M$PY, na.rm = TRUE),
              max(values$M$PY, na.rm = TRUE)
            )
          ),
          by = "Year"
        ) %>%
        mutate(Articles = replace_na(Articles, 0)) %>%
        arrange(Year) %>%
        as.data.frame()

      ny <- diff(range(TAB$Year))
      values$GR <- round(
        ((TAB[nrow(TAB), 2] / TAB[1, 2])^(1 / (ny)) - 1) * 100,
        digits = 2
      )
    },
    "tab3" = {
      listAU <- (strsplit(values$M$AU, ";"))
      nAU <- lengths(listAU)
      fracAU <- rep(1 / nAU, nAU)
      TAB <- tibble(Author = unlist(listAU), fracAU = fracAU) %>%
        group_by(Author) %>%
        summarize(
          Articles = n(),
          AuthorFrac = sum(fracAU)
        ) %>%
        arrange(desc(Articles)) %>%
        as.data.frame()
      names(TAB) <- c("Authors", "Articles", "Articles Fractionalized")
      # print(S$MostProdAuthors)
    },
    "tab4" = {
      y <- as.numeric(substr(Sys.Date(), 1, 4))
      TAB <- values$M %>%
        mutate(TCperYear = TC / (y + 1 - PY)) %>%
        select(SR, DI, TC, TCperYear, PY) %>%
        group_by(PY) %>%
        mutate(NTC = TC / mean(TC)) %>%
        ungroup() %>%
        select(-PY) %>%
        arrange(desc(TC)) %>%
        as.data.frame()
      names(TAB) <- c(
        "Paper",
        "DOI",
        "Total Citations",
        "TC per Year",
        "Normalized TC"
      )
    },
    "tab5" = {
      TAB <- countryCollab(values$M)
      TAB <- TAB %>%
        mutate(Freq = Articles / sum(Articles)) %>%
        mutate(MCP_Ratio = MCP / Articles) %>%
        drop_na(Country)
    },
    "tab6" = {
      if (!"AU1_CO" %in% names(values$M)) {
        values$M <- metaTagExtraction(values$M, "AU1_CO")
      }
      TAB <- values$M %>%
        select(AU1_CO, TC) %>%
        drop_na(AU1_CO) %>%
        rename(
          Country = AU1_CO,
          TotalCitation = TC
        ) %>%
        group_by(Country) %>%
        summarise(
          "TC" = sum(TotalCitation),
          "Average Article Citations" = round(
            sum(TotalCitation) / length(TotalCitation),
            1
          )
        ) %>%
        arrange(-TC) %>%
        as.data.frame(.data)
    },
    "tab7" = {
      TAB <- values$M %>%
        select(SO) %>%
        group_by(SO) %>%
        count() %>%
        arrange(desc(n)) %>%
        rename(
          Sources = SO,
          Articles = n
        ) %>%
        as.data.frame()
    },
    "tab10" = {
      TAB <- mapworld(values$M)$tab
    },
    "tab11" = {
      if (!("AU_UN" %in% names(values$M))) {
        values$M <- metaTagExtraction(values$M, Field = "AU_UN")
      }
      TAB <- data.frame(Affiliation = unlist(strsplit(values$M$AU_UN, ";"))) %>%
        group_by(Affiliation) %>%
        count() %>%
        drop_na(Affiliation) %>%
        arrange(desc(n)) %>%
        rename(Articles = n) %>%
        filter(Affiliation != "NA") %>%
        as.data.frame()
    },
    "tab12" = {
      TAB <- tableTag(values$M, "C1")
      TAB <- data.frame(Affiliations = names(TAB), Articles = as.numeric(TAB))
      TAB <- TAB[nchar(TAB[, 1]) > 4, ]
      # names(TAB)=c("Affiliations", "Articles")
    },
    "tab13" = {
      CR <- localCitations(values$M, fast.search = FALSE, verbose = FALSE)
      TAB <- CR$Authors
      # TAB=data.frame(Authors=names(CR$Authors$Author), Citations=as.numeric(CR$Cited))
    }
  )
  values$TAB <- TAB
  res <- list(values = values, TAB = TAB)
  return(res)
}

AffiliationOverTime <- function(values, n) {
  if (!("AU_UN" %in% names(values$M))) {
    values$M <- metaTagExtraction(values$M, Field = "AU_UN")
  }
  AFF <- strsplit(values$M$AU_UN, ";")
  nAFF <- lengths(AFF)

  AFFY <- data.frame(
    Affiliation = unlist(AFF),
    Year = rep(values$M$PY, nAFF)
  ) %>%
    filter(Affiliation != "NA") %>%
    drop_na(Affiliation, Year) %>%
    group_by(Affiliation, Year) %>%
    count() %>%
    group_by(Affiliation) %>%
    arrange(Year) %>%
    ungroup() %>%
    pivot_wider(Affiliation, names_from = Year, values_from = n) %>%
    mutate_all(~ replace(., is.na(.), 0)) %>%
    pivot_longer(
      cols = !Affiliation,
      names_to = "Year",
      values_to = "Articles"
    ) %>%
    group_by(Affiliation) %>%
    mutate(Articles = cumsum(Articles))

  Affselected <- AFFY %>%
    filter(Year == max(Year)) %>%
    ungroup() %>%
    slice_max(Articles, n = n)

  values$AffOverTime <- AFFY %>%
    filter(Affiliation %in% Affselected$Affiliation) %>%
    mutate(Year = Year %>% as.numeric())

  Text <- paste(
    values$AffOverTime$Affiliation,
    " (",
    values$AffOverTime$Year,
    ") ",
    values$AffOverTime$Articles,
    sep = ""
  )
  width_scale <- 1.7 * 26 / length(unique(values$AffOverTime$Affiliation))
  x <- c(
    max(values$AffOverTime$Year) -
      0.02 -
      diff(range(values$AffOverTime$Year)) * 0.15,
    max(values$AffOverTime$Year) - 0.02
  ) +
    1
  y <- c(
    min(values$AffOverTime$Articles),
    min(values$AffOverTime$Articles) +
      diff(range(values$AffOverTime$Articles)) * 0.15
  )

  values$AffOverTimePlot <- ggplot(
    values$AffOverTime,
    aes(
      x = Year,
      y = Articles,
      group = Affiliation,
      color = Affiliation,
      text = Text
    )
  ) +
    geom_line() +
    labs(
      x = "Year",
      y = "Articles",
      title = "Affiliations' Production over Time"
    ) +
    scale_x_continuous(
      breaks = (values$AffOverTime$Year[seq(
        1,
        length(values$AffOverTime$Year),
        by = ceiling(length(values$AffOverTime$Year) / 20)
      )])
    ) +
    geom_hline(aes(yintercept = 0), alpha = 0.1) +
    labs(color = "Affiliation") +
    theme(
      text = element_text(color = "#444444"),
      legend.text = ggplot2::element_text(size = width_scale),
      legend.box.margin = margin(6, 6, 6, 6),
      legend.title = ggplot2::element_text(
        size = 1.5 * width_scale,
        face = "bold"
      ),
      legend.position = "bottom",
      legend.direction = "vertical",
      legend.key.size = grid::unit(width_scale / 50, "inch"),
      legend.key.width = grid::unit(width_scale / 50, "inch"),
      plot.caption = element_text(
        size = 9,
        hjust = 0.5,
        color = "black",
        face = "bold"
      ),
      panel.background = element_rect(fill = "#FFFFFF"),
      panel.grid.minor = element_line(color = "#EFEFEF"),
      panel.grid.major = element_line(color = "#EFEFEF"),
      plot.title = element_text(size = 24),
      axis.title = element_text(size = 14, color = "#555555"),
      axis.title.y = element_text(vjust = 1, angle = 90),
      axis.title.x = element_text(hjust = 0.95, angle = 0),
      axis.text.x = element_text(size = 10, angle = 90),
      axis.line.x = element_line(color = "black", linewidth = 0.5),
      axis.line.y = element_line(color = "black", linewidth = 0.5)
    ) +
    annotation_custom(
      values$logoGrid,
      xmin = x[1],
      xmax = x[2],
      ymin = y[1],
      ymax = y[2]
    )
  return(values)
}

CountryOverTime <- function(values, n) {
  if (!("AU_CO" %in% names(values$M))) {
    values$M <- metaTagExtraction(values$M, Field = "AU_CO")
  }
  AFF <- strsplit(values$M$AU_CO, ";")
  nAFF <- lengths(AFF)

  AFFY <- data.frame(
    Affiliation = unlist(AFF),
    Year = rep(values$M$PY, nAFF)
  ) %>%
    drop_na(Affiliation, Year) %>%
    group_by(Affiliation, Year) %>%
    count() %>%
    group_by(Affiliation) %>%
    arrange(Year) %>%
    ungroup() %>%
    pivot_wider(Affiliation, names_from = Year, values_from = n) %>%
    mutate_all(~ replace(., is.na(.), 0)) %>%
    pivot_longer(
      cols = !Affiliation,
      names_to = "Year",
      values_to = "Articles"
    ) %>%
    group_by(Affiliation) %>%
    mutate(Articles = cumsum(Articles))

  Affselected <- AFFY %>%
    filter(Year == max(Year)) %>%
    ungroup() %>%
    slice_max(Articles, n = n)

  values$CountryOverTime <- AFFY %>%
    filter(Affiliation %in% Affselected$Affiliation) %>%
    mutate(Year = Year %>% as.numeric()) %>%
    rename(Country = Affiliation)

  Text <- paste(
    values$CountryOverTime$Country,
    " (",
    values$CountryOverTime$Year,
    ") ",
    values$CountryOverTime$Articles,
    sep = ""
  )
  width_scale <- 1.7 * 26 / length(unique(values$CountryOverTime$Country))
  x <- c(
    max(values$CountryOverTime$Year) -
      0.02 -
      diff(range(values$CountryOverTime$Year)) * 0.15,
    max(values$CountryOverTime$Year) - 0.02
  ) +
    1
  y <- c(
    min(values$CountryOverTime$Articles),
    min(values$CountryOverTime$Articles) +
      diff(range(values$CountryOverTime$Articles)) * 0.15
  )

  values$CountryOverTimePlot <- ggplot(
    values$CountryOverTime,
    aes(x = Year, y = Articles, group = Country, color = Country, text = Text)
  ) +
    geom_line() +
    labs(
      x = "Year",
      y = "Articles",
      title = "Country Production over Time"
    ) +
    scale_x_continuous(
      breaks = (values$CountryOverTime$Year[seq(
        1,
        length(values$CountryOverTime$Year),
        by = ceiling(length(values$CountryOverTime$Year) / 20)
      )])
    ) +
    geom_hline(aes(yintercept = 0), alpha = 0.1) +
    labs(color = "Country") +
    theme(
      text = element_text(color = "#444444"),
      legend.text = ggplot2::element_text(size = width_scale),
      legend.box.margin = margin(6, 6, 6, 6),
      legend.title = ggplot2::element_text(
        size = 1.5 * width_scale,
        face = "bold"
      ),
      legend.position = "bottom",
      legend.direction = "vertical",
      legend.key.size = grid::unit(width_scale / 50, "inch"),
      legend.key.width = grid::unit(width_scale / 50, "inch"),
      plot.caption = element_text(
        size = 9,
        hjust = 0.5,
        color = "black",
        face = "bold"
      ),
      panel.background = element_rect(fill = "#FFFFFF"),
      panel.grid.minor = element_line(color = "#EFEFEF"),
      panel.grid.major = element_line(color = "#EFEFEF"),
      plot.title = element_text(size = 24),
      axis.title = element_text(size = 14, color = "#555555"),
      axis.title.y = element_text(vjust = 1, angle = 90),
      axis.title.x = element_text(hjust = 0.95, angle = 0),
      axis.text.x = element_text(size = 10, angle = 90),
      axis.line.x = element_line(color = "black", linewidth = 0.5),
      axis.line.y = element_line(color = "black", linewidth = 0.5)
    ) +
    annotation_custom(
      values$logoGrid,
      xmin = x[1],
      xmax = x[2],
      ymin = y[1],
      ymax = y[2]
    )
  return(values)
}

wordlist <- function(
  M,
  Field,
  n,
  measure,
  ngrams,
  remove.terms = NULL,
  synonyms = NULL
) {
  switch(
    Field,
    ID = {
      v <- tableTag(M, "ID", remove.terms = remove.terms, synonyms = synonyms)
    },
    DE = {
      v <- tableTag(M, "DE", remove.terms = remove.terms, synonyms = synonyms)
    },
    KW_Merged = {
      v <- tableTag(
        M,
        "KW_Merged",
        remove.terms = remove.terms,
        synonyms = synonyms
      )
    },
    TI = {
      if (!("TI_TM" %in% names(M))) {
        v <- tableTag(
          M,
          "TI",
          ngrams = ngrams,
          remove.terms = remove.terms,
          synonyms = synonyms
        )
      }
    },
    AB = {
      if (!("AB_TM" %in% names(M))) {
        v <- tableTag(
          M,
          "AB",
          ngrams = ngrams,
          remove.terms = remove.terms,
          synonyms = synonyms
        )
      }
    },
    WC = {
      v <- tableTag(M, "WC")
    }
  )
  names(v) <- tolower(names(v))
  # v=tableTag(values$M,"ID")
  n <- min(c(n, length(v)))
  Words <- data.frame(
    Terms = names(v)[1:n],
    Frequency = (as.numeric(v)[1:n]),
    stringsAsFactors = FALSE
  )
  W <- Words
  switch(
    measure,
    identity = {},
    sqrt = {
      W$Frequency <- sqrt(W$Frequency)
    },
    log = {
      W$Frequency <- log(W$Frequency + 1)
    },
    log10 = {
      W$Frequency <- log10(W$Frequency + 1)
    }
  )

  results <- list(v = v, W = W, Words = Words)
  return(results)
}

readStopwordsFile <- function(file, sep = ",") {
  if (!is.null(file)) {
    req(file$datapath)
    remove.terms <- unlist(strsplit(readr::read_lines(file$datapath), sep))
  } else {
    remove.terms <- NULL
  }
  return(remove.terms)
}

readSynWordsFile <- function(file, sep = ",") {
  if (!is.null(file)) {
    req(file$datapath)
    syn.terms <- readr::read_lines(file$datapath)
    if (sep != ";") syn.terms <- gsub(sep, ";", syn.terms)
  } else {
    syn.terms <- NULL
  }
  return(syn.terms)
}

mapworld <- function(M, values) {
  if (!("AU_CO" %in% names(M))) {
    M <- metaTagExtraction(M, "AU_CO")
  }
  CO <- as.data.frame(tableTag(M, "AU_CO"))
  CO$Tab <- gsub("[[:digit:]]", "", CO$Tab)
  CO$Tab <- gsub(".", "", CO$Tab, fixed = TRUE)
  CO$Tab <- gsub(";;", ";", CO$Tab, fixed = TRUE)
  CO$Tab <- gsub("UNITED STATES", "USA", CO$Tab)
  CO$Tab <- gsub("RUSSIAN FEDERATION", "RUSSIA", CO$Tab)
  CO$Tab <- gsub("TAIWAN", "CHINA", CO$Tab)
  CO$Tab <- gsub("ENGLAND", "UNITED KINGDOM", CO$Tab)
  CO$Tab <- gsub("SCOTLAND", "UNITED KINGDOM", CO$Tab)
  CO$Tab <- gsub("WALES", "UNITED KINGDOM", CO$Tab)
  CO$Tab <- gsub("NORTH IRELAND", "UNITED KINGDOM", CO$Tab)
  CO$Tab <- gsub("UNITED KINGDOM", "UK", CO$Tab)
  CO$Tab <- gsub("KOREA", "SOUTH KOREA", CO$Tab)

  map.world <- map_data("world")
  map.world$region <- toupper(map.world$region)

  # dplyr::anti_join(CO, map.world, by = c('Tab' = 'region'))

  country.prod <- dplyr::left_join(map.world, CO, by = c("region" = "Tab"))

  tab <- data.frame(
    country.prod %>%
      dplyr::group_by(region) %>%
      dplyr::summarise(Freq = mean(Freq))
  )

  tab <- tab[!is.na(tab$Freq), ]

  tab <- tab[order(-tab$Freq), ]

  # breaks=as.numeric(round(quantile(CO$Freq,c(0.2,0.4,0.6,0.8,1))))
  # names(breaks)=breaks
  # breaks=log(breaks)
  breaks <- as.numeric(cut(CO$Freq, breaks = 10))
  names(breaks) <- breaks

  g <- ggplot(
    country.prod,
    aes(
      x = long,
      y = lat,
      group = group,
      text = paste("Country: ", region, "\nN.of Documents: ", Freq)
    )
  ) +
    geom_polygon(aes(fill = Freq, group = group)) +
    scale_fill_continuous(
      low = "#87CEEB",
      high = "dodgerblue4",
      breaks = breaks,
      na.value = "grey80"
    ) +
    guides(fill = guide_legend(reverse = T)) +
    # geom_text(data=centroids, aes(label = centroids$Tab, x = centroids$long, y = centroids$lat, group=centroids$Tab)) +
    labs(
      fill = "N.Documents",
      title = "Country Scientific Production",
      x = NULL,
      y = NULL
    ) +
    theme(
      text = element_text(color = "#333333"),
      plot.title = element_text(size = 28),
      plot.subtitle = element_text(size = 14),
      axis.ticks = element_blank(),
      axis.text = element_blank(),
      panel.grid = element_blank(),
      panel.background = element_rect(fill = "#FFFFFF"), #' #333333'
      plot.background = element_rect(fill = "#FFFFFF"),
      legend.position = "none"
      # ,legend.background = element_blank()
      # ,legend.key = element_blank()
    ) +
    annotation_custom(
      values$logoGrid,
      xmin = 143,
      xmax = 189.5,
      ymin = -69,
      ymax = -48
    )

  results <- list(g = g, tab = tab)
  return(results)
}

### Structure fuctions ----
CAmap <- function(input, values) {
  if ((input$CSfield %in% names(values$M))) {
    if (input$CSfield %in% c("TI", "AB")) {
      ngrams <- as.numeric(input$CSngrams)
    } else {
      ngrams <- 1
    }

    ### load file with terms to remove
    if (input$CSStopFile == "Y") {
      remove.terms <- trimws(values$CSremove.terms$stopword)
    } else {
      remove.terms <- NULL
    }
    # values$CSremove.terms <- remove.terms
    ### end of block
    ### load file with synonyms
    if (input$FASynFile == "Y") {
      synonyms <- values$FAsyn.terms %>%
        group_by(term) %>%
        mutate(term = paste0(term, ";", synonyms)) %>%
        select(term)
      synonyms <- synonyms$term
    } else {
      synonyms <- NULL
    }
    # values$FAsyn.terms <- synonyms
    ### end of block

    tab <- tableTag(values$M, input$CSfield, ngrams = ngrams)
    if (length(tab >= 2)) {
      minDegree <- as.numeric(tab[input$CSn])

      values$CS <- conceptualStructure(
        values$M,
        method = input$method,
        field = input$CSfield,
        minDegree = minDegree,
        clust = input$nClustersCS,
        k.max = 8,
        stemming = F,
        labelsize = input$CSlabelsize / 2,
        documents = input$CSdoc,
        graph = FALSE,
        ngrams = ngrams,
        remove.terms = remove.terms,
        synonyms = synonyms
      )
      if (input$method != "MDS") {
        CSData <- values$CS$docCoord
        CSData <- data.frame(Documents = row.names(CSData), CSData)
        CSData$dim1 <- round(CSData$dim1, 2)
        CSData$dim2 <- round(CSData$dim2, 2)
        CSData$contrib <- round(CSData$contrib, 2)
        values$CS$CSData <- CSData
      } else {
        values$CS$CSData <- data.frame(Docuemnts = NA, dim1 = NA, dim2 = NA)
      }

      switch(
        input$method,
        CA = {
          WData <- data.frame(
            word = row.names(values$CS$km.res$data.clust),
            values$CS$km.res$data.clust,
            stringsAsFactors = FALSE
          )
          names(WData)[4] <- "cluster"
        },
        MCA = {
          WData <- data.frame(
            word = row.names(values$CS$km.res$data.clust),
            values$CS$km.res$data.clust,
            stringsAsFactors = FALSE
          )
          names(WData)[4] <- "cluster"
        },
        MDS = {
          WData <- data.frame(
            word = row.names(values$CS$res),
            values$CS$res,
            cluster = values$CS$km.res$cluster
          )
        }
      )

      WData$Dim1 <- round(WData$Dim1, 2)
      WData$Dim2 <- round(WData$Dim2, 2)
      values$CS$WData <- WData
    } else {
      emptyPlot("Selected field is not included in your data collection")
      values$CS <- list("NA")
    }
  } else {
    emptyPlot("Selected field is not included in your data collection")
    values$CS <- list("NA")
  }
  return(values)
}

historiograph <- function(input, values) {
  min.cit <- 0

  # if (values$Histfield=="NA"){
  values$histResults <- histNetwork(
    values$M,
    min.citations = min.cit,
    sep = ";"
  )
  # values$Histfield="done"
  # }
  values$histResults$histData <- values$histResults$histData %>%
    tibble::rownames_to_column(var = "SR")
  # titlelabel <- input$titlelabel
  values$histlog <- (values$histPlot <- histPlot(
    values$histResults,
    n = input$histNodes,
    size = input$histsize,
    remove.isolates = (input$hist.isolates == "yes"),
    labelsize = input$histlabelsize,
    label = input$titlelabel,
    verbose = FALSE
  ))

  values$histResults$histData$DOI <- paste0(
    '<a href=\"https://doi.org/',
    values$histResults$histData$DOI,
    '\" target=\"_blank\">',
    values$histResults$histData$DOI,
    "</a>"
  )

  values$histResults$histData <- values$histResults$histData %>%
    left_join(
      values$histPlot$layout %>%
        select(name, color),
      by = c("Paper" = "name")
    ) %>%
    drop_na(color) %>%
    mutate(cluster = match(color, unique(color))) %>%
    select(!color) %>%
    group_by(cluster) %>%
    arrange(Year, .by_group = TRUE)

  return(values)
}


### Network functions ----
degreePlot <- function(net) {
  # deg <- data.frame(node = names(net$nodeDegree), x= (1:length(net$nodeDegree)), y = net$nodeDegree)
  ma <- function(x, n = 5) {
    stats::filter(x, rep(1 / n, n), sides = 1)
  }

  deg <- net$nodeDegree %>%
    mutate(x = row_number())

  p <- ggplot(
    data = deg,
    aes(
      x = x,
      y = degree,
      text = paste(node, " - Degree ", round(degree, 3), sep = "")
    )
  ) +
    geom_point() +
    geom_line(aes(group = "NA"), color = "#002F80", alpha = .5) +
    # geom_hline(yintercept=cutting$degree, linetype="dashed",color = '#002F80', alpha = .5)+
    theme(
      text = element_text(color = "#444444"),
      panel.background = element_rect(fill = "#FFFFFF"),
      panel.grid.minor = element_line(color = "#EFEFEF"),
      panel.grid.major = element_line(color = "#EFEFEF"),
      plot.title = element_text(size = 24),
      axis.title = element_text(size = 14, color = "#555555"),
      axis.title.y = element_text(vjust = 1, angle = 0),
      axis.title.x = element_text(hjust = 0),
      axis.line.x = element_line(color = "black", linewidth = 0.5),
      axis.line.y = element_line(color = "black", linewidth = 0.5)
    ) +
    labs(x = "Node", y = "Cumulative Degree", title = "Node Degrees")
  return(p)
}

# Associate a Year to each Keyword
keywords2Years <- function(M, field = "DE", n = 100) {
  suppressMessages(Y <- KeywordGrowth(M, Tag = field, top = Inf, cdf = FALSE))

  ## Normalize data and exclude tot from normalization
  df <- Y %>%
    rowwise() %>%
    mutate(year_freq = sum(c_across(!matches("Year")))) %>%
    mutate(across(!matches("Year"), ~ .x / year_freq)) %>%
    mutate(across(!matches("Year"), ~ replace_na(.x, 0)))

  df_long <- df %>%
    pivot_longer(
      cols = -c(Year, year_freq),
      names_to = "Keyword",
      values_to = "Probability"
    )

  df_max_year <- df_long %>%
    group_by(Keyword) %>%
    filter(Probability == max(Probability)) %>%
    slice_max(order_by = year_freq, n = 1, with_ties = FALSE) %>%
    ungroup()

  return(df_max_year)
}

cocNetwork <- function(input, values) {
  n <- input$Nodes
  label.n <- input$Labels

  ### load file with terms to remove
  if (input$COCStopFile == "Y") {
    remove.terms <- trimws(values$COCremove.terms$stopword)
  } else {
    remove.terms <- NULL
  }
  # values$COCremove.terms <- remove.terms
  ### end of block
  ### load file with synonyms
  if (input$COCSynFile == "Y") {
    synonyms <- values$COCsyn.terms %>%
      group_by(term) %>%
      mutate(term = paste0(term, ";", synonyms)) %>%
      select(term)
    synonyms <- synonyms$term
  } else {
    synonyms <- NULL
  }
  # values$COCsyn.terms <- synonyms
  ### end of block

  if ((input$field %in% names(values$M))) {
    if (
      (dim(values$NetWords)[1]) == 1 |
        !(input$field == values$field) |
        !(input$cocngrams == values$cocngrams) |
        ((dim(values$NetWords)[1]) != input$Nodes)
    ) {
      values$field <- input$field
      values$ngrams <- input$cocngrams

      switch(
        input$field,
        ID = {
          values$NetWords <- biblioNetwork(
            values$M,
            analysis = "co-occurrences",
            network = "keywords",
            n = n,
            sep = ";",
            remove.terms = remove.terms,
            synonyms = synonyms
          )
          values$Title <- "Keywords Plus Network"
        },
        DE = {
          values$NetWords <- biblioNetwork(
            values$M,
            analysis = "co-occurrences",
            network = "author_keywords",
            n = n,
            sep = ";",
            remove.terms = remove.terms,
            synonyms = synonyms
          )
          values$Title <- "Authors' Keywords network"
        },
        KW_Merged = {
          values$NetWords <- biblioNetwork(
            values$M,
            analysis = "co-occurrences",
            network = "all_keywords",
            n = n,
            sep = ";",
            remove.terms = remove.terms,
            synonyms = synonyms
          )
          values$Title <- "All Keywords network"
        },
        TI = {
          # if(!("TI_TM" %in% names(values$M))){
          values$M <- termExtraction(
            values$M,
            Field = "TI",
            verbose = FALSE,
            ngrams = as.numeric(input$cocngrams),
            remove.terms = remove.terms,
            synonyms = synonyms
          )
          # }
          values$NetWords <- biblioNetwork(
            values$M,
            analysis = "co-occurrences",
            network = "titles",
            n = n,
            sep = ";"
          )
          values$Title <- "Title Words network"
        },
        AB = {
          # if(!("AB_TM" %in% names(values$M))){
          values$M <- termExtraction(
            values$M,
            Field = "AB",
            verbose = FALSE,
            ngrams = as.numeric(input$cocngrams),
            remove.terms = remove.terms,
            synonyms = synonyms
          )
          # }
          values$NetWords <- biblioNetwork(
            values$M,
            analysis = "co-occurrences",
            network = "abstracts",
            n = n,
            sep = ";"
          )
          values$Title <- "Abstract Words network"
        },
        WC = {
          WSC <- cocMatrix(values$M, Field = "WC", binary = FALSE)
          values$NetWords <- crossprod(WSC, WSC)
          values$Title <- "Subject Categories network"
        }
      )
    }

    if (label.n > n) {
      label.n <- n
    }
    if (input$normalize == "none") {
      normalize <- NULL
    } else {
      normalize <- input$normalize
    }
    if (input$label.cex == "Yes") {
      label.cex <- TRUE
    } else {
      label.cex <- FALSE
    }
    if (input$coc.curved == "Yes") {
      curved <- TRUE
    } else {
      curved <- FALSE
    }

    values$cocnet <- networkPlot(
      values$NetWords,
      normalize = normalize,
      Title = values$Title,
      type = input$layout,
      size.cex = TRUE,
      size = 5,
      remove.multiple = F,
      edgesize = input$edgesize * 3,
      labelsize = input$labelsize,
      label.cex = label.cex,
      label.n = label.n,
      edges.min = input$edges.min,
      label.color = F,
      curved = curved,
      alpha = input$cocAlpha,
      cluster = input$cocCluster,
      remove.isolates = (input$coc.isolates == "yes"),
      community.repulsion = input$coc.repulsion / 2,
      seed = values$random_seed,
      verbose = FALSE
    )

    g <- values$cocnet$graph
    Y <- keywords2Years(values$M, field = input$field, n = Inf)
    label <- data.frame(Keyword = igraph::V(g)$name)
    df <- label %>%
      left_join(Y %>% mutate(Keyword = tolower(Keyword)), by = "Keyword") %>%
      rename(year_med = Year)
    igraph::V(g)$year_med <- df$year_med

    if (input$cocyears == "Yes") {
      col <- hcl.colors(
        (diff(range(df$year_med)) + 1) * 10,
        palette = "Blues 3"
      )
      igraph::V(g)$color <- col[(max(df$year_med) - df$year_med + 1) * 10]
    }
    values$cocnet$graph <- g
  } else {
    emptyPlot("Selected field is not included in your data collection")
  }
  return(values)
}

intellectualStructure <- function(input, values) {
  n <- input$citNodes
  label.n <- input$citLabels

  if (
    (dim(values$NetRefs)[1]) == 1 |
      !(input$citField == values$citField) |
      !(input$citSep == values$citSep) |
      !(input$citShortlabel == values$citShortlabel) |
      ((dim(values$NetRefs)[1]) != input$citNodes)
  ) {
    values$citField <- input$citField
    values$citSep <- input$citSep
    if (input$citShortlabel == "Yes") {
      shortlabel <- TRUE
    } else {
      shortlabel <- FALSE
    }
    values$citShortlabel <- input$citShortlabel
    switch(
      input$citField,
      CR = {
        values$NetRefs <- biblioNetwork(
          values$M,
          analysis = "co-citation",
          network = "references",
          n = n,
          sep = input$citSep,
          shortlabel = shortlabel
        )
        values$Title <- "Cited References network"
      },
      CR_AU = {
        if (!("CR_AU" %in% names(values$M))) {
          values$M <- metaTagExtraction(
            values$M,
            Field = "CR_AU",
            sep = input$citSep
          )
        }
        values$NetRefs <- biblioNetwork(
          values$M,
          analysis = "co-citation",
          network = "authors",
          n = n,
          sep = input$citSep
        )
        values$Title <- "Cited Authors network"
      },
      CR_SO = {
        if (!("CR_SO" %in% names(values$M))) {
          values$M <- metaTagExtraction(
            values$M,
            Field = "CR_SO",
            sep = input$citSep
          )
        }
        values$NetRefs <- biblioNetwork(
          values$M,
          analysis = "co-citation",
          network = "sources",
          n = n,
          sep = input$citSep
        )
        values$Title <- "Cited Sources network"
      }
    )
  }

  if (label.n > n) {
    label.n <- n
  }
  if (input$citlabel.cex == "Yes") {
    label.cex <- TRUE
  } else {
    label.cex <- FALSE
  }
  if (input$cocit.curved == "Yes") {
    curved <- TRUE
  } else {
    curved <- FALSE
  }

  values$cocitnet <- networkPlot(
    values$NetRefs,
    normalize = NULL,
    Title = values$Title,
    type = input$citlayout,
    size.cex = TRUE,
    size = 5,
    remove.multiple = F,
    edgesize = input$citedgesize * 3,
    labelsize = input$citlabelsize,
    label.cex = label.cex,
    curved = curved,
    label.n = label.n,
    edges.min = input$citedges.min,
    label.color = F,
    remove.isolates = (input$cit.isolates == "yes"),
    alpha = 0.7,
    cluster = input$cocitCluster,
    community.repulsion = input$cocit.repulsion / 2,
    verbose = FALSE
  )
  return(values)
}

authors2Years <- function(M, field = "AU") {
  WAU <- cocMatrix(M, field)
  WPY <- cocMatrix(M, "PY")
  B <- crossprod(WPY, WAU) %>%
    as.matrix() %>%
    as.data.frame() %>%
    tibble::rownames_to_column("Year")

  # create a data frame that startig from B associate at each author the year of the first non zero value
  C <- B %>%
    pivot_longer(-Year, names_to = "Item", values_to = "Value") %>%
    filter(Value > 0) %>%
    group_by(Item) %>%
    summarise(FirstYear = min(Year)) %>%
    ungroup()

  return(C)
}

socialStructure <- function(input, values) {
  n <- input$colNodes
  label.n <- input$colLabels

  if (
    (dim(values$ColNetRefs)[1]) == 1 |
      !(input$colField == values$colField) |
      ((dim(values$ColNetRefs)[1]) != input$colNodes)
  ) {
    values$colField <- input$colField

    if (!"nAU" %in% names(values$M)) {
      values$M$nAU <- str_count(values$M$AU, ";") + 1
    }

    switch(
      input$colField,
      COL_AU = {
        if (input$col.filterMaxAuthors) {
          M_AU <- values$M %>% filter(nAU <= 20)
        } else {
          M_AU <- values$M
        }
        values$ColNetRefs <- biblioNetwork(
          M_AU,
          analysis = "collaboration",
          network = "authors",
          n = n,
          sep = ";"
        )
        values$Title <- "Author Collaboration network"
        values$fieldCOL <- "AU"
      },
      COL_UN = {
        if (!("AU_UN" %in% names(values$M))) {
          values$M <- metaTagExtraction(values$M, Field = "AU_UN", sep = ";")
        }
        values$ColNetRefs <- biblioNetwork(
          values$M,
          analysis = "collaboration",
          network = "universities",
          n = n,
          sep = ";"
        )
        values$Title <- "Edu Collaboration network"
        values$fieldCOL <- "AU_UN"
      },
      COL_CO = {
        if (!("AU_CO" %in% names(values$M))) {
          values$M <- metaTagExtraction(values$M, Field = "AU_CO", sep = ";")
        }
        values$ColNetRefs <- biblioNetwork(
          values$M,
          analysis = "collaboration",
          network = "countries",
          n = n,
          sep = ";"
        )
        values$Title <- "Country Collaboration network"
        values$fieldCOL <- "AU_CO"
        # values$cluster="none"
      }
    )
  }

  if (label.n > n) {
    label.n <- n
  }
  if (input$colnormalize == "none") {
    normalize <- NULL
  } else {
    normalize <- input$colnormalize
  }
  if (input$collabel.cex == "Yes") {
    label.cex <- TRUE
  } else {
    label.cex <- FALSE
  }
  if (input$soc.curved == "Yes") {
    curved <- TRUE
  } else {
    curved <- FALSE
  }

  type <- input$collayout
  if (input$collayout == "worldmap") {
    type <- "auto"
  }

  values$colnet <- networkPlot(
    values$ColNetRefs,
    normalize = normalize,
    Title = values$Title,
    type = type,
    size.cex = TRUE,
    size = 5,
    remove.multiple = F,
    edgesize = input$coledgesize * 3,
    labelsize = input$collabelsize,
    label.cex = label.cex,
    curved = curved,
    label.n = label.n,
    edges.min = input$coledges.min,
    label.color = F,
    alpha = input$colAlpha,
    remove.isolates = (input$col.isolates == "yes"),
    cluster = input$colCluster,
    community.repulsion = input$col.repulsion / 2,
    verbose = FALSE
  )

  g <- values$colnet$graph
  Y <- authors2Years(values$M, values$fieldCOL)
  label <- data.frame(Item = igraph::V(g)$name)
  df <- label %>%
    left_join(Y %>% mutate(Item = tolower(Item)), by = "Item") %>%
    rename(year_med = FirstYear)
  igraph::V(g)$year_med <- df$year_med

  values$colnet$graph <- g

  return(values)
}

countrycollaboration <- function(M, label, edgesize, min.edges, values) {
  M <- metaTagExtraction(M, "AU_CO")
  net <- biblioNetwork(M, analysis = "collaboration", network = "countries")
  CO <- data.frame(Tab = rownames(net), Freq = diag(net))
  bsk.network <- igraph::graph_from_adjacency_matrix(net, mode = "undirected")
  COedges <- as.data.frame(igraph::ends(
    bsk.network,
    igraph::E(bsk.network),
    names = TRUE
  ))

  map.world <- map_data("world")
  map.world$region <- toupper(map.world$region)
  map.world$region <- gsub("^UK$", "UNITED KINGDOM", map.world$region)
  map.world$region <- gsub("^SOUTH KOREA$", "KOREA", map.world$region)

  country.prod <- dplyr::left_join(map.world, CO, by = c("region" = "Tab"))

  # breaks <- as.numeric(round(quantile(CO$Freq,seq(0.1,1,by=0.1))))
  breaks <- as.numeric(cut(CO$Freq, breaks = 10))
  names(breaks) <- breaks
  # breaks=breaks
  data("countries", envir = environment())
  names(countries)[1] <- "Tab"

  COedges <- dplyr::inner_join(COedges, countries, by = c("V1" = "Tab"))
  COedges <- dplyr::inner_join(COedges, countries, by = c("V2" = "Tab"))
  COedges <- COedges[COedges$V1 != COedges$V2, ]
  COedges <- count.duplicates(COedges)
  tab <- COedges
  COedges <- COedges[COedges$count >= min.edges, ]
  COedges$region <- paste(
    "\nCollaboration between\n",
    COedges$V1,
    "\n and \n",
    COedges$V2
  )

  g <- ggplot(
    country.prod,
    aes(x = long, y = lat, group = group, text = paste("Country: ", region))
  ) +
    geom_polygon(aes(fill = Freq)) +
    scale_fill_continuous(
      low = "#87CEEB",
      high = "dodgerblue4",
      breaks = breaks,
      na.value = "grey80"
    ) +
    # guides(fill = guide_legend(reverse = T)) +
    guides(colour = FALSE, fill = FALSE) +
    # geom_curve(data=COedges, aes(x = Longitude.x , y = Latitude.x, xend = Longitude.y, yend = Latitude.y,     # draw edges as arcs
    #                              color = "firebrick4", size = count, group=continent.x),
    #            curvature = 0.33,
    #            alpha = 0.5) +
    geom_segment(
      data = COedges,
      aes(
        x = Longitude.x,
        y = Latitude.x,
        xend = Longitude.y,
        yend = Latitude.y, # draw edges as arcs
        size = count,
        group = continent.x
      ),
      color = "orangered4", # FFB347",
      # curvature = 0.33,
      alpha = 0.3
    ) +
    scale_size_continuous(guide = FALSE, range = c(0.25, edgesize)) +
    labs(title = NULL, x = "Latitude", y = "Longitude") +
    theme(
      text = element_text(color = "#333333"),
      plot.title = element_text(size = 28),
      plot.subtitle = element_text(size = 14),
      axis.ticks = element_blank(),
      axis.text = element_blank(),
      panel.grid = element_blank(),
      panel.background = element_rect(fill = "#FFFFFF"), #' #333333'
      plot.background = element_rect(fill = "#FFFFFF"),
      legend.position = c(.18, .36),
      legend.background = element_blank(),
      legend.key = element_blank()
    ) +
    annotation_custom(
      values$logoGrid,
      xmin = 143,
      xmax = 189.5,
      ymin = -69,
      ymax = -48
    )
  if (isTRUE(label)) {
    CO <- dplyr::inner_join(CO, countries, by = c("Tab" = "Tab"))
    g <- g +
      # ggrepel::geom_text_repel(data=CO, aes(x = Longitude, y = Latitude, label = Tab, group=continent),             # draw text labels
      #                          hjust = 0, nudge_x = 1, nudge_y = 4,
      #                          size = 3, color = "orange", fontface = "bold")
      ggrepel::geom_text(
        data = CO,
        aes(x = Longitude, y = Latitude, label = Tab, group = continent), # draw text labels
        hjust = 0,
        nudge_x = 1,
        nudge_y = 4,
        size = 3,
        color = "orange",
        fontface = "bold"
      )
  }

  results <- list(g = g, tab = tab)
  return(results)
}
### visNetwork tools ----
netLayout <- function(type) {
  switch(
    type,
    auto = {
      l <- "layout_nicely"
    },
    circle = {
      l <- "layout_in_circle"
    },
    mds = {
      l <- "layout_with_mds"
    },
    star = {
      l <- "layout_as_star"
    },
    sphere = {
      l <- "layout_on_sphere"
    },
    fruchterman = {
      l <- "layout_with_fr"
    },
    kamada = {
      l <- "layout_with_kk"
    }
  )
  return(l)
}

savenetwork <- function(con, VIS) {
  VIS %>%
    visOptions(height = "800px") %>%
    visNetwork::visSave(con)
}

igraph2vis <- function(
  g,
  curved,
  labelsize,
  opacity,
  type,
  shape,
  net,
  shadow = TRUE,
  edgesize = 5,
  noOverlap = TRUE
) {
  LABEL <- igraph::V(g)$name

  LABEL[igraph::V(g)$labelsize == 0] <- ""

  vn <- visNetwork::toVisNetworkData(g)

  vn$nodes$label <- LABEL
  vn$edges$num <- 1
  vn$edges$dashes <- FALSE
  vn$edges$dashes[vn$edges$lty == 2] <- TRUE

  ## opacity
  vn$nodes$color <- adjustcolor(vn$nodes$color, alpha.f = min(c(opacity, 1)))
  ## set a darkest gray for iter-cluster edges
  vn$edges$color <- paste(substr(vn$edges$color, 1, 7), "90", sep = "")
  vn$edges$color[substr(vn$edges$color, 1, 7) == "#B3B3B3"] <- "#69696960"
  vn$edges$color <- adjustcolor(vn$edges$color, alpha.f = opacity)

  ## removing multiple edges
  vn$edges <- unique(vn$edges)

  vn$edges$width <- vn$edges$width^2 / (max(vn$edges$width^2)) * (10 + edgesize)

  # if (edgesize==0){
  #   vn$edges$hidden <- TRUE
  #   }else{vn$edges$hidden <- FALSE}

  ## labelsize
  vn$nodes$font.size <- vn$nodes$deg
  scalemin <- 20
  scalemax <- 150
  Min <- min(vn$nodes$font.size)
  Max <- max(vn$nodes$font.size)
  if (Max > Min) {
    size <- (vn$nodes$font.size - Min) / (Max - Min) * 15 * labelsize + 10
  } else {
    size <- 10 * labelsize
  }
  size[size < scalemin] <- scalemin
  size[size > scalemax] <- scalemax
  vn$nodes$font.size <- size
  l <- netLayout(type)

  ### TO ADD SHAPE AND FONT COLOR OPTIONS
  coords <- net$layout

  vn$nodes$size <- vn$nodes$font.size * 0.7

  # vn$nodes$font.color <- adjustcolor("black", alpha.f = min(c(opacity,1)))

  if (shape %in% c("dot", "square")) {
    vn$nodes$font.vadjust <- -0.7 * vn$nodes$font.size
  } else {
    vn$nodes$font.vadjust <- 0
  }

  opacity_font <- sqrt(
    (vn$nodes$font.size - min(vn$nodes$font.size)) /
      diff(range(vn$nodes$font.size))
  ) *
    opacity +
    0.3
  if (is.nan(opacity_font[1])) {
    opacity_font <- rep(0.3, length(opacity_font))
  }

  if (labelsize > 0) {
    vn$nodes$font.color <- unlist(lapply(opacity_font, function(x) {
      adjustcolor("black", alpha.f = x)
    }))
  } else {
    vn$nodes$font.color <- adjustcolor("black", alpha.f = 0)
  }
  ## avoid label overlaps
  if (noOverlap) {
    threshold <- 0.05
    ymax <- diff(range(coords[, 2]))
    xmax <- diff(range(coords[, 1]))
    threshold2 <- threshold * mean(xmax, ymax)
    w <- data.frame(
      x = coords[, 1],
      y = coords[, 2],
      labelToPlot = vn$nodes$label,
      dotSize = size,
      row.names = vn$nodes$label
    )
    labelToRemove <- avoidNetOverlaps(w, threshold = threshold2)
  } else {
    labelToRemove <- ""
  }

  vn$nodes <- vn$nodes %>%
    mutate(
      label = ifelse(label %in% labelToRemove, "", label),
      title = id
    )
  ##

  VIS <-
    visNetwork::visNetwork(
      nodes = vn$nodes,
      edges = vn$edges,
      type = "full",
      smooth = TRUE,
      physics = FALSE
    ) %>%
    visNetwork::visNodes(
      shadow = shadow,
      shape = shape,
      font = list(
        color = vn$nodes$font.color,
        size = vn$nodes$font.size,
        vadjust = vn$nodes$font.vadjust
      )
    ) %>%
    visNetwork::visIgraphLayout(
      layout = "layout.norm",
      layoutMatrix = coords,
      type = "full"
    ) %>%
    visNetwork::visEdges(smooth = list(type = "horizontal")) %>%
    visNetwork::visOptions(
      highlightNearest = list(enabled = T, hover = T, degree = 1),
      nodesIdSelection = T
    ) %>%
    visNetwork::visInteraction(
      dragNodes = TRUE,
      navigationButtons = F,
      hideEdgesOnDrag = TRUE,
      zoomSpeed = 0.4
    ) %>%
    visNetwork::visOptions(
      manipulation = curved,
      height = "100%",
      width = "100%"
    )

  return(list(VIS = VIS, vn = vn, type = type, l = l, curved = curved))
}

## function to avoid label overlapping ----
avoidNetOverlaps <- function(w, threshold = 0.10) {
  w[, 2] <- w[, 2] / 2

  Ds <- dist(
    w %>%
      dplyr::filter(labelToPlot != "") %>%
      select(1:2),
    method = "manhattan",
    upper = T
  ) %>%
    dist2df() %>%
    rename(
      from = row,
      to = col,
      dist = value
    ) %>%
    left_join(
      w %>% dplyr::filter(labelToPlot != "") %>% select(labelToPlot, dotSize),
      by = c("from" = "labelToPlot")
    ) %>%
    rename(w_from = dotSize) %>%
    left_join(
      w %>% dplyr::filter(labelToPlot != "") %>% select(labelToPlot, dotSize),
      by = c("to" = "labelToPlot")
    ) %>%
    rename(w_to = dotSize) %>%
    filter(dist < threshold)

  if (nrow(Ds) > 0) {
    st <- TRUE
    i <- 1
    label <- NULL
    case <- "n"

    while (isTRUE(st)) {
      if (Ds$w_from[i] > Ds$w_to[i] & Ds$dist[i] < threshold) {
        case <- "y"
        lab <- Ds$to[i]
      } else if (Ds$w_from[i] <= Ds$w_to[i] & Ds$dist[i] < threshold) {
        case <- "y"
        lab <- Ds$from[i]
      }

      switch(
        case,
        "y" = {
          Ds <- Ds[Ds$from != lab, ]
          Ds <- Ds[Ds$to != lab, ]
          label <- c(label, lab)
        },
        "n" = {
          Ds <- Ds[-1, ]
        }
      )

      if (i >= nrow(Ds)) {
        st <- FALSE
      }
      case <- "n"
      # print(nrow(Ds))
    }
  } else {
    label <- NULL
  }
  label
}


## visnetwork for subgraphs
igraph2visClust <- function(
  g,
  curved = FALSE,
  labelsize = 3,
  opacity = 0.7,
  shape = "dot",
  shadow = TRUE,
  edgesize = 5
) {
  LABEL <- igraph::V(g)$name

  LABEL[igraph::V(g)$labelsize == 0] <- ""

  vn <- visNetwork::toVisNetworkData(g)

  vn$nodes$label <- LABEL
  vn$edges$num <- 1
  vn$edges$dashes <- FALSE
  vn$edges$dashes[vn$edges$lty == 2] <- TRUE

  ## opacity
  vn$nodes$color <- adjustcolor(vn$nodes$color, alpha.f = min(c(opacity, 1)))
  ## set a darkest gray for iter-cluster edges
  vn$edges$color <- paste(substr(vn$edges$color, 1, 7), "90", sep = "")
  vn$edges$color[substr(vn$edges$color, 1, 7) == "#B3B3B3"] <- "#69696960"
  vn$edges$color <- adjustcolor(vn$edges$color, alpha.f = opacity)

  ## removing multiple edges
  vn$edges <- unique(vn$edges)

  vn$edges$width <- vn$edges$width^2 / (max(vn$edges$width^2)) * (5 + edgesize)

  ## labelsize
  scalemin <- 20
  scalemax <- 100
  # aggiunta
  vn$nodes$font.size <- vn$nodes$deg
  #
  Min <- min(vn$nodes$font.size)
  Max <- max(vn$nodes$font.size)
  if (Max > Min) {
    size <- (vn$nodes$font.size - Min) / (Max - Min) * 15 * labelsize #+10
  } else {
    size <- 5 * labelsize
  }
  size[size < scalemin] <- scalemin
  size[size > scalemax] <- scalemax
  vn$nodes$font.size <- size
  # l<-netLayout(type)

  ### TO ADD SHAPE AND FONT COLOR OPTIONS

  vn$nodes$size <- vn$nodes$font.size * 0.4

  if (shape %in% c("dot", "square")) {
    vn$nodes$font.vadjust <- -0.7 * vn$nodes$font.size
  } else {
    vn$nodes$font.vadjust <- 0
  }

  opacity_font <- sqrt(
    (vn$nodes$font.size - min(vn$nodes$font.size)) /
      diff(range(vn$nodes$font.size))
  ) *
    opacity +
    0.3
  if (is.nan(opacity_font[1])) {
    opacity_font <- rep(0.3, length(opacity_font))
  }

  if (labelsize > 0) {
    vn$nodes$font.color <- unlist(lapply(opacity_font, function(x) {
      adjustcolor("black", alpha.f = x)
    }))
  } else {
    vn$nodes$font.color <- adjustcolor("black", alpha.f = 0)
  }

  VIS <-
    visNetwork::visNetwork(
      nodes = vn$nodes,
      edges = vn$edges,
      type = "full",
      smooth = TRUE,
      physics = FALSE
    ) %>%
    visNetwork::visNodes(
      shadow = shadow,
      shape = shape,
      font = list(
        color = vn$nodes$font.color,
        size = vn$nodes$font.size,
        vadjust = vn$nodes$font.vadjust
      )
    ) %>%
    visNetwork::visIgraphLayout(layout = "layout_nicely", type = "full") %>%
    visNetwork::visEdges(smooth = list(type = "horizontal")) %>%
    visNetwork::visOptions(
      highlightNearest = list(enabled = T, hover = T, degree = 1),
      nodesIdSelection = T
    ) %>%
    visNetwork::visInteraction(
      dragNodes = TRUE,
      navigationButtons = F,
      hideEdgesOnDrag = TRUE,
      zoomSpeed = 0.4
    ) %>%
    visNetwork::visOptions(
      manipulation = curved,
      height = "100%",
      width = "100%"
    )

  return(list(VIS = VIS, vn = vn))
}


hist2vis <- function(
  net,
  labelsize = 2,
  nodesize = 2,
  curved = FALSE,
  shape = "dot",
  opacity = 0.7,
  labeltype = "short",
  timeline = TRUE
) {
  LABEL <- igraph::V(net$net)$id

  LABEL[igraph::V(net$net)$labelsize == 0] <- ""

  layout <- net$layout %>%
    dplyr::select(x, y, color, name)

  vn <- visNetwork::toVisNetworkData(net$net)

  vn$nodes$short_label <- LABEL

  if (labeltype != "short") {
    vn$nodes$label <- paste0(vn$nodes$years, ": ", LABEL)
  } else {
    vn$nodes$label <- LABEL
  }

  vn$nodes <- dplyr::left_join(vn$nodes, layout, by = c("id" = "name"))

  vn$edges$num <- 1
  vn$edges$dashes <- FALSE
  vn$edges$dashes[vn$edges$lty == 2] <- TRUE
  vn$edges$color <- "grey"

  ## opacity
  vn$nodes$font.color <- vn$nodes$color

  vn$nodes$color <- adjustcolor(
    vn$nodes$color,
    alpha.f = min(c(opacity - 0.2, 1))
  )
  vn$edges$color <- adjustcolor(vn$edges$color, alpha.f = opacity - 0.2)
  vn$edges$smooth <- curved

  ## removing multiple edges
  vn$edges <- unique(vn$edges)

  ## labelsize
  scalemin <- 20
  scalemax <- 150
  size <- 10 * labelsize
  size[size < scalemin] <- scalemin
  size[size > scalemax] <- scalemax
  vn$nodes$font.size <- size * 0.5
  vn$nodes$size <- nodesize * 2

  if (shape %in% c("dot", "square")) {
    vn$nodes$font.vadjust <- -0.7 * vn$nodes$font.size
  } else {
    vn$nodes$font.vadjust <- 0
  }

  text_data <- net$graph.data %>%
    select(Label, DOI, LCS, GCS) %>%
    rename(id = Label) %>%
    filter(!duplicated(id))

  vn$nodes <- vn$nodes %>% left_join(text_data, by = "id")

  ## split node tooltips into two strings
  title <- strsplit(stringi::stri_trans_totitle(vn$nodes$title), " ")

  vn$nodes$title <- unlist(lapply(title, function(l) {
    n <- floor(length(l) / 2)
    paste0(
      paste(l[1:n], collapse = " ", sep = ""),
      "<br>",
      paste(l[(n + 1):length(l)], collapse = " ", sep = "")
    )
  }))

  vn$nodes <- vn$nodes %>%
    mutate(
      title_orig = title,
      title = paste(
        "<b>Title</b>: ",
        title,
        "<br><b>DOI</b>: ",
        paste0(
          '<a href=\"https://doi.org/',
          DOI,
          '\" target=\"_blank\">',
          # "DOI: ",
          DOI,
          "</a>"
        ),
        "<br><b>GCS</b>: ",
        GCS,
        "<br><b>LCS</b>: ",
        LCS,
        sep = ""
      )
    )

  ## add time line
  vn$nodes$group <- "normal"
  vn$nodes$shape <- "dot"
  vn$nodes$shadow <- TRUE

  # nr <- nrow(vn$nodes)
  # y <- max(vn$nodes$y)
  # vn$nodes[nr + 1, c("id", "title", "label", "color", "font.color")] <-
  #   c(rep("logo", 3), "black", "white")
  # vn$nodes$x[nr + 1] <- max(vn$nodes$x, na.rm = TRUE) + 1
  # vn$nodes$y[nr + 1] <- y
  # vn$nodes$size[nr + 1] <- vn$nodes$size[nr] * 4
  # vn$nodes$years[nr + 1] <- as.numeric(vn$nodes$x[nr + 1])
  # vn$nodes$font.size[nr + 1] <- vn$nodes$font.size[nr]
  # vn$nodes$group[nr + 1] <- "logo"
  # vn$nodes$shape[nr + 1] <- "image"
  # vn$nodes$image[nr + 1] <- "logo.jpg"
  # vn$nodes$fixed.x <- TRUE
  # vn$nodes$fixed.y <- FALSE
  # vn$nodes$fixed.y[nr + 1] <- TRUE
  # vn$nodes$shadow[nr + 1] <- FALSE

  # coords <- vn$nodes[, c("x", "y")] %>%
  #   as.matrix()
  #
  # coords[, 2] <- coords[, 2]^(1 / 2)

  tooltipStyle <- ("position: fixed;visibility:hidden;padding: 5px;white-space: nowrap;
                  font-size:12px;font-color:black;background-color:white;")

  ## Font opacity
  vn$nodes$LCS[is.na(vn$nodes$LCS)] <- max(vn$nodes$LCS, na.rm = TRUE)
  opacity_font <- sqrt(
    (vn$nodes$LCS - min(vn$nodes$LCS)) / diff(range(vn$nodes$LCS))
  ) *
    0.6 +
    0.4

  vn$nodes$size <- opacity_font * 5 * nodesize
  vn$nodes$size[nrow(vn$nodes)] <- max(5 * nodesize)

  for (i in 1:nrow(vn$nodes)) {
    vn$nodes$font.color[i] <- adjustcolor(
      vn$nodes$font.color[i],
      alpha.f = opacity_font[i]
    )
  }

  x <- vn$nodes$x
  y <- vn$nodes$y
  vn$nodes$x <- y
  vn$nodes$y <- x

  vn$nodes <- assign_horizontal_coords_clusters_adaptive(vn$nodes)

  vn$nodes$fixed.x <- FALSE
  vn$nodes$fixed.y <- TRUE

  coords <- vn$nodes[, c("x", "y")] %>%
    as.matrix()
  coords[, 2] <- coords[, 2]

  VIS <-
    visNetwork::visNetwork(
      nodes = vn$nodes,
      edges = vn$edges,
      type = "full",
      smooth = TRUE,
      physics = FALSE
    ) %>%
    visNetwork::visNodes(
      shadow = vn$nodes$shadow,
      shape = shape,
      size = vn$nodes$size,
      font = list(
        color = vn$nodes$font.color,
        size = vn$nodes$font.size,
        vadjust = vn$nodes$font.vadjust
      )
    ) %>%
    visNetwork::visIgraphLayout(
      layout = "layout.norm",
      layoutMatrix = coords,
      type = "full"
    ) %>%
    #visNetwork::visEdges(smooth = list(type = "horizontal"), arrows = list(to = list(enabled = TRUE, scaleFactor = 0.5))) %>%
    visNetwork::visEdges(
      smooth = list(enabled = TRUE, type = "dynamic", roundness = 0.3),
      arrows = list(to = list(enabled = TRUE, scaleFactor = 0.5))
    ) %>%
    visNetwork::visInteraction(
      dragNodes = T,
      navigationButtons = F,
      hideEdgesOnDrag = F,
      tooltipStyle = tooltipStyle,
      zoomSpeed = 0.2
    ) %>%
    visNetwork::visOptions(
      highlightNearest = list(
        enabled = T,
        hover = T,
        degree = list(from = 1),
        algorithm = "hierarchical"
      ),
      nodesIdSelection = F,
      manipulation = FALSE,
      height = "100%",
      width = "100%"
    )

  return(list(VIS = VIS, vn = vn, type = "historiograph", curved = curved))
}

## calculate node coordinates in historiograph
assign_horizontal_coords_clusters_adaptive <- function(
  nodes_df,
  spacing_base = 1.0,
  cluster_spacing = 6,
  tol = 0.15
) {
  clusters <- nodes_df %>%
    count(color, name = "n_cluster") %>%
    arrange(desc(n_cluster)) %>%
    mutate(
      cluster_id = row_number(),
      cluster_center = (cluster_id - mean(cluster_id)) * cluster_spacing
    )

  nodes_df <- nodes_df %>%
    left_join(clusters, by = "color")

  nodes_df <- nodes_df %>%
    group_by(years, color) %>%
    mutate(
      n_nodes = n(),
      spacing = spacing_base * n_nodes, # USA direttamente il numero di nodi
      x = (cluster_center[1] + spacing[1] * (row_number() - (n() + 1) / 2)) *
        runif(1, 1 - tol, 1 + tol)
    ) %>%
    ungroup()

  return(nodes_df)
}

## Pajek Export
graph2Pajek <- function(graph, filename = "my_pajek_network") {
  nodes <- igraph::as_data_frame(graph, what = c("vertices")) %>%
    mutate(id = row_number())

  edges <- igraph::as_data_frame(graph, what = c("edges"))
  edges <- edges %>%
    left_join(nodes %>% select(id, name), by = c("from" = "name")) %>%
    rename(id_from = id) %>%
    left_join(nodes %>% select(id, name), by = c("to" = "name")) %>%
    rename(id_to = id)

  ### Creation of NET file
  file <- paste0(filename, ".net")

  # Nodes
  write(paste0("*Vertices ", nrow(nodes)), file = file)
  write(paste0(nodes$id, ' "', nodes$name, '"'), file = file, append = T)

  # Edges
  write(paste0("*Edges ", nrow(nodes)), file = file, append = T)
  write(
    paste0(edges$id_from, " ", edges$id_to, " ", edges$weight),
    file = file,
    append = T
  )

  ### Creation of VEC file
  file <- paste0(filename, ".vec")

  # Nodes
  write(paste0("*Vertices ", nrow(nodes)), file = file)
  write(paste0(nodes$deg), file = file, append = T)

  ### Creation of CLU file
  file <- paste0(filename, ".clu")

  # Nodes
  write(paste0("*Vertices ", nrow(nodes)), file = file)
  write(paste0(nodes$community), file = file, append = T)
}


## Dendogram to Visnetwork
dend2vis <- function(hc, labelsize, nclusters = 1, community = FALSE) {
  # community = TRUE means that hc is an igraph community detection object
  # community = FALSE mean that hc is a hclust object

  # transform and plot a community igraph object using dendrogram
  if (community) {
    hc <- as.hclust(hc, use.modularity = TRUE)
  }

  h_tail <- round((max(hc$height) * 0.12), 1)

  hc$height <- hc$height + h_tail

  VIS <- visHclust(
    hc,
    cutree = nclusters,
    colorEdges = "grey60",
    horizontal = TRUE,
    export = FALSE
  )
  VIS$x$edges <- data.frame(color = unique(VIS$x$edges$color)) %>%
    mutate(new_color = colorlist()[1:nrow(.)]) %>%
    right_join(VIS$x$edges, by = "color") %>%
    select(-color) %>%
    rename(color = new_color)
  VIS$x$nodes <- VIS$x$nodes %>%
    mutate(
      label = ifelse(group != "individual", NA, label),
      group = ifelse(group == "individual", "word", group),
      title = gsub("individuals", "words", title),
      value = 1,
      scaling.min = 10,
      scaling.max = 10
    )
  coords <- VIS$x$nodes %>%
    select(x, y) %>%
    as.matrix()

  edges <- VIS$x$edges
  nodes <- VIS$x$nodes %>%
    select(id, label) %>%
    dplyr::filter(label != "1")

  VIS$x$edges <- edges %>%
    select(-id) %>%
    left_join(nodes, by = c("to" = "id")) %>%
    select(-label.x) %>%
    rename(label = label.y) %>%
    mutate(
      value = 10,
      font.color = color,
      font.size = labelsize * 10,
      font.vadjust = -0.2 * font.size,
      label = ifelse(is.na(label), "", label)
    )

  VIS <- VIS %>%
    visGroups(
      groupname = "group",
      color = "gray90",
      shape = "dot",
      size = 10
    ) %>%
    visGroups(
      groupname = "word",
      font = list(size = 0),
      color = list(
        background = "white",
        border = "#80B1D3",
        highlight = "#e2e9e9",
        hover = "orange"
      ),
      shape = "box"
    ) %>%
    visNodes(font = list(align = VIS$x$nodes$font.align)) %>%
    visNetwork::visOptions(
      highlightNearest = list(
        enabled = T,
        hover = T,
        degree = list(to = 1000, from = 0),
        algorithm = "hierarchical"
      ),
      nodesIdSelection = FALSE,
      manipulation = FALSE,
      height = "100%",
      width = "100%"
    ) %>%
    visNetwork::visInteraction(
      dragNodes = FALSE,
      navigationButtons = F,
      hideEdgesOnDrag = TRUE,
      zoomSpeed = 0.4
    ) %>%
    visIgraphLayout(
      layout = "layout.norm",
      layoutMatrix = coords,
      type = "full"
    ) %>%
    visEdges(font = list(align = "top", size = VIS$x$edges$font.size)) %>%
    visEvents(
      click = "function(nodes){
                  Shiny.onInputChange('click_dend', nodes.nodes[0]);
                  ;}"
    )

  for (i in 1:nrow(VIS$x$nodes)) {
    if (VIS$x$nodes$group[i] == "group") {
      old_inertia <- as.character(VIS$x$nodes$inertia[i])
      inertia <- as.character(VIS$x$nodes$inertia[i] - h_tail)
      VIS$x$nodes$title[i] <- gsub(old_inertia, inertia, VIS$x$nodes$title[i])
    }
  }

  VIS
}

## Factorial Analysis dynamic plots
ca2plotly <- function(
  CS,
  method = "MCA",
  dimX = 1,
  dimY = 2,
  topWordPlot = Inf,
  threshold = 0.10,
  labelsize = 16,
  size = 5
) {
  LABEL <- CS$WData$word
  switch(
    method,
    CA = {
      contrib <- rowSums(CS$coord$contrib %>% as.data.frame()) / 2
      wordCoord <- CS$coord$coord[, 1:2] %>%
        data.frame() %>%
        mutate(
          label = LABEL,
          contrib = contrib
        ) %>%
        select(c(3, 1, 2, 4))
      row.names(wordCoord) <- LABEL
      xlabel <- paste0("Dim 1 (", round(CS$res$eigCorr$perc[1], 2), "%)")
      ylabel <- paste0("Dim 2 (", round(CS$res$eigCorr$perc[2], 2), "%)")
    },
    MCA = {
      contrib <- rowSums(CS$coord$contrib %>% as.data.frame()) / 2
      wordCoord <- CS$coord$coord[, 1:2] %>%
        data.frame() %>%
        mutate(
          label = LABEL,
          contrib = contrib
        ) %>%
        select(c(3, 1, 2, 4))
      row.names(wordCoord) <- LABEL
      xlabel <- paste0("Dim 1 (", round(CS$res$eigCorr$perc[1], 2), "%)")
      ylabel <- paste0("Dim 2 (", round(CS$res$eigCorr$perc[2], 2), "%)")
    },
    MDS = {
      contrib <- size
      xlabel <- "Dim 1"
      ylabel <- "Dim 2"
      wordCoord <- CS$WData %>%
        data.frame() %>%
        select(1:3) %>%
        mutate(contrib = contrib / 2) %>%
        rename(label = "word")
    }
  )

  dimContrLabel <- paste0("Contrib", c(dimX, dimY))
  ymax <- diff(range((wordCoord[, 3])))
  xmax <- diff(range((wordCoord[, 2])))
  threshold2 <- threshold * mean(xmax, ymax)

  # scaled size for dots
  dotScale <- (wordCoord$contrib) + size

  # Threshold labels to plot
  thres <- sort(dotScale, decreasing = TRUE)[min(topWordPlot, nrow(wordCoord))]

  names(wordCoord)[2:3] <- c("Dim1", "Dim2")

  wordCoord <- wordCoord %>%
    mutate(
      dotSize = dotScale,
      groups = CS$km.res$cluster,
      labelToPlot = ifelse(dotSize >= thres, label, ""),
      font.color = ifelse(
        labelToPlot == "",
        NA,
        adjustcolor(colorlist()[groups], alpha.f = 0.85)
      ),
      font.size = round(dotSize * 2, 0)
    )

  ## Avoid label overlapping
  labelToRemove <- avoidOverlaps(
    wordCoord,
    threshold = threshold2,
    dimX = dimX,
    dimY = dimY
  )
  wordCoord <- wordCoord %>%
    mutate(
      labelToPlot = ifelse(labelToPlot %in% labelToRemove, "", labelToPlot)
    ) %>%
    mutate(
      label = gsub("_1", "", label),
      labelToPlot = gsub("_1", "", labelToPlot)
    )

  hoverText <- paste(
    " <b>",
    wordCoord$label,
    "</b>\n Contribute: ",
    round(wordCoord$contrib, 3),
    sep = ""
  )

  fig <- plot_ly(
    data = wordCoord,
    x = wordCoord[, "Dim1"],
    y = wordCoord[, "Dim2"], # customdata=results$wordCoord,
    type = "scatter",
    mode = "markers",
    marker = list(
      size = dotScale,
      color = adjustcolor(colorlist()[wordCoord$groups], alpha.f = 0.3), #' rgb(79, 121, 66, .5)',
      line = list(
        color = adjustcolor(colorlist()[wordCoord$groups], alpha.f = 0.3), #' rgb(79, 121, 66, .8)',
        width = 2
      )
    ),
    text = hoverText,
    hoverinfo = "text",
    alpha = .3
  )

  fig <- fig %>%
    layout(
      yaxis = list(
        title = ylabel,
        showgrid = TRUE,
        showline = FALSE,
        showticklabels = TRUE,
        domain = c(0, 1)
      ),
      xaxis = list(
        title = xlabel,
        zeroline = TRUE,
        showgrid = TRUE,
        showline = FALSE,
        showticklabels = TRUE
      ),
      plot_bgcolor = "rgba(0, 0, 0, 0)",
      paper_bgcolor = "rgba(0, 0, 0, 0)"
    )

  for (i in seq_len(max(wordCoord$groups))) {
    if (method == "MDS") {
      w <- wordCoord %>%
        dplyr::filter(groups == i) %>%
        mutate(
          Dim1 = Dim1 + 0.005,
          Dim2 = Dim2 + 0.005
        )
    } else {
      w <- wordCoord %>%
        dplyr::filter(groups == i) %>%
        mutate(
          Dim1 = Dim1 + dotSize * 0.005,
          Dim2 = Dim2 + dotSize * 0.01
        )
    }

    if (max(CS$hull_data$clust) > 1) {
      hull_df <- CS$hull_data %>% dplyr::filter(clust == i)
      fig <- fig %>%
        add_polygons(
          x = hull_df$Dim1,
          y = hull_df$Dim2,
          inherit = FALSE,
          showlegend = FALSE,
          color = I(hull_df$color[1]),
          opacity = 0.3,
          line = list(width = 2),
          text = paste0("Cluster ", i),
          hoverinfo = "text",
          hoveron = "points"
        )
    }
    fig <- fig %>%
      add_annotations(
        data = w,
        x = ~Dim1,
        y = ~Dim2,
        xref = "x1",
        yref = "y",
        text = ~labelToPlot,
        font = list(
          family = "sans serif",
          size = labelsize,
          color = w$font.color[1]
        ), #' rgb(79, 121, 66)'),
        showarrow = FALSE
      )
  }

  fig <- fig %>%
    config(
      displaylogo = FALSE,
      modeBarButtonsToRemove = c(
        #' toImage',
        "sendDataToCloud",
        "pan2d",
        "select2d",
        "lasso2d",
        "toggleSpikelines",
        "hoverClosestCartesian",
        "hoverCompareCartesian"
      )
    ) %>%
    event_register("plotly_selecting")
  return(fig)
}


## function to avoid label overlapping ----
avoidOverlaps <- function(w, threshold = 0.10, dimX = 1, dimY = 2) {
  w[, "Dim2"] <- w[, "Dim2"] / 3

  Ds <- dist(
    w %>%
      dplyr::filter(labelToPlot != "") %>%
      select(Dim1, Dim2),
    method = "manhattan",
    upper = T
  ) %>%
    dist2df() %>%
    rename(
      from = row,
      to = col,
      dist = value
    ) %>%
    left_join(
      w %>% dplyr::filter(labelToPlot != "") %>% select(labelToPlot, dotSize),
      by = c("from" = "labelToPlot")
    ) %>%
    rename(w_from = dotSize) %>%
    left_join(
      w %>% dplyr::filter(labelToPlot != "") %>% select(labelToPlot, dotSize),
      by = c("to" = "labelToPlot")
    ) %>%
    rename(w_to = dotSize) %>%
    filter(dist < threshold)

  st <- TRUE
  i <- 1
  label <- NULL
  case <- "n"

  while (isTRUE(st)) {
    if (Ds$w_from[i] > Ds$w_to[i] & Ds$dist[i] < threshold) {
      case <- "y"
      lab <- Ds$to[i]
    } else if (Ds$w_from[i] <= Ds$w_to[i] & Ds$dist[i] < threshold) {
      case <- "y"
      lab <- Ds$from[i]
    }

    switch(
      case,
      "y" = {
        Ds <- Ds[Ds$from != lab, ]
        Ds <- Ds[Ds$to != lab, ]
        label <- c(label, lab)
      },
      "n" = {
        Ds <- Ds[-1, ]
      }
    )

    if (i >= nrow(Ds)) {
      st <- FALSE
    }
    case <- "n"
    # print(nrow(Ds))
  }

  label
}

## convert a distance object into a data.frame ----
dist2df <- function(inDist) {
  if (class(inDist) != "dist") {
    stop("wrong input type")
  }
  A <- attr(inDist, "Size")
  B <- if (is.null(attr(inDist, "Labels"))) {
    sequence(A)
  } else {
    attr(inDist, "Labels")
  }
  if (isTRUE(attr(inDist, "Diag"))) {
    attr(inDist, "Diag") <- FALSE
  }
  if (isTRUE(attr(inDist, "Upper"))) {
    attr(inDist, "Upper") <- FALSE
  }
  data.frame(
    row = B[unlist(lapply(sequence(A)[-1], function(x) x:A))],
    col = rep(B[-length(B)], (length(B) - 1):1),
    value = as.vector(inDist)
  )
}

### Excel report functions
addDataWb <- function(list_df, wb, sheetname) {
  l <- length(list_df)
  startRow <- 1
  for (i in 1:l) {
    df <- list_df[[i]]
    n <- nrow(df)
    writeDataTable(
      wb,
      sheetname,
      df,
      startRow = startRow,
      startCol = 1,
      tableStyle = "TableStyleMedium20"
    )
    startRow <- startRow + n + 3
  }
  return(wb)
}

addDataScreenWb <- function(list_df, wb, sheetname) {
  ind <- which(regexpr(sheetname, wb$sheet_names) > -1)
  if (length(ind) > 0) {
    sheetname <- paste(sheetname, "(", length(ind) + 1, ")", sep = "")
  }
  addWorksheet(wb = wb, sheetName = sheetname, gridLines = FALSE)
  if (!is.null(list_df)) {
    addDataWb(list_df, wb, sheetname)
    col <- max(unlist(lapply(list_df, ncol))) + 2
  } else {
    col <- 1
  }

  results <- list(wb = wb, col = col, sheetname = sheetname)
  return(results)
}

addGgplotsWb <- function(
  list_plot,
  wb,
  sheetname,
  col,
  width = 10,
  height = 7,
  dpi = 75
) {
  l <- length(list_plot)
  startRow <- 1
  for (i in 1:l) {
    fileName <- tempfile(
      pattern = "figureImage",
      fileext = ".png"
    )
    if (inherits(list_plot[[i]], "ggplot")) {
      ggsave(
        plot = list_plot[[i]],
        filename = fileName,
        width = width,
        height = height,
        units = "in",
        dpi = dpi
      )
    }
    if (inherits(list_plot[[i]], "igraph")) {
      igraph2PNG(
        x = list_plot[[i]],
        filename = fileName,
        width = width,
        height = height,
        dpi = dpi
      )
    }
    if (inherits(list_plot[[i]], "bibliodendrogram")) {
      # print("dendrogram plot")
      # 1. Open jpeg file
      png(
        filename = fileName,
        width = width,
        height = height,
        res = 300,
        units = "in"
      )
      # 2. Create the plot
      plot(list_plot[[i]])
      # 3. Close the file
      dev.off()
    }
    insertImage(
      wb = wb,
      sheet = sheetname,
      file = fileName,
      width = width,
      height = height,
      startRow = startRow,
      startCol = col,
      units = "in",
      dpi = dpi
    )
    startRow <- startRow + (height * 6) + 1
  }
  return(wb)
}

screenSh <- function(p, zoom = 2, type = "vis") {
  tmpdir <- tempdir()
  fileName <- tempfile(
    pattern = "figureImage",
    tmpdir = tmpdir,
    fileext = ".png"
  ) # %>% substr(.,2,nchar(.))

  plot2png(p, filename = fileName, zoom = zoom, type = type, tmpdir = tmpdir)

  return(fileName)
}

screenShot <- function(p, filename, type) {
  home <- homeFolder()

  # setting up the main directory
  # filename <- paste0(file.path(home,"downloads/"),filename)
  if ("downloads" %in% tolower(dir(home))) {
    filename <- paste0(file.path(home, "downloads"), "/", filename)
  } else {
    filename <- paste0(home, "/", filename)
  }

  plot2png(p, filename, zoom = 2, type = type, tmpdir = tempdir())
}

plot2png <- function(p, filename, zoom = 2, type = "vis", tmpdir) {
  html_name <- tempfile(
    fileext = ".html",
    tmpdir = tmpdir
  )
  switch(
    type,
    vis = {
      visSave(p, html_name)
    },
    plotly = {
      htmlwidgets::saveWidget(p, file = html_name)
    }
  )
  biblioShot(url = html_name, zoom = zoom, file = filename) # , verbose=FALSE)

  popUpGeneric(
    title = NULL,
    type = "success",
    color = c("#1d8fe1"),
    subtitle = paste0("Plot was saved in the following path: ", filename),
    btn_labels = "OK",
    size = "40%"
  )
}

addScreenWb <- function(df, wb, width = 14, height = 8, dpi = 75) {
  names(df) <- c("sheet", "file", "n")
  if (nrow(df) > 0) {
    sheet <- unique(df$sheet)
    for (i in 1:length(sheet)) {
      sh <- sheet[i]
      df_sh <- df %>% dplyr::filter(sheet == sh)
      l <- nrow(df_sh)
      startRow <- 1
      for (j in 1:l) {
        fileName <- df_sh$file[j]
        insertImage(
          wb = wb,
          sheet = sh,
          file = fileName,
          width = width,
          height = height,
          startRow = startRow,
          startCol = df_sh$n[j],
          units = "in",
          dpi = dpi
        )
        startRow <- startRow + (height * 10) + 3
      }
    }
  }
  return(wb)
}

addSheetToReport <- function(list_df, list_plot, sheetname, wb, dpi = 75) {
  ind <- which(regexpr(sheetname, wb$sheet_names) > -1)
  if (length(ind) > 0) {
    sheetname <- paste(sheetname, "(", length(ind) + 1, ")", sep = "")
  }
  addWorksheet(wb, sheetname, gridLines = FALSE)

  if (!is.null(list_df)) {
    col <- max(unlist(lapply(list_df, ncol))) + 2
    wb <- addDataWb(list_df, wb = wb, sheetname = sheetname)
  } else {
    col <- 1
  }

  if (!is.null(list_plot)) {
    wb <- addGgplotsWb(
      list_plot,
      wb = wb,
      sheetname = sheetname,
      col = col,
      dpi = dpi
    )
  }
  # values$sheet_name <- sheetname
  return(wb)
}

short2long <- function(df, myC) {
  z <- unlist(lapply(myC, function(x) {
    y <- gsub(r"{\s*\([^\)]+\)}", "", x)
    gsub(y, df$long[df$short == y], x)
  }))
  names(myC) <- z
  return(myC)
}

dfLabel <- function() {
  short <- c(
    "Empty Report",
    "MissingData",
    "MainInfo",
    "AnnualSciProd",
    "AnnualCitPerYear",
    "LifeCycle",
    "ThreeFieldsPlot",
    "MostRelSources",
    "MostLocCitSources",
    "BradfordLaw",
    "SourceLocImpact",
    "SourceProdOverTime",
    "MostRelAuthors",
    "MostLocCitAuthors",
    "AuthorProdOverTime",
    "LotkaLaw",
    "AuthorLocImpact",
    "MostRelAffiliations",
    "AffOverTime",
    "CorrAuthCountries",
    "CountrySciProd",
    "CountryProdOverTime",
    "MostCitCountries",
    "MostGlobCitDocs",
    "MostLocCitDocs",
    "MostLocCitRefs",
    "RPYS",
    "MostFreqWords",
    "WordCloud",
    "TreeMap",
    "WordFreqOverTime",
    "TrendTopics",
    "CouplingMap",
    "CoWordNet",
    "ThematicMap",
    "ThematicEvolution",
    "TE_Period_1",
    "TE_Period_2",
    "TE_Period_3",
    "TE_Period_4",
    "TE_Period_5",
    "FactorialAnalysis",
    "CoCitNet",
    "Historiograph",
    "CollabNet",
    "CollabWorldMap"
  )

  long <- c(
    "Empty Report",
    "Missing Data Table",
    "Main Information",
    "Annual Scientific Production",
    "Annual Citation Per Year",
    "Life Cycle of Publications",
    "Three-Field Plot",
    "Most Relevant Sources",
    "Most Local Cited Sources",
    "Bradfords Law",
    "Sources Local Impact",
    "Sources Production over Time",
    "Most Relevant Authors",
    "Most Local Cited Authors",
    "Authors Production over Time",
    "Lotkas Law",
    "Authors Local Impact",
    "Most Relevant Affiliations",
    "Affiliations Production over Time",
    "Corresponding Authors Countries",
    "Countries Scientific Production",
    "Countries Production over Time",
    "Most Cited Countries",
    "Most Global Cited Documents",
    "Most Local Cited Documents",
    "Most Local Cited References",
    "Reference Spectroscopy",
    "Most Frequent Words",
    "WordCloud",
    "TreeMap",
    "Words Frequency over Time",
    "Trend Topics",
    "Clustering by Coupling",
    "Co-occurence Network",
    "Thematic Map",
    "Thematic Evolution",
    "TE_Period_1",
    "TE_Period_2",
    "TE_Period_3",
    "TE_Period_4",
    "TE_Period_5",
    "Factorial Analysis",
    "Co-citation Network",
    "Historiograph",
    "Collaboration Network",
    "Countries Collaboration World Map"
  )
  data.frame(short = short, long = long)
}

## Generic PopUp
popUpGeneric <- function(
  title = NULL,
  type = "success",
  color = c("#1d8fe1", "#913333", "#FFA800"),
  subtitle = "",
  btn_labels = "OK",
  size = "40%"
) {
  showButton <- TRUE
  timer <- NA
  show_alert(
    title = title,
    text = subtitle,
    type = type,
    size = size,
    closeOnEsc = TRUE,
    closeOnClickOutside = TRUE,
    html = FALSE,
    showConfirmButton = showButton,
    showCancelButton = FALSE,
    btn_labels = btn_labels,
    btn_colors = color,
    timer = timer,
    imageUrl = "",
    animation = TRUE
  )
}


## Ad to Report PopUp
popUp <- function(title = NULL, type = "success", btn_labels = "OK") {
  switch(
    type,
    success = {
      title <- paste(title, "\n added to report", sep = "")
      subtitle <- ""
      btn_colors <- "#1d8fe1"
      showButton <- TRUE
      timer <- 3000
    },
    error = {
      title <- "No results to add to the report "
      subtitle <- "Please Run the analysis and then Add it to the report"
      btn_colors <- "#913333"
      showButton <- TRUE
      timer <- 3000
    },
    waiting = {
      title <- "Please wait... "
      subtitle <- "Adding results to report"
      btn_colors <- "#FFA800"
      showButton <- FALSE
      btn_labels <- NA
      timer <- NA
    }
  )

  show_alert(
    title = title,
    text = subtitle,
    type = type,
    size = "s",
    closeOnEsc = TRUE,
    closeOnClickOutside = TRUE,
    html = FALSE,
    showConfirmButton = showButton,
    showCancelButton = FALSE,
    btn_labels = btn_labels,
    btn_colors = btn_colors,
    timer = timer,
    imageUrl = "",
    animation = TRUE
  )
}

colorlist <- function() {
  c(
    "#E41A1C",
    "#377EB8",
    "#4DAF4A",
    "#984EA3",
    "#FF7F00",
    "#A65628",
    "#F781BF",
    "#999999",
    "#66C2A5",
    "#FC8D62",
    "#8DA0CB",
    "#E78AC3",
    "#A6D854",
    "#FFD92F",
    "#B3B3B3",
    "#A6CEE3",
    "#1F78B4",
    "#B2DF8A",
    "#33A02C",
    "#FB9A99",
    "#E31A1C",
    "#FDBF6F",
    "#FF7F00",
    "#CAB2D6",
    "#6A3D9A",
    "#B15928",
    "#8DD3C7",
    "#BEBADA",
    "#FB8072",
    "#80B1D3",
    "#FDB462",
    "#B3DE69",
    "#D9D9D9",
    "#BC80BD",
    "#CCEBC5"
  )
}

overlayPlotly <- function(VIS) {
  # colorscale_VOS=matrix(c(0, 'rgba(66,65,135,255)', 0.1, 'rgba(34,170,134,255)',
  #                         0.3, 'rgba(202,224,31,255)',
  #                         1, 'rgba(244,227,92,255)'),4,2, byrow=T)

  # colorscale_Our=matrix(c(0, 'rgba(238,238,238,255)',
  #                         0.1, 'rgba(232,202,177,255)',
  #                         0.2, 'rgba(217,137,100,255)',
  #                         0.6, 'rgba(199,107,90,255)',
  #                         0.9, 'rgba(164,38,39,255)',
  #                         1,   'rgba(178,34,34,255)'),
  #                       6,2, byrow=T)

  Reds <- matrix(
    c(
      "0",
      "rgb(255,255,255)",
      "0.05",
      "rgb(238,238,238)",
      "0.125",
      "rgb(254,224,210)",
      "0.25",
      "rgb(252,187,161)",
      "0.375",
      "rgb(252,146,114)",
      "0.5",
      "rgb(251,106,74)",
      "0.625",
      "rgb(239,59,44)",
      "0.75",
      "rgb(203,24,29)",
      "0.875",
      "rgb(165,15,21)",
      "1",
      "rgb(103,0,13)"
    )
  )

  nodes <- VIS$x$nodes %>%
    mutate(
      y = y * (-1),
      font.size = (((font.size - min(font.size)) / diff(range(font.size))) *
        20) +
        10
    )

  colori <- c(
    "Blackbody",
    "Bluered",
    "Blues",
    "Cividis",
    "Earth",
    "Electric",
    "Greens",
    "Greys",
    "Hot",
    "Jet",
    "Picnic",
    "Portland",
    "Rainbow",
    "RdBu",
    "Reds",
    "Viridis",
    "YlGnBu",
    "YlOrRd"
  )

  nodes2 <- nodes %>%
    group_by(id) %>%
    mutate(log = ceiling(log(deg))) %>%
    slice(rep(1, each = log))

  p <- plot_ly(nodes2, x = ~x, y = ~y) %>%
    add_histogram2d(
      histnorm = "density",
      zsmooth = "fast",
      colorscale = Reds,
      # colorscale=colori[15],
      showscale = FALSE
    )

  for (i in 1:nrow(nodes)) {
    p <- p %>%
      add_annotations(
        xref = "x1",
        yref = "y",
        x = nodes$x[i],
        y = nodes$y[i],
        text = nodes$label[i],
        font = list(
          family = "Arial",
          size = nodes$font.size[i],
          color = adjustcolor(nodes$font.color[i], alpha.f = 0.8)
        ),
        showarrow = FALSE
      )
  }
  p <- p %>%
    layout(
      yaxis = list(
        title = "",
        zeroline = FALSE,
        showgrid = FALSE,
        showline = FALSE,
        showticklabels = FALSE,
        domain = c(-1, 1),
        gridcolor = "#FFFFFF",
        tickvals = list(NA)
      ),
      xaxis = list(
        title = "",
        zeroline = FALSE,
        showgrid = FALSE,
        showline = FALSE,
        showticklabels = FALSE,
        domain = c(-1, 1),
        gridcolor = "#FFFFFF",
        tickvals = list(NA)
      ),
      plot_bgcolor = "rgba(0, 0, 0, 0)",
      paper_bgcolor = "rgba(0, 0, 0, 0)",
      showlegend = FALSE
    ) %>%
    style(hoverinfo = "none") %>%
    config(
      displaylogo = FALSE,
      modeBarButtonsToRemove = c(
        #' toImage',
        "sendDataToCloud",
        "pan2d",
        "select2d",
        "lasso2d",
        "toggleSpikelines",
        "hoverClosestCartesian",
        "hoverCompareCartesian"
      )
    )
  return(p)
}


menuList <- function(values) {
  TC <- ISI <- MLCS <- MLCA <- AFF <- MCC <- DB_TC <- DB_CR <- CR <- FALSE
  if (!"TC" %in% values$missTags) {
    TC <- TRUE
  }
  if ("ISI" %in% values$M$DB[1] & !"CR" %in% values$missTags) {
    MLCS <- TRUE
  }
  if ("ISI" %in% values$M$DB[1] & !"CR" %in% values$missTags) {
    MLCA <- TRUE
  }
  if ("ISI" %in% values$M$DB[1]) {
    ISI <- TRUE
  }
  if (!"C1" %in% values$missTags) {
    AFF <- TRUE
  }
  if (!"CR" %in% values$missTags) {
    CR <- TRUE
  }
  if (!"TC" %in% values$missTags & !"C1" %in% values$missTags) {
    MCC <- TRUE
  }
  if (sum(c("SCOPUS", "ISI") %in% values$M$DB[1]) > 0) {
    DB_CR <- TRUE
  }
  if (sum(c("SCOPUS", "ISI", "OPENALEX", "LENS") %in% values$M$DB[1]) > 0) {
    DB_TC <- TRUE
  }

  # out <- list(TC,ISI,MLCS,AFF,MCC,DB_TC,DB_CR,CR)
  out <- NULL

  L <- list()

  # APPRAISAL
  L[[length(L) + 1]] <-
    tags$div(
      id = "appraisal-header",
      style = "display: flex; 
          align-items: center;
          justify-content: space-between;
          font-size: 14px; 
          font-weight: 600; 
          color: #FFFFFF; 
          background: rgba(255,255,255,0.1);
          padding: 10px 10px; 
          margin: 15px 8px 8px 8px;
          border-radius: 6px;
          border-left: 3px solid #66BB6A;
          letter-spacing: 0.8px;
          cursor: pointer;",
      onclick = "toggleSection('appraisal')",
      tags$div(
        style = "display: flex; align-items: center;",
        tags$span(
          style = "background: #66BB6A; 
              padding: 4px 8px; 
              border-radius: 4px; 
              margin-right: 10px;
              font-size: 12px;",
          icon("filter")
        ),
        "APPRAISAL"
      ),
      tags$i(
        id = "appraisal-chevron",
        class = "fa fa-chevron-down",
        style = "transition: transform 0.3s; font-size: 12px;"
      )
    )

  L[[length(L) + 1]] <-
    tags$li(
      class = "appraisal-item treeview",
      menuItem("Filters", tabName = "filters", icon = fa_i(name = "filter"))
    )

  # ANALYSIS
  L[[length(L) + 1]] <-
    tags$div(
      style = "display: flex;
        align-items: center;
        font-size: 14px;
        font-weight: 600;
        color: #FFFFFF;
        background: rgba(255,255,255,0.1);
        padding: 10px 10px;
        margin: 15px 8px 8px 8px;
        border-radius: 6px;
        border-left: 3px solid #FFA726;
        letter-spacing: 0.8px;",
      tags$span(
        style = "background: #FFA726;
          padding: 4px 8px;
          border-radius: 4px;
          margin-right: 10px;
          font-size: 12px;",
        icon("chart-line")
      ),
      "ANALYSIS"
    )

  L[[length(L) + 1]] <-
    menuItem(
      "Overview",
      tabName = "overview",
      icon = fa_i(name = "table"),
      startExpanded = FALSE,
      menuSubItem(
        "Main Information",
        tabName = "mainInfo",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Annual Scientific Production",
        tabName = "annualScPr",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      if (isTRUE(TC)) {
        menuSubItem(
          "Average Citations per Year",
          tabName = "averageCitPerYear",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      menuSubItem(
        "Life Cycle",
        tabName = "lifeCycle",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Three-Field Plot",
        tabName = "threeFieldPlot",
        icon = icon("chevron-right", lib = "glyphicon")
      )
    )

  L[[length(L) + 1]] <-
    menuItem(
      "Sources",
      tabName = "sources",
      icon = fa_i(name = "book"),
      startExpanded = FALSE,
      menuSubItem(
        "Most Relevant Sources",
        tabName = "relevantSources",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      if (isTRUE(MLCS)) {
        menuSubItem(
          "Most Local Cited Sources",
          tabName = "localCitedSources",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      menuSubItem(
        "Bradford's Law",
        tabName = "bradford",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      if (isTRUE(TC)) {
        menuSubItem(
          "Sources' Local Impact",
          tabName = "sourceImpact",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      menuSubItem(
        "Sources' Production over Time",
        tabName = "sourceDynamics",
        icon = icon("chevron-right", lib = "glyphicon")
      )
    )

  AU <-
    menuItem(
      "Authors",
      tabName = "authors",
      icon = fa_i(name = "user"),
      startExpanded = FALSE,
      "Authors",
      menuSubItem(
        "Author Profile",
        tabName = "AuthorPage",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Most Relevant Authors",
        tabName = "mostRelAuthors",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      if (isTRUE(MLCA)) {
        menuSubItem(
          "Most Local Cited Authors",
          tabName = "mostLocalCitedAuthors",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      menuSubItem(
        "Authors' Production over Time",
        tabName = "authorsProdOverTime",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Lotka's Law",
        tabName = "lotka",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      if (isTRUE(TC)) {
        menuSubItem(
          "Authors' Local Impact",
          tabName = "authorImpact",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(AFF)) {
        "Affiliations"
      },
      if (isTRUE(AFF)) {
        menuSubItem(
          "Most Relevant Affiliations",
          tabName = "mostRelAffiliations",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(AFF)) {
        menuSubItem(
          "Affiliations' Production over Time",
          tabName = "AffOverTime",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(AFF)) {
        "Countries"
      },
      if (isTRUE(AFF)) {
        menuSubItem(
          "Corresponding Author's Countries",
          tabName = "correspAuthorCountry",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(AFF)) {
        menuSubItem(
          "Countries' Scientific Production",
          tabName = "countryScientProd",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(AFF)) {
        menuSubItem(
          "Countries' Production over Time",
          tabName = "COOverTime",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(MCC)) {
        menuSubItem(
          "Most Cited Countries",
          tabName = "mostCitedCountries",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      }
    )

  L[[length(L) + 1]] <- AU

  DOC <-
    menuItem(
      "Documents",
      tabName = "documents",
      icon = fa_i(name = "layer-group"),
      startExpanded = FALSE,
      if (isTRUE(TC) | isTRUE(DB_TC)) {
        "Documents"
      },
      if (isTRUE(TC)) {
        menuSubItem(
          "Most Global Cited Documents",
          tabName = "mostGlobalCitDoc",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(DB_TC) & isTRUE(CR) & isTRUE(TC)) {
        menuSubItem(
          "Most Local Cited Documents",
          tabName = "mostLocalCitDoc",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(DB_CR)) {
        "Cited References"
      },
      if (isTRUE(DB_CR)) {
        menuSubItem(
          "Most Local Cited References",
          tabName = "mostLocalCitRef",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      if (isTRUE(DB_CR)) {
        menuSubItem(
          "References Spectroscopy",
          tabName = "ReferenceSpect",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      },
      "Words",
      menuSubItem(
        "Most Frequent Words",
        tabName = "mostFreqWords",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "WordCloud",
        tabName = "wcloud",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "TreeMap",
        tabName = "treemap",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Words' Frequency over Time",
        tabName = "wordDynamics",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Trend Topics",
        tabName = "trendTopic",
        icon = icon("chevron-right", lib = "glyphicon")
      )
    )

  L[[length(L) + 1]] <- DOC

  # SYNTHESIS
  L[[length(L) + 1]] <-
    tags$div(
      style = "display: flex;
          align-items: center;
          font-size: 14px;
          font-weight: 600;
          color: #FFFFFF;
          background: rgba(255,255,255,0.1);
          padding: 10px 10px;
          margin: 15px 8px 8px 8px;
          border-radius: 6px;
          border-left: 3px solid #EC407A;
          letter-spacing: 0.8px;",
      tags$span(
        style = "background: #EC407A;
            padding: 4px 8px;
            border-radius: 4px;
            margin-right: 10px;
            font-size: 12px;",
        icon("project-diagram")
      ),
      "SYNTHESIS"
    )

  L[[length(L) + 1]] <-
    menuItem(
      "Clustering",
      tabName = "clustering",
      icon = fa_i(name = "spinner"),
      startExpanded = FALSE,
      menuSubItem(
        "Clustering by Coupling",
        tabName = "coupling",
        icon = icon("chevron-right", lib = "glyphicon")
      )
    )

  L[[length(L) + 1]] <-
    menuItem(
      "Conceptual Structure",
      tabName = "concepStructure",
      icon = fa_i(name = "spell-check"),
      startExpanded = FALSE,
      "Network Approach",
      menuSubItem(
        "Co-occurence Network",
        tabName = "coOccurenceNetwork",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Thematic Map",
        tabName = "thematicMap",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      menuSubItem(
        "Thematic Evolution",
        tabName = "thematicEvolution",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      "Factorial Approach",
      menuSubItem(
        "Factorial Analysis",
        tabName = "factorialAnalysis",
        icon = icon("chevron-right", lib = "glyphicon")
      )
    )

  if (!"CR" %in% values$missTags) {
    L[[length(L) + 1]] <-
      menuItem(
        "Intellectual Structure",
        tabName = "intStruct",
        icon = fa_i(name = "gem"),
        startExpanded = FALSE,
        menuSubItem(
          "Co-citation Network",
          tabName = "coCitationNetwork",
          icon = icon("chevron-right", lib = "glyphicon")
        ),
        if (isTRUE(DB_TC) & isTRUE(CR)) {
          menuSubItem(
            "Historiograph",
            tabName = "historiograph",
            icon = icon("chevron-right", lib = "glyphicon")
          )
        }
      )
  }

  L[[length(L) + 1]] <-
    menuItem(
      "Social Structure",
      tabName = "socialStruct",
      icon = fa_i("users"),
      startExpanded = FALSE,
      menuSubItem(
        "Collaboration Network",
        tabName = "collabNetwork",
        icon = icon("chevron-right", lib = "glyphicon")
      ),
      if (isTRUE(AFF)) {
        menuSubItem(
          "Countries' Collaboration World Map",
          tabName = "collabWorldMap",
          icon = icon("chevron-right", lib = "glyphicon")
        )
      }
    )

  # L[[length(L) + 1]] <- tags$hr(
  #   style = "border: 0;
  #                border-top: 1px solid rgba(255,255,255,0.15);
  #                margin: 15px 15px 10px 15px;"
  # )

  L[[length(L) + 1]] <- tags$div(style = "margin-top: 20px;")

  #   menuItem("Content Analysis",
  #                        tabName = "content_analysis",
  #                        icon = icon("quote-right"))

  L[[length(L) + 1]] <- menuItem(
    "Report",
    tabName = "report",
    icon = fa_i(name = "list-alt")
  )

  L[[length(L) + 1]] <- menuItem(
    "TALL Export",
    tabName = "tall",
    icon = icon("text-size", lib = "glyphicon")
  )

  # L[[length(L) + 1]] <- menuItem("Settings", tabName = "settings", icon = fa_i(name = "sliders"))

  if (!isTRUE(TC)) {
    out <- c(
      out,
      "Average Citations per Year",
      "Sources' Local Impact",
      "Authors' Local Impact",
      "Most Global Cited Documents"
    )
  }
  if (!isTRUE(MLCS)) {
    out <- c(out, "Most Local Cited Sources")
  }
  if (!isTRUE(ISI)) {
    out <- c(out, "Most Local Cited Authors")
  }
  if (!isTRUE(AFF)) {
    out <- c(
      out,
      "Most Relevant Affiliations",
      "Affiliations' Production over Time",
      "Corresponding Author's Countries",
      "Countries' Scientific Production",
      "Countries' Production over Time",
      "Countries' Collaboration World Map"
    )
  }
  if (!isTRUE(MCC)) {
    out <- c(out, "Most Cited Countries")
  }
  if (!(isTRUE(DB_TC) & isTRUE(CR) & isTRUE(TC))) {
    out <- c(out, "Most Local Cited Documents")
  }
  if (!isTRUE(DB_CR)) {
    out <- c(out, "Most Local Cited References", "References Spectroscopy")
  }
  if (!isTRUE(CR)) {
    out <- c(out, "Co-citation Network")
  }
  if (!(isTRUE(DB_TC) & isTRUE(CR))) {
    out <- c(out, "Historiograph")
  }

  values$out <- out

  return(L)
}


# find home folder
homeFolder <- function() {
  switch(
    Sys.info()[["sysname"]],
    Windows = {
      home <- Sys.getenv("R_USER")
    },
    Linux = {
      home <- Sys.getenv("HOME")
    },
    Darwin = {
      home <- Sys.getenv("HOME")
    }
  )
  return(home)
}

Try the bibliometrix package in your browser

Any scripts or data that you put into this service are public.

bibliometrix documentation built on Nov. 8, 2025, 5:06 p.m.