from collections.abc import Iterable

import numpy as np
import matplotlib
import matplotlib.pyplot as plt

from . import COLORS
from ..base import hex2rgb

RC_PARAMS = {
    # ***************************************************************************
    # * BACKENDS                                                                *
    # ***************************************************************************
    "interactive": False,
    # ***************************************************************************
    # * LINES                                                                   *
    # ***************************************************************************
    "lines.linewidth": 1.8,  # line width in points
    # ***************************************************************************
    # * PATHCES                                                                 *
    # ***************************************************************************
    # ***************************************************************************
    # * HATHCES                                                                 *
    # ***************************************************************************
    # ***************************************************************************
    # * BOXPLOT                                                                 *
    # ***************************************************************************
    # ***************************************************************************
    # * FONT                                                                    *
    # ***************************************************************************
    # ***************************************************************************
    # * TEXT                                                                    *
    # ***************************************************************************
    # ***************************************************************************
    # * LATEX                                                                   *
    # ***************************************************************************
    # ***************************************************************************
    # * AXES                                                                    *
    # ***************************************************************************
    "axes.titlesize": "x-large",  # font size of the axes title
    "axes.formatter.use_mathtext": True,  # When True, use mathtext for scientific
    "axes.grid": True,  # display grid or not
    "axes.grid.which": "both",  # grid lines at {major, minor, both} ticks
    "grid.color": COLORS.LIGHT_GREY,  # grid color
    "axes.labelsize": "large",  # font size of the x and y labels
    "axes.prop_cycle": matplotlib.cycler(
        color=[
            COLORS.DARK_BLUE,
            COLORS.ORANGE_B,
            COLORS.PURPLE_D,
            COLORS.GREEN,
            COLORS.GREY_BROWN,
            COLORS.GOLD,
            COLORS.MAROON_D,
            COLORS.BLUE_C,
            COLORS.BLACK,
            COLORS.PINK,
        ]
    ),
    "axes.formatter.limits": [-4, 4],  # use scientific notation if log10
    # of the axis range is smaller than the
    # first or larger than the second
    "axes.spines.left": True,  # display axis spines
    "axes.spines.bottom": True,
    "axes.spines.top": False,
    "axes.spines.right": False,
    # ***************************************************************************
    # * DATES                                                                   *
    # ***************************************************************************
    # ***************************************************************************
    # * TICKS                                                                   *
    # ***************************************************************************
    # ***************************************************************************
    # * GRIDS                                                                   *
    # ***************************************************************************
    # ***************************************************************************
    # * LEGEND                                                                  *
    # ***************************************************************************
    # ***************************************************************************
    # * FIGURE                                                                  *
    # ***************************************************************************
    "figure.autolayout": True,  # When True, automatically adjust subplot
    "figure.figsize": [8, 8],  # figure size in inches
    "figure.dpi": 100,
    "figure.facecolor": "none",  # figure face color
    "figure.edgecolor": "none",  # figure edge color
    # ***************************************************************************
    # * IMAGES                                                                  *
    # ***************************************************************************
    # ***************************************************************************
    # * CONTOUR PLOTS                                                           *
    # ***************************************************************************
    # ***************************************************************************
    # * ERRORBAR PLOTS                                                          *
    # ***************************************************************************
    "errorbar.capsize": 3,
    # ***************************************************************************
    # * HISTOGRAM PLOTS                                                         *
    # ***************************************************************************
    # ***************************************************************************
    # * SCATTER PLOTS                                                           *
    # ***************************************************************************
    # ***************************************************************************
    # * AGG RENDERING                                                           *
    # ***************************************************************************
    # ***************************************************************************
    # * PATHS                                                                   *
    # ***************************************************************************
    # ***************************************************************************
    # * SAVING FIGURES                                                          *
    # ***************************************************************************
    "savefig.dpi": "figure",  # figure dots per inch or 'figure'
    "savefig.format": "png",  # {png, ps, pdf, svg}
    "savefig.bbox": "tight",  # {tight, standard}
    # ***************************************************************************
    # * INTERACTIVE KEYMAPS                                                     *
    # ***************************************************************************
    # ***************************************************************************
    # * ANIMATIONS                                                              *
    # ***************************************************************************
}

RC_PARAMS_DEFAULT = {
    ### MATPLOTLIBRC FORMAT
    # NOTE FOR END USERS: DO NOT EDIT THIS FILE!
    #
    # This is a sample Matplotlib configuration file - you can find a copy
    # of it on your system in site-packages/matplotlib/mpl-data/matplotlibrc
    # (relative to your Python installation location).
    #
    # You should find a copy of it on your system at
    # site-packages/matplotlib/mpl-data/matplotlibrc (relative to your Python
    # installation location).  DO NOT EDIT IT!
    #
    # If you wish to change your default style, copy this file to one of the
    # following locations:
    #     Unix/Linux:
    #         $HOME/.config/matplotlib/matplotlibrc OR
    #         $XDG_CONFIG_HOME/matplotlib/matplotlibrc (if $XDG_CONFIG_HOME is set)
    #     Other platforms:
    #         $HOME/.matplotlib/matplotlibrc
    # and edit that copy.
    #
    # See https://matplotlib.org/users/customizing.html#the-matplotlibrc-file
    # for more details on the paths which are checked for the configuration file.
    #
    # Blank lines, or lines starting with a comment symbol, are ignored, as are
    # trailing comments.  Other lines must have the format:
    #     key: val  # optional comment
    #
    # Formatting: Use PEP8-like style (as enforced in the rest of the codebase).
    # All lines start with an additional '#', so that removing all leading '#'s
    # yields a valid style file.
    #
    # Colors: for the color values below, you can either use
    #     - a Matplotlib color string, such as r, k, or b
    #     - an RGB tuple, such as (1.0, 0.5, 0.0)
    #     - a hex string, such as ff00ff
    #     - a scalar grayscale intensity such as 0.75
    #     - a legal html color name, e.g., red, blue, darkslategray
    #
    # Matplotlib configuration are currently divided into following parts:
    #     - BACKENDS
    #     - LINES
    #     - PATCHES
    #     - HATCHES
    #     - BOXPLOT
    #     - FONT
    #     - TEXT
    #     - LaTeX
    #     - AXES
    #     - DATES
    #     - TICKS
    #     - GRIDS
    #     - LEGEND
    #     - FIGURE
    #     - IMAGES
    #     - CONTOUR PLOTS
    #     - ERRORBAR PLOTS
    #     - HISTOGRAM PLOTS
    #     - SCATTER PLOTS
    #     - AGG RENDERING
    #     - PATHS
    #     - SAVING FIGURES
    #     - INTERACTIVE KEYMAPS
    #     - ANIMATION
    #### CONFIGURATION BEGINS HERE
    # ***************************************************************************
    # * BACKENDS                                                                *
    # ***************************************************************************
    # The default backend.  If you omit this parameter, the first working
    # backend from the following list is used:
    #     MacOSX Qt5Agg Gtk3Agg TkAgg WxAgg Agg
    # Other choices include:
    #     Qt5Cairo GTK3Cairo TkCairo WxCairo Cairo
    #     Qt4Agg Qt4Cairo Wx  # deprecated.
    #     PS PDF SVG Template
    # You can also deploy your own backend outside of Matplotlib by referring to
    # the module name (which must be in the PYTHONPATH) as 'module://my_backend'.
    "backend": "Agg",
    # The port to use for the web server in the WebAgg backend.
    "webagg.port": 8988,
    # The address on which the WebAgg web server should be reachable
    "webagg.address": "127.0.0.1",
    # If webagg.port is unavailable, a number of other random ports will
    # be tried until one that is available is found.
    "webagg.port_retries": 50,
    # When True, open the web browser to the plot that is shown
    "webagg.open_in_browser": True,
    # If you are running pyplot inside a GUI and your backend choice
    # conflicts, we will automatically try to find a compatible one for
    # you if backend_fallback is True
    "backend_fallback": True,
    "interactive": True,
    "toolbar": "toolbar2",  # {None, toolbar2, toolmanager}
    "timezone": "UTC",  # a pytz timezone string, e.g., US/Central or Europe/Paris
    # ***************************************************************************
    # * LINES                                                                   *
    # ***************************************************************************
    # See https://matplotlib.org/api/artist_api.html#module-matplotlib.lines
    # for more information on line properties.
    "lines.linewidth": 1.5,  # line width in points
    "lines.linestyle": "-",  # solid line
    "lines.color": "C0",  # has no affect on plot(); see axes.prop_cycle
    "lines.marker": None,  # the default marker
    "lines.markerfacecolor": "auto",  # the default marker face color
    "lines.markeredgecolor": "auto",  # the default marker edge color
    "lines.markeredgewidth": 1.0,  # the line width around the marker symbol
    "lines.markersize": 6,  # marker size, in points
    "lines.dash_joinstyle": "round",  # {miter, round, bevel}
    "lines.dash_capstyle": "butt",  # {butt, round, projecting}
    "lines.solid_joinstyle": "round",  # {miter, round, bevel}
    "lines.solid_capstyle": "projecting",  # {butt, round, projecting}
    "lines.antialiased": True,  # render lines in antialiased (no jaggies)
    # The three standard dash patterns.  These are scaled by the linewidth.
    "lines.dashed_pattern": [3.7, 1.6],
    "lines.dashdot_pattern": [6.4, 1.6, 1, 1.6],
    "lines.dotted_pattern": [1, 1.65],
    "lines.scale_dashes": True,
    "markers.fillstyle": "full",  # {full, left, right, bottom, top, none}
    "pcolor.shading": "flat",
    "pcolormesh.snap": True,  # Whether to snap the mesh to pixel boundaries. This
    # is provided solely to allow old test images to remain
    # unchanged. Set to False to obtain the previous behavior.
    # ***************************************************************************
    # * PATCHES                                                                 *
    # ***************************************************************************
    # Patches are graphical objects that fill 2D space, like polygons or circles.
    # See https://matplotlib.org/api/artist_api.html#module-matplotlib.patches
    # for more information on patch properties.
    "patch.linewidth": 1,  # edge width in points
    "patch.facecolor": "C0",
    "patch.edgecolor": "black",  # if forced, or patch is not filled
    "patch.force_edgecolor": False,  # True to always use edgecolor
    "patch.antialiased": True,  # render patches in antialiased (no jaggies)
    # ***************************************************************************
    # * HATCHES                                                                 *
    # ***************************************************************************
    "hatch.color": "black",
    "hatch.linewidth": 1.0,
    # ***************************************************************************
    # * BOXPLOT                                                                 *
    # ***************************************************************************
    "boxplot.notch": False,
    "boxplot.vertical": True,
    "boxplot.whiskers": 1.5,
    "boxplot.bootstrap": None,
    "boxplot.patchartist": False,
    "boxplot.showmeans": False,
    "boxplot.showcaps": True,
    "boxplot.showbox": True,
    "boxplot.showfliers": True,
    "boxplot.meanline": False,
    "boxplot.flierprops.color": "black",
    "boxplot.flierprops.marker": "o",
    "boxplot.flierprops.markerfacecolor": "none",
    "boxplot.flierprops.markeredgecolor": "black",
    "boxplot.flierprops.markeredgewidth": 1.0,
    "boxplot.flierprops.markersize": 6,
    "boxplot.flierprops.linestyle": "none",
    "boxplot.flierprops.linewidth": 1.0,
    "boxplot.boxprops.color": "black",
    "boxplot.boxprops.linewidth": 1.0,
    "boxplot.boxprops.linestyle": "-",
    "boxplot.whiskerprops.color": "black",
    "boxplot.whiskerprops.linewidth": 1.0,
    "boxplot.whiskerprops.linestyle": "-",
    "boxplot.capprops.color": "black",
    "boxplot.capprops.linewidth": 1.0,
    "boxplot.capprops.linestyle": "-",
    "boxplot.medianprops.color": "C1",
    "boxplot.medianprops.linewidth": 1.0,
    "boxplot.medianprops.linestyle": "-",
    "boxplot.meanprops.color": "C2",
    "boxplot.meanprops.marker": "^",
    "boxplot.meanprops.markerfacecolor": "C2",
    "boxplot.meanprops.markeredgecolor": "C2",
    "boxplot.meanprops.markersize": 6,
    "boxplot.meanprops.linestyle": "--",
    "boxplot.meanprops.linewidth": 1.0,
    # ***************************************************************************
    # * FONT                                                                    *
    # ***************************************************************************
    # The font properties used by `text.Text`.
    # See https://matplotlib.org/api/font_manager_api.html for more information
    # on font properties.  The 6 font properties used for font matching are
    # given below with their default values.
    #
    # The font.family property can take either a concrete font name (not supported
    # when rendering text with usetex), or one of the following five generic
    # values:
    #     - 'serif' (e.g., Times),
    #     - 'sans-serif' (e.g., Helvetica),
    #     - 'cursive' (e.g., Zapf-Chancery),
    #     - 'fantasy' (e.g., Western), and
    #     - 'monospace' (e.g., Courier).
    # Each of these values has a corresponding default list of font names
    # (font.serif, etc.); the first available font in the list is used.  Note that
    # for font.serif, font.sans-serif, and font.monospace, the first element of
    # the list (a DejaVu font) will always be used because DejaVu is shipped with
    # Matplotlib and is thus guaranteed to be available; the other entries are
    # left as examples of other possible values.
    #
    # The font.style property has three values: normal (or roman), italic
    # or oblique.  The oblique style will be used for italic, if it is not
    # present.
    #
    # The font.variant property has two values: normal or small-caps.  For
    # TrueType fonts, which are scalable fonts, small-caps is equivalent
    # to using a font size of 'smaller', or about 83%% of the current font
    # size.
    #
    # The font.weight property has effectively 13 values: normal, bold,
    # bolder, lighter, 100, 200, 300, ..., 900.  Normal is the same as
    # 400, and bold is 700.  bolder and lighter are relative values with
    # respect to the current weight.
    #
    # The font.stretch property has 11 values: ultra-condensed,
    # extra-condensed, condensed, semi-condensed, normal, semi-expanded,
    # expanded, extra-expanded, ultra-expanded, wider, and narrower.  This
    # property is not currently implemented.
    #
    # The font.size property is the default font size for text, given in points.
    # 10 pt is the standard value.
    #
    # Note that font.size controls default text sizes.  To configure
    # special text sizes tick labels, axes, labels, title, etc., see the rc
    # settings for axes and ticks.  Special text sizes can be defined
    # relative to font.size, using the following values: xx-small, x-small,
    # small, medium, large, x-large, xx-large, larger, or smaller
    "font.family": "sans-serif",
    "font.style": "normal",
    "font.variant": "normal",
    "font.weight": "normal",
    "font.stretch": "normal",
    "font.size": 10.0,
    "font.serif": [
        "DejaVu Serif",
        "Bitstream Vera Serif",
        "Computer Modern Roman",
        "New Century Schoolbook",
        "Century Schoolbook L",
        "Utopia",
        "ITC Bookman",
        "Bookman",
        "Nimbus Roman No9 L",
        "Times New Roman",
        "Times",
        "Palatino",
        "Charter",
        "serif",
    ],
    "font.sans-serif": [
        "DejaVu Sans",
        "Bitstream Vera Sans",
        "Computer Modern Sans Serif",
        "Lucida Grande",
        "Verdana",
        "Geneva",
        "Lucid",
        "Arial",
        "Helvetica",
        "Avant Garde",
        "sans-serif",
    ],
    "font.cursive": [
        "Apple Chancery",
        "Textile",
        "Zapf Chancery",
        "Sand",
        "Script MT",
        "Felipa",
        "Comic Neue",
        "Comic Sans MS",
        "cursive",
    ],
    "font.fantasy": [
        "Chicago",
        "Charcoal",
        "Impact",
        "Western",
        "Humor Sans",
        "xkcd",
        "fantasy",
    ],
    "font.monospace": [
        "DejaVu Sans Mono",
        "Bitstream Vera Sans Mono",
        "Computer Modern Typewriter",
        "Andale Mono",
        "Nimbus Mono L",
        "Courier New",
        "Courier",
        "Fixed",
        "Terminal",
        "monospace",
    ],
    # ***************************************************************************
    # * TEXT                                                                    *
    # ***************************************************************************
    # The text properties used by `text.Text`.
    # See https://matplotlib.org/api/artist_api.html#module-matplotlib.text
    # for more information on text properties
    "text.color": "black",
    # ***************************************************************************
    # * LaTeX                                                                   *
    # ***************************************************************************
    # For more information on LaTeX properties, see
    # https://matplotlib.org/tutorials/text/usetex.html
    "text.usetex": False,  # use latex for all text handling. The following fonts
    # are supported through the usual rc parameter settings:
    # new century schoolbook, bookman, times, palatino,
    # zapf chancery, charter, serif, sans-serif, helvetica,
    # avant garde, courier, monospace, computer modern roman,
    # computer modern sans serif, computer modern typewriter
    # If another font is desired which can loaded using the
    # LaTeX \usepackage command, please inquire at the
    # Matplotlib mailing list
    "text.latex.preamble": "",  # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES
    # AND IS THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP
    # IF THIS FEATURE DOES NOT DO WHAT YOU EXPECT IT TO.
    # text.latex.preamble is a single line of LaTeX code that
    # will be passed on to the LaTeX system. It may contain
    # any code that is valid for the LaTeX "preamble", i.e.
    # between the "\documentclass" and "\begin{document}"
    # statements.
    # Note that it has to be put on a single line, which may
    # become quite long.
    # The following packages are always loaded with usetex, so
    # beware of package collisions: color, geometry, graphicx,
    # type1cm, textcomp.
    # Adobe Postscript (PSSNFS) font packages may also be
    # loaded, depending on your font settings.
    # FreeType hinting flag ("foo" corresponds to FT_LOAD_FOO); may be one of the
    # following (Proprietary Matplotlib-specific synonyms are given in parentheses,
    # but their use is discouraged):
    # - default: Use the font's native hinter if possible, else FreeType's auto-hinter.
    #            ("either" is a synonym).
    # - no_autohint: Use the font's native hinter if possible, else don't hint.
    #                ("native" is a synonym.)
    # - force_autohint: Use FreeType's auto-hinter.  ("auto" is a synonym.)
    # - no_hinting: Disable hinting.  ("none" is a synonym.)
    "text.hinting": "force_autohint",
    "text.hinting_factor": 8,  # Specifies the amount of softness for hinting in the
    # horizontal direction.  A value of 1 will hint to full
    # pixels.  A value of 2 will hint to half pixels etc.
    "text.kerning_factor": 0,  # Specifies the scaling factor for kerning values. This
    # is provided solely to allow old test images to remain
    # unchanged. Set to 6 to obtain previous behavior. Values
    # other than 0 or 6 have no defined meaning.
    "text.antialiased": True,  # If True (default), the text will be antialiased.
    # This only affects raster outputs.
    # The following settings allow you to select the fonts in math mode.
    "mathtext.fontset": "dejavusans",  # Should be 'dejavusans' (default),
    # 'dejavuserif', 'cm' (Computer Modern), 'stix',
    # 'stixsans' or 'custom' (unsupported, may go
    # away in the future)
    # "mathtext.fontset: custom" is defined by the mathtext.bf, .cal, .it, ...
    # settings which map a TeX font name to a fontconfig font pattern.  (These
    # settings are not used for other font sets.)
    "mathtext.bf": "sans:bold",
    "mathtext.cal": "cursive",
    "mathtext.it": "sans:italic",
    "mathtext.rm": "sans",
    "mathtext.sf": "sans",
    "mathtext.tt": "monospace",
    "mathtext.fallback": "cm",  # Select fallback font from ['cm' (Computer Modern), 'stix'
    # 'stixsans'] when a symbol can not be found in one of the
    # custom math fonts. Select 'None' to not perform fallback
    # and replace the missing character by a dummy symbol.
    "mathtext.default": "it",  # The default font to use for math.
    # Can be any of the LaTeX font names, including
    # the special name "regular" for the same font
    # used in regular text.
    # ***************************************************************************
    # * AXES                                                                    *
    # ***************************************************************************
    # Following are default face and edge COLORS, default tick sizes,
    # default font sizes for tick labels, and so on.  See
    # https://matplotlib.org/api/axes_api.html#module-matplotlib.axes
    "axes.facecolor": "white",  # axes background color
    "axes.edgecolor": "black",  # axes edge color
    "axes.linewidth": 0.8,  # edge line width
    "axes.grid": True,  # display grid or not
    "axes.grid.axis": "both",  # which axis the grid should apply to
    "axes.grid.which": "both",  # grid lines at {major, minor, both} ticks
    "axes.titlelocation": "center",  # alignment of the title: {left, right, center}
    "axes.titlesize": "large",  # font size of the axes title
    "axes.titleweight": "normal",  # font weight of title
    "axes.titlecolor": "auto",  # color of the axes title, auto falls back to
    # text.color as default value
    "axes.titley": None,  # position title (axes relative units).  None implies auto
    "axes.titlepad": 6.0,  # pad between axes and title in points
    "axes.labelsize": "large",  # font size of the x and y labels
    "axes.labelpad": 4.0,  # space between label and axis
    "axes.labelweight": "normal",  # weight of the x and y labels
    "axes.labelcolor": "black",
    "axes.axisbelow": "line",  # draw axis gridlines and ticks:
    # - below patches (True)
    # - above patches but below lines ('line')
    # - above all (False)
    "axes.formatter.limits": [-5, 6],  # use scientific notation if log10
    # of the axis range is smaller than the
    # first or larger than the second
    "axes.formatter.use_locale": False,  # When True, format tick labels
    # according to the user's locale.
    # For example, use ',' as a decimal
    # separator in the fr_FR locale.
    "axes.formatter.use_mathtext": True,  # When True, use mathtext for scientific
    # notation.
    "axes.formatter.min_exponent": 0,  # minimum exponent to format in scientific notation
    "axes.formatter.useoffset": True,  # If True, the tick label formatter
    # will default to labeling ticks relative
    # to an offset when the data range is
    # small compared to the minimum absolute
    # value of the data.
    "axes.formatter.offset_threshold": 4,  # When useoffset is True, the offset
    # will be used when it can remove
    # at least this number of significant
    # digits from tick labels.
    "axes.spines.left": True,  # display axis spines
    "axes.spines.bottom": True,
    "axes.spines.top": True,
    "axes.spines.right": True,
    "axes.unicode_minus": True,  # use Unicode for the minus symbol rather than hyphen.  See
    # https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes
    "axes.prop_cycle": matplotlib.cycler(
        "color",
        [
            "1f77b4",
            "ff7f0e",
            "2ca02c",
            "d62728",
            "9467bd",
            "8c564b",
            "e377c2",
            "7f7f7f",
            "bcbd22",
            "17becf",
        ],
    ),
    # color cycle for plot lines as list of string color specs:
    # single letter, long name, or web-style hex
    # As opposed to all other parameters in this file, the color
    # values must be enclosed in quotes for this parameter,
    # e.g. '1f77b4', instead of 1f77b4.
    # See also https://matplotlib.org/tutorials/intermediate/color_cycle.html
    # for more details on prop_cycle usage.
    "axes.xmargin": 0.05,  # x margin.  See `axes.Axes.margins`
    "axes.ymargin": 0.05,  # y margin.  See `axes.Axes.margins`
    "axes.zmargin": 0.05,  # z margin.  See `axes.Axes.margins`
    "axes.autolimit_mode": "data",  # If "data", use axes.xmargin and axes.ymargin as is.
    # If "round_numbers", after application of margins, axis
    # limits are further expanded to the nearest "round" number.
    "polaraxes.grid": True,  # display grid on polar axes
    "axes3d.grid": True,  # display grid on 3D axes
    # ***************************************************************************
    # * AXIS                                                                    *
    # ***************************************************************************
    "xaxis.labellocation": "center",  # alignment of the xaxis label: {left, right, center}
    "yaxis.labellocation": "center",  # alignment of the yaxis label: {bottom, top, center}
    # ***************************************************************************
    # * DATES                                                                   *
    # ***************************************************************************
    # These control the default format strings used in AutoDateFormatter.
    # Any valid format datetime format string can be used (see the python
    # `datetime` for details).  For example, by using:
    #     - '%%x' will use the locale date representation
    #     - '%%X' will use the locale time representation
    #     - '%%c' will use the full locale datetime representation
    # These values map to the scales:
    #     {'year': 365, 'month': 30, 'day': 1, 'hour': 1/24, 'minute': 1 / (24 * 60)}
    "date.autoformatter.year": "%Y",
    "date.autoformatter.month": "%Y-%m",
    "date.autoformatter.day": "%Y-%m-%d",
    "date.autoformatter.hour": "%m-%d %H",
    "date.autoformatter.minute": "%d %H:%M",
    "date.autoformatter.second": "%H:%M:%S",
    "date.autoformatter.microsecond": "%M:%S.%f",
    # The reference date for Matplotlib's internal date representation
    # See https://matplotlib.org/examples/ticks_and_spines/date_precision_and_epochs.py
    "date.epoch": "1970-01-01T00:00:00",
    # 'auto', 'concise':
    "date.converter": "auto",
    # For auto converter whether to use interval_multiples:
    "date.interval_multiples": True,
    # ***************************************************************************
    # * TICKS                                                                   *
    # ***************************************************************************
    # See https://matplotlib.org/api/axis_api.html#matplotlib.axis.Tick
    "xtick.top": False,  # draw ticks on the top side
    "xtick.bottom": True,  # draw ticks on the bottom side
    "xtick.labeltop": False,  # draw label on the top
    "xtick.labelbottom": True,  # draw label on the bottom
    "xtick.major.size": 3.5,  # major tick size in points
    "xtick.minor.size": 2,  # minor tick size in points
    "xtick.major.width": 0.8,  # major tick width in points
    "xtick.minor.width": 0.6,  # minor tick width in points
    "xtick.major.pad": 3.5,  # distance to major tick label in points
    "xtick.minor.pad": 3.4,  # distance to the minor tick label in points
    "xtick.color": "black",  # color of the ticks
    "xtick.labelcolor": "inherit",  # color of the tick labels or inherit from xtick.color
    "xtick.labelsize": "medium",  # font size of the tick labels
    "xtick.direction": "out",  # direction: {in, out, inout}
    "xtick.minor.visible": False,  # visibility of minor ticks on x-axis
    "xtick.major.top": True,  # draw x axis top major ticks
    "xtick.major.bottom": True,  # draw x axis bottom major ticks
    "xtick.minor.top": True,  # draw x axis top minor ticks
    "xtick.minor.bottom": True,  # draw x axis bottom minor ticks
    "xtick.alignment": "center",  # alignment of xticks
    "ytick.left": True,  # draw ticks on the left side
    "ytick.right": False,  # draw ticks on the right side
    "ytick.labelleft": True,  # draw tick labels on the left side
    "ytick.labelright": False,  # draw tick labels on the right side
    "ytick.major.size": 3.5,  # major tick size in points
    "ytick.minor.size": 2,  # minor tick size in points
    "ytick.major.width": 0.8,  # major tick width in points
    "ytick.minor.width": 0.6,  # minor tick width in points
    "ytick.major.pad": 3.5,  # distance to major tick label in points
    "ytick.minor.pad": 3.4,  # distance to the minor tick label in points
    "ytick.color": "black",  # color of the ticks
    "ytick.labelcolor": "inherit",  # color of the tick labels or inherit from ytick.color
    "ytick.labelsize": "medium",  # font size of the tick labels
    "ytick.direction": "out",  # direction: {in, out, inout}
    "ytick.minor.visible": False,  # visibility of minor ticks on y-axis
    "ytick.major.left": True,  # draw y axis left major ticks
    "ytick.major.right": True,  # draw y axis right major ticks
    "ytick.minor.left": True,  # draw y axis left minor ticks
    "ytick.minor.right": True,  # draw y axis right minor ticks
    "ytick.alignment": "center_baseline",  # alignment of yticks
    # ***************************************************************************
    # * GRIDS                                                                   *
    # ***************************************************************************
    "grid.color": COLORS.DARK_GREY,  # grid color
    "grid.linestyle": "-",  # solid
    "grid.linewidth": 0.8,  # in points
    "grid.alpha": 1.0,  # transparency, between 0.0 and 1.0
    # ***************************************************************************
    # * LEGEND                                                                  *
    # ***************************************************************************
    "legend.loc": "best",
    "legend.frameon": True,  # if True, draw the legend on a background patch
    "legend.framealpha": 0.8,  # legend patch transparency
    "legend.facecolor": "inherit",  # inherit from axes.facecolor; or color spec
    "legend.edgecolor": "0.8",  # background patch boundary color
    "legend.fancybox": True,  # if True, use a rounded box for the
    # legend background, else a rectangle
    "legend.shadow": False,  # if True, give background a shadow effect
    "legend.numpoints": 1,  # the number of marker points in the legend line
    "legend.scatterpoints": 1,  # number of scatter points
    "legend.markerscale": 1.0,  # the relative size of legend markers vs. original
    "legend.fontsize": "medium",
    "legend.title_fontsize": None,  # None sets to the same as the default axes.
    # Dimensions as fraction of font size:
    "legend.borderpad": 0.4,  # border whitespace
    "legend.labelspacing": 0.5,  # the vertical space between the legend entries
    "legend.handlelength": 2.0,  # the length of the legend lines
    "legend.handleheight": 0.7,  # the height of the legend handle
    "legend.handletextpad": 0.8,  # the space between the legend line and legend text
    "legend.borderaxespad": 0.5,  # the border between the axes and legend edge
    "legend.columnspacing": 2.0,  # column separation
    # ***************************************************************************
    # * FIGURE                                                                  *
    # ***************************************************************************
    # See https://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure
    "figure.titlesize": "large",  # size of the figure title (``Figure.suptitle()``)
    "figure.titleweight": "normal",  # weight of the figure title
    "figure.figsize": [10, 10],  # figure size in inches
    "figure.dpi": 100,  # figure dots per inch
    "figure.facecolor": "white",  # figure face color
    "figure.edgecolor": "white",  # figure edge color
    "figure.frameon": True,  # enable figure frame
    "figure.max_open_warning": 20,  # The maximum number of figures to open through
    # the pyplot interface before emitting a warning.
    # If less than one this feature is disabled.
    "figure.raise_window": True,  # Raise the GUI window to front when show() is called.
    # The figure subplot parameters.  All dimensions are a fraction of the figure width and height.
    "figure.subplot.left": 0.125,  # the left side of the subplots of the figure
    "figure.subplot.right": 0.9,  # the right side of the subplots of the figure
    "figure.subplot.bottom": 0.11,  # the bottom of the subplots of the figure
    "figure.subplot.top": 0.88,  # the top of the subplots of the figure
    "figure.subplot.wspace": 0.2,  # the amount of width reserved for space between subplots,
    # expressed as a fraction of the average axis width
    "figure.subplot.hspace": 0.2,  # the amount of height reserved for space between subplots,
    # expressed as a fraction of the average axis height
    # Figure layout
    "figure.autolayout": True,  # When True, automatically adjust subplot
    # parameters to make the plot fit the figure
    # using `tight_layout`
    "figure.constrained_layout.use": False,  # When True, automatically make plot
    # elements fit on the figure. (Not
    # compatible with `autolayout`, above).
    "figure.constrained_layout.h_pad": 0.04167,  # Padding around axes objects. Float representing
    "figure.constrained_layout.w_pad": 0.04167,  # inches. Default is 3/72 inches (3 points)
    "figure.constrained_layout.hspace": 0.02,  # Space between subplot groups. Float representing
    "figure.constrained_layout.wspace": 0.02,  # a fraction of the subplot widths being separated.
    # ***************************************************************************
    # * IMAGES                                                                  *
    # ***************************************************************************
    "image.aspect": "equal",  # {equal, auto} or a number
    "image.interpolation": "antialiased",  # see help(imshow) for options
    "image.cmap": "viridis",  # A colormap name, gray etc...
    "image.lut": 256,  # the size of the colormap lookup table
    "image.origin": "upper",  # {lower, upper}
    "image.resample": True,
    "image.composite_image": True,  # When True, all the images on a set of axes are
    # combined into a single composite image before
    # saving a figure as a vector graphics file,
    # such as a PDF.
    # ***************************************************************************
    # * CONTOUR PLOTS                                                           *
    # ***************************************************************************
    "contour.negative_linestyle": "dashed",  # string or on-off ink sequence
    "contour.corner_mask": True,  # {True, False, legacy}
    "contour.linewidth": None,  # {float, None} Size of the contour line
    # widths. If set to None, it falls back to
    # `line.linewidth`.
    # ***************************************************************************
    # * ERRORBAR PLOTS                                                          *
    # ***************************************************************************
    "errorbar.capsize": 0,  # length of end cap on error bars in pixels
    # ***************************************************************************
    # * HISTOGRAM PLOTS                                                         *
    # ***************************************************************************
    "hist.bins": 10,  # The default number of histogram bins or 'auto'.
    # ***************************************************************************
    # * SCATTER PLOTS                                                           *
    # ***************************************************************************
    "scatter.marker": "o",  # The default marker type for scatter plots.
    "scatter.edgecolors": "face",  # The default edge COLORS for scatter plots.
    # ***************************************************************************
    # * AGG RENDERING                                                           *
    # ***************************************************************************
    # Warning: experimental, 2008/10/10
    "agg.path.chunksize": 0,  # 0 to disable; values in the range
    # 10000 to 100000 can improve speed slightly
    # and prevent an Agg rendering failure
    # when plotting very large data sets,
    # especially if they are very gappy.
    # It may cause minor artifacts, though.
    # A value of 20000 is probably a good
    # starting point.
    # ***************************************************************************
    # * PATHS                                                                   *
    # ***************************************************************************
    "path.simplify": True,  # When True, simplify paths by removing "invisible"
    # points to reduce file size and increase rendering
    # speed
    "path.simplify_threshold": 0.111111111111,  # The threshold of similarity below
    # which vertices will be removed in
    # the simplification process.
    "path.snap": True,  # When True, rectilinear axis-aligned paths will be snapped
    # to the nearest pixel when certain criteria are met.
    # When False, paths will never be snapped.
    "path.sketch": None,  # May be None, or a 3-tuple of the form:
    # (scale, length, randomness).
    #     - *scale* is the amplitude of the wiggle
    #         perpendicular to the line (in pixels).
    #     - *length* is the length of the wiggle along the
    #         line (in pixels).
    #     - *randomness* is the factor by which the length is
    #         randomly scaled.
    # 'path.effects':                       '',
    # ***************************************************************************
    # * SAVING FIGURES                                                          *
    # ***************************************************************************
    # The default savefig parameters can be different from the display parameters
    # e.g., you may want a higher resolution, or to make the figure
    # background white
    "savefig.dpi": "figure",  # figure dots per inch or 'figure'
    "savefig.facecolor": "auto",  # figure face color when saving
    "savefig.edgecolor": "auto",  # figure edge color when saving
    "savefig.format": "png",  # {png, ps, pdf, svg}
    "savefig.bbox": "tight",  # {tight, standard}
    # 'tight' is incompatible with pipe-based animation
    # backends (e.g. 'ffmpeg') but will work with those
    # based on temporary files (e.g. 'ffmpeg_file')
    "savefig.pad_inches": 0.1,  # Padding to be used when bbox is set to 'tight'
    "savefig.directory": "~",  # default directory in savefig dialog box,
    # leave empty to always use current working directory
    "savefig.transparent": False,  # setting that controls whether figures are saved with a
    # transparent background by default
    "savefig.orientation": "portrait",  # Orientation of saved figure
    ## tk backend params
    "tk.window_focus": False,  # Maintain shell focus for TkAgg
    ## ps backend params
    "ps.papersize": "letter",  # {auto, letter, legal, ledger, A0-A10, B0-B10}
    "ps.useafm": False,  # use of AFM fonts, results in small files
    "ps.usedistiller": False,  # {ghostscript, xpdf, None}
    # Experimental: may produce smaller files.
    # xpdf intended for production of publication quality files,
    # but requires ghostscript, xpdf and ps2eps
    "ps.distiller.res": 6000,  # dpi
    "ps.fonttype": 3,  # Output Type 3 (Type3) or Type 42 (TrueType)
    ## PDF backend params
    "pdf.compression": 6,  # integer from 0 to 9
    # 0 disables compression (good for debugging)
    "pdf.fonttype": 3,  # Output Type 3 (Type3) or Type 42 (TrueType)
    "pdf.use14corefonts": False,
    "pdf.inheritcolor": False,
    ## SVG backend params
    "svg.image_inline": True,  # Write raster image data directly into the SVG file
    "svg.fonttype": "path",  # How to handle SVG fonts:
    # path: Embed characters as paths -- supported
    #       by most SVG renderers
    # None: Assume fonts are installed on the
    #       machine where the SVG will be viewed.
    "svg.hashsalt": None,  # If not None, use this string as hash salt instead of uuid4
    ## pgf parameter
    # See https://matplotlib.org/tutorials/text/pgf.html for more information.
    "pgf.rcfonts": True,
    # 'pgf.preamble':                       '',  # See text.latex.preamble for documentation
    "pgf.texsystem": "xelatex",
    ## docstring params
    "docstring.hardcopy": False,  # set this when you want to generate hardcopy docstring
    # ***************************************************************************
    # * INTERACTIVE KEYMAPS                                                     *
    # ***************************************************************************
    # Event keys to interact with figures/plots via keyboard.
    # See https://matplotlib.org/users/navigation_toolbar.html for more details on
    # interactive navigation.  Customize these settings according to your needs.
    # Leave the field(s) empty if you don't need a key-map. (i.e., fullscreen : '')
    "keymap.fullscreen": ["f", "ctrl+f"],  # toggling
    "keymap.home": ["h", "r", "home"],  # home or reset mnemonic
    "keymap.back": [
        "left",
        "c",
        "backspace",
        "MouseButton.BACK",
    ],  # forward / backward keys
    "keymap.forward": ["right", "v", "MouseButton.FORWARD"],  # for quick navigation
    "keymap.pan": "p",  # pan mnemonic
    "keymap.zoom": "o",  # zoom mnemonic
    "keymap.save": ["s", "ctrl+s"],  # saving current figure
    "keymap.help": "f1",  # display help about active tools
    "keymap.quit": ["ctrl+w", "cmd+w", "q"],  # close the current figure
    # 'keymap.quit_all':                    '',  # close all figures
    "keymap.grid": "g",  # switching on/off major grids in current axes
    "keymap.grid_minor": "G",  # switching on/off minor grids in current axes
    "keymap.yscale": "l",  # toggle scaling of y-axes ('log'/'linear')
    "keymap.xscale": ["k", "L"],  # toggle scaling of x-axes ('log'/'linear')
    "keymap.copy": ["ctrl+c", "cmd+c"],  # Copy figure to clipboard
    # ***************************************************************************
    # * ANIMATION                                                               *
    # ***************************************************************************
    "animation.html": "none",  # How to display the animation as HTML in
    # the IPython notebook:
    #     - 'html5' uses HTML5 video tag
    #     - 'jshtml' creates a JavaScript animation
    "animation.writer": "ffmpeg",  # MovieWriter 'backend' to use
    "animation.codec": "h264",  # Codec to use for writing movie
    "animation.bitrate": -1,  # Controls size/quality trade-off for movie.
    # -1 implies let utility auto-determine
    "animation.frame_format": "png",  # Controls frame format used by temp files
    "animation.ffmpeg_path": "ffmpeg",  # Path to ffmpeg binary. Without full path
    # $PATH is searched
    # 'animation.ffmpeg_args':              '',  # Additional arguments to pass to ffmpeg
    "animation.convert_path": "convert",  # Path to ImageMagick's convert binary.
    # On Windows use the full path since convert
    # is also the name of a system tool.
    # 'animation.convert_args':             '',  # Additional arguments to pass to convert
    "animation.embed_limit": 20.0,  # Limit, in MB, of size of base64 encoded
    # animation in HTML (i.e. IPython notebook)
}


def get_style_rcparams(style: str):
    return plt.style.core._base_library[style]  # pylint: disable=protected-access


STYLES = plt.style.core._base_library | {  # pylint: disable=protected-access
    "default": matplotlib.rcParamsDefault,
    "liron-utils-default": RC_PARAMS,
    "liron-utils-article": {  # get_style_rcparams("seaborn-v0_8-talk")
        # BACKENDS
        # LINES
        "lines.linewidth": 2.275,  # line width in points
        "lines.markeredgewidth": 0.0,  # the line width around the marker symbol
        "lines.markersize": 9.1,  # marker size, in points
        # PATCHES
        "patch.linewidth": 0.39,  # edge width in points.
        # HATCHES
        # BOXPLOT
        # FONT
        # TEXT
        # LaTeX
        # AXES
        "axes.labelsize": 14.3,  # font size of the x and y labels
        "axes.titlesize": 15.6,  # font size of the axes title
        "axes.grid": False,  # display grid or not
        "axes.spines.left": True,  # display axis spines
        "axes.spines.bottom": True,
        "axes.spines.top": True,
        "axes.spines.right": True,
        # DATES
        # TICKS
        "xtick.labelsize": 13.0,  # font size of the tick labels
        "xtick.major.pad": 9.1,  # distance to major tick label in points
        "xtick.major.width": 1.3,  # major tick width in points
        "xtick.minor.width": 0.65,  # minor tick width in points
        "ytick.labelsize": 13.0,  # font size of the tick labels
        "ytick.major.pad": 9.1,  # distance to major tick label in points
        "ytick.major.width": 1.3,  # major tick width in points
        "ytick.minor.width": 0.65,  # minor tick width in points
        "xtick.top": True,  # draw ticks on the top side
        "xtick.direction": "in",  # direction: {in, out, inout}
        "ytick.right": True,  # draw ticks on the right side
        "ytick.direction": "in",  # direction: {in, out, inout}
        # GRIDS
        "grid.linewidth": 1.3,  # in points
        # LEGEND
        "legend.fontsize": 13.0,
        # FIGURE
        "figure.figsize": [10.4, 7.15],  # figure size in inches
        "figure.titlesize": 15.6,  # size of the figure title (``Figure.suptitle()``)
        # IMAGES
        # CONTOUR PLOTS
        # ERRORBAR PLOTS
        # HISTOGRAM PLOTS
        # SCATTER PLOTS
        # AGG RENDERING
        # PATHS
        # SAVING FIGURES
        "savefig.pad_inches": 0,  # Padding to be used when bbox is set to 'tight'
        # INTERACTIVE KEYMAPS
        # ANIMATION
    },
    "liron-utils-text-color": lambda color: {
        # BACKENDS
        # LINES
        # PATCHES
        # HATCHES
        # BOXPLOT
        # FONT
        # TEXT
        # LaTeX
        # AXES
        "axes.titlecolor": color,
        "axes.labelcolor": color,
        # DATES
        # TICKS
        "xtick.color": color,
        "ytick.color": color,
        # GRIDS
        # LEGEND
        # FIGURE
        # IMAGES
        # CONTOUR PLOTS
        # ERRORBAR PLOTS
        # HISTOGRAM PLOTS
        # SCATTER PLOTS
        # AGG RENDERING
        # PATHS
        # SAVING FIGURES
        # INTERACTIVE KEYMAPS
        # ANIMATION
    },
    "liron-utils-notebook": {
        # BACKENDS (need to run '%matplotlib Notebook')
        "backend": "nbAgg",
        "interactive": True,
        # LINES
        # PATCHES
        # HATCHES
        # BOXPLOT
        # FONT
        # TEXT
        # LaTeX
        # AXES
        "axes.titlesize": "small",  # font size of the axes title
        "axes.labelsize": "small",  # font size of the x and y labels
        # DATES
        # TICKS
        "xtick.labelsize": "x-small",  # font size of the tick labels
        "ytick.labelsize": "x-small",  # font size of the tick labels
        # GRIDS
        # LEGEND
        "legend.fontsize": "x-small",
        # FIGURE
        "figure.titlesize": "small",  # size of the figure title (``Figure.suptitle()``)
        "figure.figsize": [6.4, 4.8],  # figure size in inches
        # IMAGES
        # CONTOUR PLOTS
        # ERRORBAR PLOTS
        # HISTOGRAM PLOTS
        # SCATTER PLOTS
        # AGG RENDERING
        # PATHS
        # SAVING FIGURES
        # INTERACTIVE KEYMAPS
        # ANIMATION
    },
}


def get_color_cycler(colors: (str, Iterable) = "cycler", rgb: bool = False, plot: (bool, str) = False):
    """
    Show all of a cycler colors in a graph

    Parameters
    ----------
    colors :        str or array_like, optional
                    - "all" - return all colors given in COLORS.py
                    - "cycler" - return only cycler colors given in RC_PARAMS["axes.prop_cycle"]
                    - "<color>" - return all colors in COLORS.py that start with <color> (e.g. "blue")
    rgb :           bool, optional
                    If True, return RGB tuples instead of hex strings
    plot :          bool, optional
                    Plot the colors

    Returns
    -------
    list of colors (hex or RGB)
    """

    if isinstance(colors, str):
        if colors == "cycler":
            cycler = RC_PARAMS["axes.prop_cycle"]
            colors = [cycler._left[i]["color"] for i in range(len(cycler._left))]  # pylint: disable=protected-access
        elif colors == "all":
            all_str = COLORS.__all__
            colors = [getattr(COLORS, all_str[i]) for i in range(len(all_str))]
        else:
            color_name = colors
            colors = [getattr(COLORS, c) for c in COLORS.__all__ if color_name.lower() in c.lower()]
            if len(colors) == 0:
                raise ValueError(f"No colors found that match '{color_name}' in COLORS.py")

    elif isinstance(colors, Iterable):
        pass

    else:
        raise ValueError("Invalid type for 'colors'.")

    # plot colors
    if plot is not False:
        fig, ax = plt.subplots()
        if plot is True or plot in ["color", "colors"]:
            ax.axes.yaxis.set_visible(False)
            for i, color in enumerate(colors):
                h = ax.bar(i + 1, 1, color=color)
                ax.bar_label(h, labels=[color], label_type="center", rotation=-90)
            ax.grid(None)

        elif plot == "sine":
            x = np.linspace(0, 1, 1001)
            y = lambda f: np.sin(2 * np.pi * f * x)
            for i, color in enumerate(colors):
                ax.plot(x, y((i + 1) / 4), label=color)
                ax.legend()

        fig.show()

    if rgb:
        colors = hex2rgb(colors)

    return colors


def set_color_cycler(colors: (str, Iterable) = "cycler", plot: (bool, str) = False):
    """
    Set the color cycler for MatPlotLib.

    Parameters
    ----------
    colors :        str or array_like, optional
                    - "all" - set all colors given in COLORS.py
                    - "cycler" - set only cycler colors given in RC_PARAMS["axes.prop_cycle"]
    plot :          bool, optional
                    Plot the colors

    Returns
    -------
    list of colors
    """

    cycler_colors = get_color_cycler(colors=colors, plot=plot)
    matplotlib.rcParams["axes.prop_cycle"] = matplotlib.cycler(color=cycler_colors)

    return cycler_colors


def update_rcParams(new_params: (str, dict, callable) = "liron-utils-default", *args, **kwargs):
    """
    Update the default MatPlotLib rcParams.

    Parameters
    ----------
    new_params :    str or dict
                    - str: either one of plt.style.available or one of STYLES
                    - dict: dictionary of rcParams

    Returns
    -------

    """

    if isinstance(new_params, str):
        if new_params.replace("_", "-") in STYLES:
            new_params = STYLES[new_params.replace("_", "-")]
            if callable(new_params):
                new_params = new_params(*args, **kwargs)
            matplotlib.rcParams.update(new_params)
        else:  # new_rcParams in plt.style.available
            plt.style.use(new_params)
            new_params = get_style_rcparams(new_params)

    else:  # type(new_rcParams) is dict
        matplotlib.rcParams.update(new_params)

    return new_params
