const AStr = AbstractString
const ColorType = Union{Symbol,Colorant,PlotUtils.ColorSchemes.ColorScheme,Integer}
const TicksType = Union{AVec{Real},Tuple{AVec{Real},AVec{AStr}},Symbol,Bool,Nothing}

# NOTE: when updating `arg_desc`, don't forget to modify `PlotDocs.make_attr_df` accordingly.
const _arg_desc = KW(
    # series args
    :label              => (AStr, "The label for a series, which appears in a legend. If empty, no legend entry is added."),
    :seriescolor        => (ColorType, "The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot. Also describes the colormap for surfaces."),
    :seriesalpha        => (Real, "The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color."),
    :seriestype         => (Symbol, "This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined."),
    :linestyle          => (Symbol, "Style of the line (for path and bar stroke). Choose from $(_allStyles)"),
    :linewidth          => (Real, "Width of the line (in pixels)."),
    :linecolor          => (ColorType, "Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default)."),
    :linealpha          => (Real, "The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor."),
    :fillrange          => (Union{Real,AVec}, "Fills area between fillrange and `y` for line-types, sets the base for `bar`, `sticks` types, and similar for other types."),
    :fillcolor          => (ColorType, "Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`."),
    :fillalpha          => (Real, "The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor."),
    :markershape        => (Union{Symbol,Shape,AVec}, "Choose from $(_allMarkers)."),
    :fillstyle          => (Symbol, "Style of the fill area. `nothing` (the default) means solid fill. Choose from :/, :\\, :|, :-, :+, :x."),
    :markercolor        => (ColorType, "Color of the interior of the marker or shape. `:match` will take the value from `:seriescolor`."),
    :markeralpha        => (Real, "The alpha/opacity override for the marker interior. `nothing` (the default) means it will take the alpha value of markercolor."),
    :markersize         => (Union{Real,AVec}, "Size (radius pixels) of the markers."),
    :markerstrokestyle  => (Symbol, "Style of the marker stroke (border). Choose from $(_allStyles)."),
    :markerstrokewidth  => (Real, "Width of the marker stroke (border) in pixels."),
    :markerstrokecolor  => (ColorType, "Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`."),
    :markerstrokealpha  => (Real, "The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor."),
    :bins               => (Union{Integer,NTuple{2,Integer},AVec,Symbol}, """
                        Default is :auto (the Freedman-Diaconis rule). For histogram-types, defines the approximate number of bins to aim for, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd).
                        For fine-grained control pass a Vector of break values, e.g. `range(minimum(x), stop = maximum(x), length = 25)`."""),
    :smooth             => (Bool, "Add a regression line ?"),
    :group              => (AVec, "Data is split into a separate series, one for each unique value in `group`."),
    :x                  => (Any, "Input data (first dimension)."),
    :y                  => (Any, "Input data (second dimension)."),
    :z                  => (Any, "Input data (third dimension). May be wrapped by a `Surface` for surface and heatmap types."),
    :marker_z           => (Union{AVec,Function}, "z-values for each series data point, which correspond to the color to be used from a markercolor gradient (`f(x,y,z) -> z_value` or `f(x,y) -> z_value`)."),
    :line_z             => (Union{AVec,Function}, "z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment)."),
    :fill_z             => (AMat, "Matrix of the same size as z matrix, which specifies the color of the 3D surface."),
    :levels             => (Union{AVec,Integer}, "Singleton for number of contours or iterable for contour values. Determines contour levels for a contour type."),
    :permute            => (NTuple{2,Symbol}, "Permutes data and axis properties of the axes given in the tuple, e.g. (:x, :y)."),
    :orientation        => (Symbol, "(deprecated in favor of `:permute`) Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default)."),
    :bar_position       => (Symbol, "Choose from `:overlay` (default), `:stack`. (warning: may only be partially implemented)."),
    :bar_width          => (Real, " Width of bars in data coordinates. When `nothing`, chooses based on `x` (or `y` when `orientation = :h`)."),
    :bar_edges          => (Bool, "Align bars to edges (true), or centers (the default) ?"),
    :xerror             => (Union{AVec,NTuple{2,AVec}}, "`x` (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)."),
    :yerror             => (Union{AVec,NTuple{2,AVec}}, "`y` (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)."),
    :ribbon             => (Union{Real,AVec}, "Creates a fillrange around the data points."),
    :quiver             => (Union{AVec,NTuple{2,AVec}}, "The directional vectors U,V which specify velocity/gradient vectors for a quiver plot."),
    :arrow              => (Union{Bool,Arrow}, "Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar."),
    :normalize          => (Union{Bool,Symbol}, "Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a discrete PDF, where the total area of the bins is 1), :probability (bin heights sum to 1) and :density (the area of each bin, rather than the height, is equal to the counts - useful for uneven bin sizes)."),
    :weights            => (AVec, "Used in histogram types for weighted counts."),
    :show_empty_bins    => (Bool, "Whether empty bins in a 2D histogram are colored as 0 (true), or transparent (the default)."),
    :contours           => (Bool, "Add contours to the side-grids of 3D plots?  Used in surface/wireframe."),
    :contour_labels     => (Bool, "Show labels at the contour lines ?"),
    :match_dimensions   => (Bool, "For heatmap types: should the first dimension of a matrix (rows) correspond to the first dimension of the plot (`x`-axis) ? Defaults to `false`, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for `z`, the function should still map `(x,y) -> z`."),
    :subplot            => (Union{Integer,Subplot}, "The subplot that this series belongs to."),
    :series_annotations => (Union{AVec,AStr,PlotText}, "These are annotations which are mapped to data points/positions."),
    :primary            => (Bool, "Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as two separate series, maybe with different data (see `sticks` recipe for an example). The secondary series will get the same color, etc as the primary."),
    :hover              => (AVec{AStr}, "Text to display when hovering over each data point."),
    :colorbar_entry     => (Bool, "Include this series in the color bar?  Set to `false` to exclude."),
    :z_order            => (Union{Symbol,Integer}, ":front (default), :back or index of position where 1 is furthest in the background."),

    # plot args
    :plot_title               => (AStr, "Whole plot title (not to be confused with the title for individual subplots)."),
    :plot_titlevspan          => (Real, "Vertical span of the whole plot title (fraction of the plot height)."),
    :background_color         => (ColorType, " Base color for all backgrounds."),
    :background_color_outside => (ColorType, "Color outside the plot area(s) (`:match` matches `:background_color`)."),
    :foreground_color         => (ColorType, "Base color for all foregrounds."),
    :size                     => (NTuple{2,Integer}, "(width_px, height_px) of the whole Plot."),
    :pos                      => (NTuple{2,Integer}, "(left_px, top_px) position of the GUI window (note: currently unimplemented)."),
    :window_title             => (AStr, "Title of the standalone gui-window."),
    :show                     => (Bool, "Should this command open/refresh a GUI/display ? Allows to display plots in scripts or functions without explicitly calling `display`."),
    :layout                   => (Union{Integer,NTuple{2,Integer},AbstractLayout}, "Number of subplot, grid dimensions, layout (for example `grid(2,2)`), or the return from the `@layout` macro. This builds the layout of subplots."),
    :link                     => (Symbol, "How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position)."),
    :overwrite_figure         => (Bool, "Should we reuse the same GUI window/figure when plotting (true) or open a new one (false)."),
    :html_output_format       => (Symbol, "When writing html output, what is the format?  `:png` and `:svg` are currently supported."),
    :tex_output_standalone    => (Bool, "When writing tex output, should the source include a preamble for a standalone document class."),
    :inset_subplots           => (AVec{NTuple{2,Any}}, "Optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots."),
    :dpi                      => (Real, "Dots Per Inch of output figures."),
    :thickness_scaling        => (Real, "Scale for the thickness of all line elements like lines, borders, axes, grid lines, ... defaults to 1."),
    :display_type             => (Symbol, "When supported, `display` will either open a GUI window or plot inline. Choose from (`:auto`, `:gui`, or `:inline`)."),
    :extra_kwargs             => (Symbol, """
                                 Specify for which element extra keyword args are collected or a KW (Dict{Symbol,Any}) to pass a map of extra keyword args which may be specific to a backend. Choose from (`:plot`, `:subplot`, `:series`), defaults to `:series`.
                                 Example: `pgfplotsx(); scatter(1:5, extra_kwargs=Dict(:subplot=>Dict("axis line shift" => "10pt"))`."""),
    :fontfamily               => (Union{AStr,Symbol}, "Default font family for title, legend entries, tick labels and guides."),
    :warn_on_unsupported      => (Bool, "Warn on unsupported attributes, series types and marker shapes."),

    # subplot args
    :title                       => (AStr, "Subplot title."),
    :titlelocation               => (Symbol, "Position of subplot title. Choose from (`:left`, `:center`, `:right`)."),
    :titlefontfamily             => (Union{AStr,Symbol}, "Font family of subplot title."),
    :titlefontsize               => (Integer, "Font pointsize of subplot title."),
    :titlefonthalign             => (Symbol, "Font horizontal alignment of subplot title. Choose from (:hcenter, :left, :right, :center)."),
    :titlefontvalign             => (Symbol, "Font vertical alignment of subplot title. Choose from (:vcenter, :top, :bottom, :center)."),
    :titlefontrotation           => (Real, "Font rotation of subplot title."),
    :titlefontcolor              => (ColorType, "Color Type. Font color of subplot title."),
    :background_color_subplot    => (ColorType, "Base background color of the subplot (`:match` matches `:background_color`)."),
    :legend_background_color     => (ColorType, "Background color of the legend (`:match` matches :background_color_subplot`)."),
    :background_color_inside     => (ColorType, "Background color inside the plot area (under the grid) (`:match` matches :background_color_subplot`)."),
    :foreground_color_subplot    => (ColorType, "Base foreground color of the subplot (`:match` matches :foreground_color`)."),
    :legend_foreground_color     => (ColorType, "Foreground color of the legend (`:match` matches :foreground_color_subplot`)."),
    :foreground_color_title      => (ColorType, "Color of subplot title (`:match` matches :foreground_color_subplot`)."),
    :color_palette               => (Union{AVec{ColorType},Symbol}, "Iterable (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen."),
    :legend_position             => (Union{Bool,NTuple{2,Real},Symbol}, """
                                    Show the legend ? Can also be a (x,y) tuple or Symbol (legend position) or angle (angle,inout) tuple. Bottom left corner of legend is placed at (x,y).
                                    Choose from (`:none`, `:best`, `:inline`, `:inside`, `:legend`) or any valid combination of `:(outer ?)(top/bottom ?)(right/left ?)`, i.e.: `:top`, `:topright`, `:outerleft`, `:outerbottomright` ... (note: only some may be supported in each backend)."""),
    :legend_column               => (Integer, "Number of columns in the legend. `-1` stands for maximum number of colums (horizontal legend)."),
    :legend_title_font           => (Font, "Font of the legend title."),
    :legend_font_family          => (Union{AStr,Symbol}, "Font family of legend entries."),
    :legend_font_pointsize       => (Integer, "Font pointsize of legend entries."),
    :legend_font_halign          => (Symbol, "Font horizontal alignment of legend entries. Choose from (:hcenter, :left, :right, :center)."),
    :legend_font_valign          => (Symbol, "Font vertical alignment of legend entries. Choose from (:vcenter, :top, :bottom, :center)."),
    :legend_font_rotation        => (Real, "Font rotation of legend entries."),
    :legend_title_font_color     => (ColorType, "Font color of legend entries."),
    :legend_title                => (AStr, "Legend title."),
    :legend_title_font_family    => (Union{AStr,Symbol}, "Font family of the legend title."),
    :legend_title_font_pointsize => (Integer, "Font pointsize the legend title."),
    :legend_title_font_halign    => (Symbol, "Font horizontal alignment of the legend title. Choose from (:hcenter, :left, :right, :center)."),
    :legend_title_font_valign    => (Symbol, "Font vertical alignment of the legend title. Choose from (:vcenter, :top, :bottom, :center)."),
    :legend_title_font_rotation  => (Real, "Font rotation of the legend title."),
    :legend_title_font_color     => (ColorType, "Font color of the legend title."),
    :colorbar                    => (Union{Bool,Symbol}, "Show the colorbar ? A symbol specifies a colorbar position. Choose from (`:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend`): `legend` matches legend value (note: only some may be supported in each backend)."),
    :clims                       => (Union{NTuple{2,Real},Symbol,Function}, "Fixes the limits of the colorbar: values, `:auto`, or a function taking series data in and returning a NTuple{2,Real}."),
    :colorbar_fontfamily         => (Union{AStr,Symbol}, "Font family of colorbar entries."),
    :colorbar_ticks              => (TicksType, "Tick values, (tickvalues, ticklabels), `:auto`/`true`, or `:none`/`false`/`nothing` (ticks disabled)."),
    :colorbar_tickfontfamily     => (Union{AStr,Symbol}, "String or Symbol. Font family of colorbar tick labels."),
    :colorbar_tickfontsize       => (Integer, "Font pointsize of colorbar tick entries."),
    :colorbar_tickfontcolor      => (ColorType, "Font color of colorbar tick entries."),
    :colorbar_scale              => (Symbol, "Scale of the colorbar axis. Choose from $(_allScales)."),
    :colorbar_formatter          => (Union{Function,Symbol}, "Choose from (:scientific, :plain, :none, :auto), or a method which converts a number to a string for tick labeling."),
    :legend_font                 => (Font, "Font of legend items."),
    :legend_titlefont            => (Font, "Font of the legend title."),
    :annotations                 => (Union{AVec{Tuple},Tuple{Real,Real,Union{AStr,PlotText,Tuple}}}, "(x,y,text) tuple(s), where text can be String, PlotText (created with `text(args...)`), or a tuple of arguments to `text` (e.g., `(\"Label\", 8, :red, :top)`). Add one-off text annotations at the (x,y) coordinates."),
    :annotationfontfamily        => (Union{AStr,Symbol}, "Font family of annotations."),
    :annotationfontsize          => (Integer, "Font pointsize of annotations."),
    :annotationhalign            => (Symbol, "horizontal alignment of annotations. Choose from (:hcenter, :left, :right, :center)."),
    :annotationvalign            => (Symbol, "Vertical alignment of annotations. Choose from (:vcenter, :top, :bottom, :center)."),
    :annotationrotation          => (Real, "Rotation of annotations in degrees."),
    :annotationcolor             => (ColorType, "Annotations color."),
    :projection                  => (Union{AStr,Symbol}, "`3d` or `polar`."),
    :projection_type             => (Symbol, "3d plots projection type: :auto (backend dependent), :persp(ective), :ortho(graphic)."),
    :aspect_ratio                => (Union{Symbol,Real}, "Plot area is resized so that 1 y-unit is the same size as `aspect_ratio` x-units. With `:none`, images inherit aspect ratio of the plot area. Use `:equal` for unit aspect ratio."),
    :margin                      => (Union{Tuple,Real}, "Number multiplied by `mm`, `px`, etc... or Tuple `(0, :mm)`. Base for individual margins... not directly used. Specifies the extra padding around subplots."),
    :left_margin                 => (Union{Tuple,Real,Symbol}, "Specifies the extra padding to the left of the subplot (`:match` matches `:margin`)."),
    :top_margin                  => (Union{Tuple,Real,Symbol}, "Specifies the extra padding on the top of the subplot (`:match` matches `:margin`)."),
    :right_margin                => (Union{Tuple,Real,Symbol}, "Specifies the extra padding to the right of the subplot (`:match` matches `:margin`)."),
    :bottom_margin               => (Union{Tuple,Real,Symbol}, "Specifies the extra padding on the bottom of the subplot (`:match` matches `:margin`)."),
    :subplot_index               => (Integer, "Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list."),
    :colorbar_title              => (AStr, "Title of colorbar."),
    :framestyle                  => (Symbol, "Style of the axes frame. Choose from $(_allFramestyles)."),
    :camera                      => (NTuple{2,Real}, "Sets the view angle (azimuthal, elevation) for 3D plots."),

    # axis args
    :guide                       => (AStr, "Axis guide (label)."),
    :guide_position              => (Symbol, "Position of axis guides. Choose from (:top, :bottom, :left, :right)."),
    :lims                        => (Union{NTuple{2,Real},Symbol}, """
                                    Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).
                                    `:round` widens the limit to the nearest round number, i.e. [0.1,3.6]=>[0.0,4.0].
                                    `:symmetric` sets the limits to be symmetric around zero.
                                    Set `widen=true` to widen the specified limits (as occurs when lims are not specified)."""),
    :ticks                       => (TicksType, "Tick values, (tickvalues, ticklabels), `:auto`/`true`, `:none`/`false`/`nothing` (ticks disabled), or `:native` (tells backend to calculate ticks by itself; good idea for interactive backends with mouse zooming)."),
    :scale                       => (Symbol, "Scale of the axis. Choose from $(_allScales)."),
    :rotation                    => (Real, "Degrees rotation of tick labels."),
    :flip                        => (Bool, "Should we flip (reverse) the axis ?"),
    :formatter                   => (Union{Symbol,Function}, "Choose from (:scientific, :plain or :auto), or a method which converts a number to a string for tick labeling."),
    :tickfontfamily              => (Union{AStr,Symbol}, "Font family of tick labels."),
    :tickfontsize                => (Integer, "Font pointsize of tick labels."),
    :tickfonthalign              => (Symbol, "Font horizontal alignment of tick labels. Choose from (:hcenter, :left, :right, :center)."),
    :tickfontvalign              => (Symbol, "Font vertical alignment of tick labels. Choose from (:vcenter, :top, :bottom, :center)."),
    :tickfontrotation            => (Real, "Font rotation of tick labels."),
    :tickfontcolor               => (ColorType, "Font color of tick labels."),
    :guidefontfamily             => (Union{AStr,Symbol}, "Font family of axes guides."),
    :guidefontsize               => (Integer, "Font pointsize of axes guides."),
    :guidefonthalign             => (Symbol, "Font horizontal alignment of axes guides. Choose from (:hcenter, :left, :right, :center)."),
    :guidefontvalign             => (Symbol, "Font vertical alignment of axes guides. Choose from (:vcenter, :top, :bottom, :center)."),
    :guidefontrotation           => (Real, "Font rotation of axes guides."),
    :guidefontcolor              => (ColorType, "Font color of axes guides."),
    :foreground_color_axis       => (ColorType, "Color of axis ticks (`:match` matches `:foreground_color_subplot`)."),
    :foreground_color_border     => (ColorType, "Color of plot area border/spines (`:match` matches `:foreground_color_subplot`)."),
    :foreground_color_text       => (ColorType, "Color of tick labels (`:match` matches `:foreground_color_subplot`)."),
    :foreground_color_guide      => (ColorType, "Color of axis guides/labels (`:match` matches `:foreground_color_subplot`)."),
    :mirror                      => (Bool, "Switch the side of the tick labels (right or top)."),
    :grid                        => (Union{Bool,Symbol,AStr}, "Show the grid lines ? `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:none`, `:off`."),
    :foreground_color_grid       => (ColorType, "Color of grid lines (`:match` matches `:foreground_color_subplot`)."),
    :gridalpha                   => (Real, "The alpha/opacity override for the grid lines."),
    :gridstyle                   => (Symbol, "Style of the grid lines. Choose from $(_allStyles)."),
    :gridlinewidth               => (Real, "Width of the grid lines (in pixels)."),
    :foreground_color_minor_grid => (ColorType, "Color of minor grid lines (`:match` matches `:foreground_color_subplot`)."),
    :minorgrid                   => (Bool, "Adds minor grid lines and ticks to the plot. Set minorticks to change number of gridlines."),
    :minorticks                  => (Integer, "Number of minor intervals between major ticks."),
    :minorgridalpha              => (Real, "The alpha/opacity override for the minorgrid lines."),
    :minorgridstyle              => (Symbol, "Style of the minor grid lines. Choose from $(_allStyles)."),
    :minorgridlinewidth          => (Real, "Width of the minor grid lines (in pixels)."),
    :tick_direction              => (Symbol, "Direction of the ticks. Choose from (`:in`, `:out`, `:none`)."),
    :showaxis                    => (Union{Bool,Symbol,AStr}, "Show the axis. `true`, `false`, `:show`, `:hide`, `:yes`, `:no`, `:x`, `:y`, `:z`, `:xy`, ..., `:all`, `:off`."),
    :widen                       => (Union{Bool,Real,Symbol}, """
                                    Widen the axis limits by a small factor to avoid cut-off markers and lines at the borders.
                                    If set to `true`, scale the axis limits by the default factor of $(default_widen_factor).
                                    A different factor may be specified by setting `widen` to a number.
                                    Defaults to `:auto`, which widens by the default factor unless limits were manually set.
                                    See also the `scale_limits!` function for scaling axis limits in an existing plot."""),
    :draw_arrow                  => (Bool, "Draw arrow at the end of the axis."),
    :unitformat                  => (Union{Bool,Nothing,Symbol,Char,String,NTuple{<:Union{Char,String}},Function}, """Check examples in https://docs.juliaplots.org/stable/generated/unitfulext_examples/#Unit-formatting"""),
)
