///|
/// A plotting primitive stored by a figure.
pub(all) enum Artist {
  Line(Array[@core.Point], @core.Style)
  Scatter(Array[@core.Point], @core.Style)
  PathCollection(PathCollection)
  Bar(Array[@core.Point], @core.Style)
  BarH(Array[@core.Point], @core.Style)
  FillBetween(Array[@core.Point], Array[@core.Point], @core.Style)
  Polygon(Array[@core.Point], @core.Style)
  CompoundPolygon(Array[Array[@core.Point]], @core.Style)
  GouraudTriangle(Array[@core.Point], Array[String])
  HLine(Double, @core.Style)
  VLine(Double, @core.Style)
  AxLine(@core.Point, @core.Point, @core.Style)
  HSpan(Double, Double, @core.Style)
  VSpan(Double, Double, @core.Style)
  Text(Double, Double, String, @core.Style)
  Annotation(Annotation)
  ErrorBar(Array[@core.Point], Array[Double], Array[Double], @core.Style)
  Image(Array[Array[Double]], Double?, Double?, @colors.ColorMap)
  ImageNorm(
    Array[Array[Double]],
    @colors.ColorNorm,
    @colors.ColorMap,
    ImageOrigin,
    (Double, Double, Double, Double)?,
    Double,
    ImageInterpolation
  )
  /// An image whose pixels already carry RGB or RGBA colors.
  ColorImage(
    Array[Array[@colors.Rgba]],
    ImageOrigin,
    (Double, Double, Double, Double)?,
    Double,
    ImageInterpolation
  )
  Table(Table)
  Pie(Array[Double], Array[String], Array[String], Double)
  BoxPlot(Array[@boxplot.BoxStats], Bool, @core.Style)
  Quiver(Array[@core.Point], Array[@core.Point], @core.Style)
  Violin(
    Array[ViolinStats],
    Array[Double],
    Array[Double],
    Bool,
    ViolinSide,
    Bool,
    Bool,
    Bool,
    @core.Style,
    @core.Style,
    Double
  )
}

///|
/// The line and arrow artists produced by one streamplot call.
///
/// The arrays contain copies of the artists added to the owning axes, so the
/// returned value can be inspected without changing the figure.
pub struct StreamplotSet {
  lines : Array[Artist]
  arrows : Array[Artist]
}

///|
/// Construct a streamplot result from its line and arrow artists.
pub fn streamplot_set(
  lines : Array[Artist],
  arrows : Array[Artist],
) -> StreamplotSet {
  { lines: lines.copy(), arrows: arrows.copy(), }
}

///|
/// Return the streamline artists generated by this result.
pub fn StreamplotSet::lines(self : StreamplotSet) -> Array[Artist] {
  self.lines.copy()
}

///|
/// Return the arrow artists generated by this result.
pub fn StreamplotSet::arrows(self : StreamplotSet) -> Array[Artist] {
  self.arrows.copy()
}

///|
fn copy_points(points : Array[@core.Point]) -> Array[@core.Point] {
  points.copy()
}

///|
fn copy_artist(artist : Artist) -> Artist {
  match artist {
    Artist::Line(points, style) => Artist::Line(copy_points(points), style)
    Artist::Scatter(points, style) =>
      Artist::Scatter(copy_points(points), style)
    Artist::PathCollection(collection) =>
      Artist::PathCollection(copy_path_collection(collection))
    Artist::Bar(points, style) => Artist::Bar(copy_points(points), style)
    Artist::BarH(points, style) => Artist::BarH(copy_points(points), style)
    Artist::FillBetween(upper, lower, style) =>
      Artist::FillBetween(copy_points(upper), copy_points(lower), style)
    Artist::Polygon(points, style) =>
      Artist::Polygon(copy_points(points), style)
    Artist::CompoundPolygon(polygons, style) => {
      let copied : Array[Array[@core.Point]] = []
      for polygon in polygons {
        copied.push(copy_points(polygon))
      }
      Artist::CompoundPolygon(copied, style)
    }
    Artist::GouraudTriangle(points, colors) =>
      Artist::GouraudTriangle(copy_points(points), colors.copy())
    Artist::HLine(value, style) => Artist::HLine(value, style)
    Artist::VLine(value, style) => Artist::VLine(value, style)
    Artist::AxLine(point, direction, style) =>
      Artist::AxLine(point, direction, style)
    Artist::HSpan(lower, upper, style) => Artist::HSpan(lower, upper, style)
    Artist::VSpan(lower, upper, style) => Artist::VSpan(lower, upper, style)
    Artist::Text(x, y, value, style) => Artist::Text(x, y, value, style)
    Artist::Annotation(value) => Artist::Annotation(value)
    Artist::ErrorBar(points, yerr, xerr, style) =>
      Artist::ErrorBar(copy_points(points), yerr.copy(), xerr.copy(), style)
    Artist::Image(data, vmin, vmax, cmap) => {
      let copied = Array(capacity=data.length())
      for row in data {
        copied.push(row.copy())
      }
      Artist::Image(copied, vmin, vmax, cmap)
    }
    Artist::ImageNorm(data, norm, cmap, origin, extent, alpha, interpolation) => {
      let copied = Array(capacity=data.length())
      for row in data {
        copied.push(row.copy())
      }
      Artist::ImageNorm(
        copied, norm, cmap, origin, extent, alpha, interpolation,
      )
    }
    Artist::ColorImage(data, origin, extent, alpha, interpolation) => {
      let copied = Array(capacity=data.length())
      for row in data {
        copied.push(row.copy())
      }
      Artist::ColorImage(copied, origin, extent, alpha, interpolation)
    }
    Artist::Table(table) => Artist::Table(copy_table(table))
    Artist::Pie(values, labels, colors, start_angle) =>
      Artist::Pie(values.copy(), labels.copy(), colors.copy(), start_angle)
    Artist::BoxPlot(stats, horizontal, style) =>
      Artist::BoxPlot(stats.copy(), horizontal, style)
    Artist::Quiver(points, vectors, style) =>
      Artist::Quiver(points.copy(), vectors.copy(), style)
    Artist::Violin(
      stats,
      positions,
      widths,
      horizontal,
      side,
      showmeans,
      showextrema,
      showmedians,
      body_style,
      line_style,
      opacity
    ) =>
      Artist::Violin(
        stats.copy(),
        positions.copy(),
        widths.copy(),
        horizontal,
        side,
        showmeans,
        showextrema,
        showmedians,
        body_style,
        line_style,
        opacity,
      )
  }
}