"""Plotting helper for MAPDL using pyvista"""
import numpy as np
import pyvista as pv

from ansys.mapdl.reader.misc import unique_rows


def general_plotter(
    title,
    meshes,
    points,
    labels,
    cpos=None,
    show_bounds=False,
    show_axes=True,
    background=None,
    off_screen=None,
    screenshot=False,
    window_size=None,
    notebook=None,
    # add_mesh kwargs:
    color="w",
    show_edges=None,
    edge_color=None,
    point_size=5.0,
    line_width=None,
    opacity=1.0,
    flip_scalars=False,
    lighting=None,
    n_colors=256,
    interpolate_before_map=True,
    cmap=None,
    render_points_as_spheres=False,
    render_lines_as_tubes=False,
    stitle=None,
    smooth_shading=False,
    # labels kwargs
    font_size=None,
    font_family=None,
    text_color=None,
):
    """General pyansys plotter for APDL geometry and meshes.

    Parameters
    ----------
    cpos : list(tuple(floats)), str
        The camera position to use.  You can either use a saved camera
        position or specify one of the following strings:

        - ``"xy"``
        - ``"xz"``
        - ``"yz"``
        - ``"yx"``
        - ``"zx"``
        - ``"zy"``
        - ``"iso"``

    off_screen : bool, optional
        Renders off screen when ``True``.  Useful for automated
        screenshots.

    window_size : list, optional
        Window size in pixels.  Defaults to ``[1024, 768]``

    notebook : bool, optional
        When True, the resulting plot is placed inline a jupyter
        notebook.  Assumes a jupyter console is active.  Automatically
        enables off_screen.

    show_bounds : bool, optional
        Shows mesh bounds when ``True``.

    show_axes : bool, optional
        Shows a vtk axes widget.  Enabled by default.

    screenshot : str or bool, optional
        Saves screenshot to file when enabled.

    color : string or 3 item list, optional, defaults to white
        Use to make the entire mesh have a single solid color.  Either
        a string, RGB list, or hex color string.  For example:
        ``color='white'``, ``color='w'``, ``color=[1, 1, 1]``, or
        ``color='#FFFFFF'``. Color will be overridden if scalars are
        specified.

    show_edges : bool, optional
        Shows the edges of a mesh.  Does not apply to a wireframe
        representation.

    edge_color : string or 3 item list, optional, defaults to black
        The solid color to give the edges when ``show_edges=True``.
        Either a string, RGB list, or hex color string.

    point_size : float, optional
        Point size of any nodes in the dataset plotted. Also applicable
        when style='points'. Default ``5.0``

    line_width : float, optional
        Thickness of lines.  Only valid for wireframe and surface
        representations.  Default None.

    opacity : float, str, array-like
        Opacity of the mesh. If a single float value is given, it will be
        the global opacity of the mesh and uniformly applied everywhere -
        should be between 0 and 1. A string can also be specified to map
        the scalars range to a predefined opacity transfer function
        (options include: 'linear', 'linear_r', 'geom', 'geom_r').
        A string could also be used to map a scalars array from the mesh to
        the opacity (must have same number of elements as the
        ``scalars`` argument). Or you can pass a custom made transfer
        function that is an array either ``n_colors`` in length or shorter.

    n_colors : int, optional
        Number of colors to use when displaying scalars. Defaults to 256.
        The scalar bar will also have this many colors.

    cmap : str, list, optional
       Name of the Matplotlib colormap to us when mapping the ``scalars``.
       See available Matplotlib colormaps.  Only applicable for when
       displaying ``scalars``. Requires Matplotlib to be installed.
       ``colormap`` is also an accepted alias for this. If ``colorcet`` or
       ``cmocean`` are installed, their colormaps can be specified by name.

        You can also specify a list of colors to override an
        existing colormap with a custom one.  For example, to
        create a three color colormap you might specify
        ``['green', 'red', 'blue']``

    render_points_as_spheres : bool, optional
        Render points as spheres.

    render_lines_as_tubes : bool, optional
        Renders lines as tubes.

    smooth_shading : bool, optional
        Smoothly render curved surfaces when plotting.  Not helpful
        for all meshes.
    """
    if notebook:
        off_screen = True

    pl = pv.Plotter(off_screen=off_screen, notebook=notebook)

    if background:
        pl.set_background(background)

    for point in points:
        pl.add_points(
            point["points"],
            scalars=point.get("scalars", None),
            color=color,
            show_edges=show_edges,
            edge_color=edge_color,
            point_size=point_size,
            line_width=line_width,
            opacity=opacity,
            flip_scalars=flip_scalars,
            lighting=lighting,
            n_colors=n_colors,
            interpolate_before_map=interpolate_before_map,
            cmap=cmap,
            render_points_as_spheres=render_points_as_spheres,
            render_lines_as_tubes=render_lines_as_tubes,
        )

    for mesh in meshes:
        pl.add_mesh(
            mesh["mesh"],
            scalars=mesh.get("scalars", None),
            stitle=mesh.get("stitle", None),
            color=mesh.get("color", color),
            show_edges=show_edges,
            edge_color=edge_color,
            smooth_shading=smooth_shading,
            point_size=point_size,
            line_width=line_width,
            opacity=opacity,
            flip_scalars=flip_scalars,
            lighting=lighting,
            n_colors=n_colors,
            interpolate_before_map=interpolate_before_map,
            cmap=cmap,
            render_points_as_spheres=render_points_as_spheres,
            render_lines_as_tubes=render_lines_as_tubes,
        )

    for label in labels:
        # verify points are not duplicates
        points, idx, _ = unique_rows(np.array(label["points"]))
        labels = np.array(label["labels"])[idx].tolist()

        pl.add_point_labels(
            points,
            labels,
            show_points=False,
            shadow=False,
            font_size=font_size,
            font_family=font_family,
            text_color=text_color,
        )

    if stitle is not None:
        pl.add_scalar_bar(title=stitle)

    if cpos:
        pl.camera_position = cpos

    if show_bounds:
        pl.show_bounds()

    if show_axes:
        pl.show_axes()

    if screenshot:
        pl.show(title=title, auto_close=False, window_size=window_size)
        pl.screenshot(screenshot)
    else:
        pl.show()

    return pl.camera_position
