#' Generate palette suitable for coloring a set
#'
#' Takes a simple palette and expands / oscillates
#' it for use with Mandelbrot sets.
#'
#' @param palette vector of color hex strings (e.g. '#FFFFFF')
#' @param fold wrap or fold the palette back on itself
#' @param reps number of times to replicate the color vector
#' @param in_set color for areas in the Mandelbrot set
#'
#' @return an extended color vector
#'
#' @examples
#' view <- mandelbrot(xlim = c(-0.8438146, -0.8226294),
#'   ylim = c(0.1963144, 0.2174996), iter = 500)
#'
#' # can be used to simply interpolate a color gradient
#' spectral <- RColorBrewer::brewer.pal(11, "Spectral")
#' cols <- mandelbrot_palette(spectral, fold = FALSE)
#' plot(view, col = cols, transform = "inv")
#'
#' # simple palettes might need folds / reps to look good
#' blues <- RColorBrewer::brewer.pal(9, "Blues")
#' cols <- mandelbrot_palette(blues, in_set = "white",
#'   fold = TRUE, reps = 2)
#' plot(view, col = cols, transform = "log")
#'
#' @export
mandelbrot_palette <- function(palette, fold = TRUE,
  reps = 1L, in_set = "black") {

  if (!fold %in% c(TRUE, FALSE)) {
    stop("Fold must be TRUE or FALSE")
  }

  if (length(palette) < 100) {
    palette <- grDevices::colorRampPalette(palette)(100)
  }

  if (fold) {
    palette <- c(palette, rev(palette))
  }

  c(rep(palette, reps), in_set)
}

#' Plot a Mandelbrot set using base graphics
#'
#' Draws colored set membership using \code{image}.
#'
#' @param x an object generated by \code{\link[mandelbrot]{mandelbrot}}
#' @param col a vector of colors, such as those generated by
#'   \code{\link[mandelbrot]{mandelbrot_palette}}
#' @param transform the name of a transformation to apply to the number
#'   of iterations matrix
#' @param asp the \code{asp} parameter to \code{\link[graphics]{image}}
#'   which controls aspect ratio
#' @param ... extra arguments passed to \code{\link[graphics]{image}}
#'
#' @importFrom grDevices grey.colors
#'
#' @export
plot.mandelbrot <- function(x,
  col = mandelbrot_palette(c("white", grey.colors(50))),
  transform = c("none", "inverse", "log"), asp = 1, ...) {

  transform <- match.arg(transform)
  old_par <- par()

  par(mar = rep(1, 4))

  if (transform != "none") {
    if (transform == "inverse") {
      x$z <- 1/x$z
      col <- rev(col)
    } else {
      if (transform == "log") {
        x$z <- log(x$z)
      } else {
        stop("transform not recognised: ", transform)
      }
    }
  }

  graphics::image(x, col = col, axes = FALSE, asp = asp, ...)

  par <- old_par
}

#' @export
print.mandelbrot <- function(x, ...) {
  cat(" Mandelbrot set view object within limits x:",
    paste(range(x$x), collapse = ", "),
    "and y:",
    paste(range(x$y), collapse = ", "),
    "\n")
  cat(" Iterations matrix:",
    paste(dim(x$z), collapse = " x "))

   invisible(x)
}

#' Convert Mandelbrot object to data.frame for plotting
#'
#' Converts objects produced by \code{\link[mandelbrot]{mandelbrot}}
#' to tidy data.frames for use with ggplot and other tidyverse packages.
#'
#' @param x a Mandelbrot set object produced by \code{\link[mandelbrot]{mandelbrot}}
#' @param ... ignored
#'
#' @return a 3-column \code{data.frame}
#'
#' @examples
#'
#' mb <- mandelbrot()
#' df <- as.data.frame(mb)
#' head(df)
#'
#' @export
as.data.frame.mandelbrot <- function(x, ...) {
  df <- reshape2::melt(x$z)
  #df <- data.table::melt(mandelbrot$z)
  df$x <- x$x[df$Var1]
  df$y <- x$y[df$Var2]
  df[,c("x", "y", "value")]
}