#' Gets the ... names
#'
#' Used to get the -year
#'
#' @param ... arguments
#'
#' @return a vector of the names of ...
#'
#' @examples
#' x <- 1:10
#' y <- 1
#' get_quos_names(-x)
#' get_quos_names(x:y)
get_quos_names <- function(...) {
  q <- quos(...)
  sapply(q, function(i) as.character(i[[2]]))
}

#' Parses a simple vector so that it looks like its input
#'
#' @param x a vector
#'
#' @return a string
#'
#' @examples
#' dput_parser("x")
#' dput_parser(c("x", "y"))
dput_parser <- function(x) {
  ifelse(length(x) == 1,
         sprintf("'%s'", x),
         paste0("c(",
                paste(sprintf("'%s'", x), collapse = ", "),
                ")"))
}

#' Adds color to processed tidy data
#'
#' @param x a processed data-frame as outputted by process_long or process_wide
#' @param key_values the unique key-values
#' @param color_fun the color function
#' @param color_header the color for the header
#' @param ... not used
#'
#' @return a data-frame with the colors
#'
#' @examples
#' NULL
add_color_tidyr <- function(x, key_values,
                            color_fun = scales::brewer_pal(type = "qual", "Set1"),
                            color_header = "#737373",
                            color_id = "#d0d0d0") {

  color_dict <- color_fun(3)
  names(color_dict) <- c("id", "key", "value")

  x %>% mutate(.color = color_dict[.type])
}

#' Processes a wide dataframe and converts it into a dataset that can be plotted
#'
#' @param x a wide data frame
#' @param ids a vector of id-variables that are already in the tidy-format
#' @param key a vector of key-variables
#' @param color_id the color for the id-body
#' @param ...
#'
#' @return
#'
#' @examples
#' wide <- data_frame(
#'   year = 2010:2011,
#'   Alice = c(105, 110),
#'   Bob = c(100, 97),
#'   Charlie = c(90, 95)
#' )
#' process_wide(wide, ids = "year", key = "person")
#' process_wide(wide, ids = "year", key = "person") %>% static_plot
process_wide <- function(x, ids, key, color_id = "lightgray", ...) {

  if (!all(ids %in% names(x)))
    stop("all ids must be in x")

  nr <- nrow(x)
  nc <- ncol(x)
  key_values <- names(x)
  key_values <- key_values[!key_values %in% ids]

  id_values <- x %>% select(one_of(ids))
  id_values <- id_values %>% gather(key = ".key_map", value = ".id_map")

  x <- x %>% mutate(.r = row_number()) %>%
    unite(one_of(ids), col = ".id_map", remove = F)

  x <- x %>%
    gather(key = ".col", value = ".val", names(x) %>% str_subset("^[^\\.]")) %>%
    mutate(.key_map = .col,
           .type = ifelse(.col %in% ids, "id", "value"),
           .val = as.character(.val),
           .x = rep(1:nc, each = nr),
           .y = -rep(1:nr, nc),
           .header = F)

  # make sure that we have one id value per key
  tmp <- x %>% filter(.key_map %in% ids)
  x <- bind_rows(
    left_join(tmp %>% select(-.key_map),
              tmp %>% select(.id_map) %>% crossing(.key_map = key_values),
              by = ".id_map"),
    x %>% filter(!.key_map %in% ids)
  )

  # add header:
  crosser <- crossing(.id_map = as.character(id_values$.id_map),
                      .key_map = key_values)
  key_header <- data_frame(
    .key_map = key_values,
    .r = 0,
    .col = key_values,
    .val = key_values,
    .type = "key",
    .x = length(ids) + 1:length(key_values),
    .y = 0,
    .header = TRUE) %>%
    left_join(crosser, by = ".key_map")

  id_header <- left_join(
    data_frame(.id_map = ids,
               .r = 0,
               .col = ids,
               .val = ids,
               .type = "id",
               .x = 1:length(ids),
               .y = 0,
               .header = TRUE),
    crossing(.id_map = ids, .key_map = key_values),
    by = ".id_map"
  )

  x <- bind_rows(id_header, key_header, x)

  x <- x %>% unite(.key_map, .id_map, .val, col = ".id", remove = F)

  x %>%
    add_color_tidyr(key_values = key_values) %>%
    mutate(.alpha = ifelse(.header == TRUE, 1, 0.6))
}

#' Processes a long dataframe and converts it into a dataset that can be plotted
#'
#' @param x a long data frame
#' @param ids a vector of id-variables that are already in the tidy-format
#' @param key a vector of key-variables
#' @param ...
#'
#' @return
#'
#' @examples
#' long <- data_frame(
#'   year = c(2010L, 2011L, 2010L, 2011L, 2010L, 2011L),
#'   person = c("Alice", "Alice", "Bob", "Bob", "Charlie", "Charlie"),
#'   sales = c(105, 110, 100, 97, 90, 95)
#' )
#' process_long(long, ids = "year", key = "person", value = "sales")
#' process_long(long, ids = "year", key = "person", value = "sales") %>% static_plot
process_long <- function(x, ids, key, value, ...) {

  if (!all(c(ids, key, value) %in% names(x)))
    stop("all ids, key, and value must be names of x")

  nr <- nrow(x)
  nc <- ncol(x)
  xn <- names(x)

  x <- x %>% mutate(.r = row_number()) %>%
    unite(ids, col = ".id_map", remove = F) %>%
    unite(key, col = ".key_map", remove = F)

  key_values <- x %>% pull(key) %>% unique()

  type_dict <- c(rep("id", length(ids)), rep("key", length(key)), rep("value", length(value)))
  names(type_dict) <-  c(ids, key, value)

  x_dict <- 1:nc
  names(x_dict) <- xn

  x <- x %>%
    gather(key = ".col", value = ".val", names(x) %>% str_subset("^[^\\.]")) %>%
    mutate(
      .x = x_dict[.col],
      .y = -rep(1:nr, nc),
      .type = type_dict[.col],
      .val = as.character(.val),
      .header = FALSE
    )

  # add headers:

  id_headers <- crossing(.id_map = ids, # x$.id_map %>% unique()
                         .key_map = key_values,
                         ) %>%
    mutate(
      .r = 0,
      .col = "id",
      .val = .id_map,
      .x = x_dict[.val],
      .y = 0,
      .type = "id",
      .header = TRUE
  )

  x <- x %>%
    add_row(
      .before = T,
      .id_map = c(rep("key", length(key)), rep("value", length(value))),
      .key_map = c(rep("key", length(key)), rep("value", length(value))),
      .r = 0,
      .col = c(rep("key", length(key)), rep("value", length(value))),
      .val = c(key, value),
      .x = length(ids) + 1:length(c(key, value)),
      .y = 0,
      .type = c(rep("key", length(key)), rep("value", length(value))),
      .header = TRUE
    )

  x <- bind_rows(id_headers, x)

  x <- x %>%
    unite(.key_map, .id_map, .val, col = ".id", remove = F)

  x %>% add_color_tidyr(key_values = key_values) %>%
    mutate(.alpha = ifelse(.header == TRUE, 1, 0.6))
}

#' Animates a gather or spread function
#'
#' internally used by animate_spread and animate_gather
#'
#' @param lhs the (processed) dataset on the left-side
#' @param rhs the (processed) dataset on the right-side
#' @param sequence a named vector of the sequence titles
#'          (current_state, final_state, operation, and reverse_operation)
#' @param key_values the unique key-values
#' @param export the export type, either gif, first or last. The latter two
#'              export ggplots of the first/last state of the join
#' @param detailed boolean value if the animation should show one step for each
#'              key value
#' @param ... further arguments passed to animate_plot
#'
#' @return the plot or the gif
#'
#' @examples
#' NULL
gather_spread <- function(lhs, rhs, sequence, key_values, export, detailed, ...) {
  # lhs is the one state of the df
  # rhs is the target state

  # animate the four steps: inital with sequence[["current_state]],
  # transformations by the unique key-values with sequence[["operation"]],
  # final with sequence[["final_state"]]
  # and back transformation with sequence[["reverse_operation]]

  # have lhs and rhs in the right format: preprocessed with ids, .x, .y etc.
  # have a color function that makes coloring easier
  # transformations: for each key-variable: respective ids "fly in", keys fly in and ids fly in (all in one step for one key. i.e., Alice)

  # how much is the rhs to the left of lhs?

  if (!detailed) {
    anim_df <- bind_rows(
      lhs %>% mutate(.frame = 0),
      rhs %>% mutate(.frame = 1)
    )
    frame_labels <- c(sequence[["operation"]], sequence[["reverse_operation"]])

    title_string <- "{ifelse(transitioning, previous_state, ifelse(grepl('gather', next_state), 'Wide', 'Long'))}"

    tl <- 2
    sl <- 1

  } else {
    xshift <- 2

    rhs <- rhs %>% mutate(.x = .x + max(lhs$.x) + xshift)
    # the header rows
    header_start <- lhs %>% filter(.header == TRUE, !.key_map %in% key_values)
    header_end <- rhs %>% filter(.header == TRUE)

    state_start <- lhs %>% mutate(.frame = 0)
    state_end <- rhs %>% mutate(.frame = length(key_values) + 2)

    step_0 <- lhs %>% mutate(.frame = 1)
    # for each unique key-value move the respective entries
    keys_remaining <- lhs %>% filter(.key_map %in% key_values)
    keys_shifted <- lhs[0, ]
    key_steps <- lhs[0, ]
    f <- 1
    ids_remaining <- lhs %>% filter(.type == "id" & .header == FALSE)

    for (keyval in key_values) {
      f <- f + 1
      move_rhs <- rhs %>% filter(.key_map == keyval)

      keys_remaining <- keys_remaining %>% filter(.key_map != keyval)

      if (keyval == key_values[length(key_values)]) {
        header_start <- NULL
      }
      hd <- header_end %>% filter(.key_map == keyval |
                                    (.type %in% c("key", "value") &
                                       .col %in% c("key", "value")))
      keys_shifted <- bind_rows(keys_shifted, move_rhs)
      round_n <- bind_rows(header_start, hd,
                           keys_remaining, keys_shifted) %>%
        mutate(.frame = f)

      key_steps <- bind_rows(key_steps, round_n)
    }

    anim_df <- bind_rows(state_start, step_0, key_steps, state_end)

    # form the .frame as proper factors
    frame_labels <- c(
      sequence[["current_state"]],
      paste(sequence[["operation"]], key_values),
      sequence[["final_state"]],
      sequence[["reverse_operation"]]
    )
    title_string <- "{gsub('\\\\) [a-zA-Z]+$', ')', previous_state)}"

    tl <- length(unique(anim_df$.frame)) * 2
    sl <- 1
  }

  frame_levels <- anim_df$.frame %>% unique()

  anim_df <- anim_df %>%
    mutate(.frame = factor(.frame,
                           levels = frame_levels,
                           labels = frame_labels))

  if (export == "gif") {
    animate_plot(anim_df, title = title_string, transition_length = tl, state_length = sl) #...
  } else if (export == "first") {
    static_plot(state_start) #....
  } else if (export == "last") {
    static_plot(state_end) #....
  }

  # open issues: ... doesnt work properly.
  # especially if the id-arguments are passed in the gather-style, i.e., -year, or year:var
}
