-----
    Brief
    -----
    Plotting function intended for an easy representation of OpenSignals acquired data.

    -----------
    Description
    -----------
    This function allows to plot data acquired with resource to OpenSignals, available at
    https://bitalino.com/en/software, in an easy way using the Bokeh library (https://bokeh.pydata.org/en/latest/).
    The plot will automatically present the OpenSignals style if the corresponding parameter is set to True
    (apply_opensignals_style=True).

    This allows to easily use multiple plot elements without the need to know any visualization libraries.

    ----------
    Parameters
    ----------
    *args: list
        Variable number of arguments with the purpose of giving the user the possibility of
        defining as an input only the "data" axis or both "time" and "data" axes.

    legend : list
        Input where the legend of each plot is specified. Should have the same shape of time.

    title : list
        Input where the title of each figure is specified. Should have the same shape of time.

    x_axis_label : list
        Input where the x label of each figure is specified. All figures will have the same x label
        in the current implementation.

    y_axis_label : list
        Input where the y label of each figure is specified. Should have a length equal to the
        number of figures.

    grid_plot : boolean
        If True then the plots will be organized in a grid_plot structure.

    grid_lines : int
        Number of lines of grid plot.

    grid_columns : int
        Number of columns of grid plot.

    hor_lines : list of lists
        The parent list must have the same shape of time and each of its elements (child lists)
        must be formed by numbers defining the y axis position of the horizontal lines.

    hor_lines_leg : list of lists
        Legends of the horizontal lines (same shape of hor_lines).

    vert_lines : list of lists
        The parent list must have the same shape of time and each of its elements (child lists)
        must be formed by numbers defining the x axis position of the vertical lines.

    vert_lines_leg : list of lists
        Legends of the vertical lines (same shape of vert_lines).

    apply_opensignals_style : boolean
        If True then the OpenSignals style will be applied.


    show_plot : boolean
        If True the generated figures will be shown.

    warn_print : bool
        If True some warnings about invalid kwargs keys will be prompted.

    get_fig_list : bool
        If True then it will be returned a list containing the figure objects generated during
        the function execution.

    file_name : str
        Path containing the destination folder where the Bokeh figure will be stored.

    **kwargs : dict
        Keyword values for applying in bokeh figures, lines and gridplots.

    Returns
    -------
    out : bokeh figure or gridplot
        Object that is produced during the execution of the present function.

def plot(*args, legend=None, title=None, x_axis_label="Time (s)", y_axis_label=None,
         grid_plot=False, grid_lines=None, grid_columns=None, hor_lines=None, hor_lines_leg=None,
         vert_lines=None, vert_lines_leg=None, apply_opensignals_style=True, show_plot=True,
         warn_print=False, get_fig_list=False, file_name=None, **kwargs):
    """
    -----
    Brief
    -----
    Plotting function intended for an easy representation of OpenSignals acquired data.

    -----------
    Description
    -----------
    This function allows to plot data acquired with resource to OpenSignals, available at
    https://bitalino.com/en/software, in an easy way using the Bokeh library (https://bokeh.pydata.org/en/latest/).
    The plot will automatically present the OpenSignals style if the corresponding parameter is set to True
    (apply_opensignals_style=True).

    This allows to easily use multiple plot elements without the need to know any visualization libraries.

    ----------
    Parameters
    ----------
    *args: list
        Variable number of arguments with the purpose of giving the user the possibility of
        defining as an input only the "data" axis or both "time" and "data" axes.

    legend : list
        Input where the legend of each plot is specified. Should have the same shape of time.

    title : list
        Input where the title of each figure is specified. Should have the same shape of time.

    x_axis_label : list
        Input where the x label of each figure is specified. All figures will have the same x label
        in the current implementation.

    y_axis_label : list
        Input where the y label of each figure is specified. Should have a length equal to the
        number of figures.

    grid_plot : boolean
        If True then the plots will be organized in a grid_plot structure.

    grid_lines : int
        Number of lines of grid plot.

    grid_columns : int
        Number of columns of grid plot.

    hor_lines : list of lists
        The parent list must have the same shape of time and each of its elements (child lists)
        must be formed by numbers defining the y axis position of the horizontal lines.

    hor_lines_leg : list of lists
        Legends of the horizontal lines (same shape of hor_lines).

    vert_lines : list of lists
        The parent list must have the same shape of time and each of its elements (child lists)
        must be formed by numbers defining the x axis position of the vertical lines.

    vert_lines_leg : list of lists
        Legends of the vertical lines (same shape of vert_lines).

    apply_opensignals_style : boolean
        If True then the OpenSignals style will be applied.


    show_plot : boolean
        If True the generated figures will be shown.

    warn_print : bool
        If True some warnings about invalid kwargs keys will be prompted.

    get_fig_list : bool
        If True then it will be returned a list containing the figure objects generated during
        the function execution.

    file_name : str
        Path containing the destination folder where the Bokeh figure will be stored.

    **kwargs : dict
        Keyword values for applying in bokeh figures, lines and gridplots.

    Returns
    -------
    out : bokeh figure or gridplot
        Object that is produced during the execution of the present function.

    """

    # Generation of the HTML file where the plot will be stored.
    #file_name = _generate_bokeh_file(file_name)

    # Data conversion for ensuring that the function only works with lists.
    if len(args) == 1:
        time = numpy.linspace(1, len(args[0]) + 1, len(args[0]))
        data = args[0]
    elif len(args) == 2:
        time = list(args[0])
        data = list(args[1])
    else:
        raise RuntimeError("biosignalsnotebooks plot function only accepts 1 or 2 arguments in *args"
                           " input. If only 1 input is given it should be a list with data samples,"
                           "otherwise if 2 inputs are given then the first one defines the time"
                           "axis and the second one data values.")

    # This function offers two input mechanisms (easy and complex). The easiest one consists in
    # the representation of a single plot in a single figure, so, the user only needs to specify as
    # inputs "time" and "data" lists. On the other hand, for the complex mechanism, the user can
    # represent plots in different figures, using for that lists of lists as "time" and "data"
    # inputs.
    # In the following lines is ensured that independently of the input given, the function will
    # achieve is purpose correctly.
    if _is_instance(Number, data, condition="all") and not _is_instance(bool, data, condition="any") \
            and _is_instance(Number, time, condition="all") \
            and not _is_instance(bool, time, condition="any"):
        time = [time]
        data = [data]
        if y_axis_label is not None:
            y_axis_label = [y_axis_label]
        if hor_lines is not None:
            hor_lines = [hor_lines]
        if hor_lines_leg is not None:
            hor_lines_leg = [hor_lines_leg]
        if vert_lines is not None:
            vert_lines = [vert_lines]
        if vert_lines_leg is not  None:
            vert_lines_leg = [vert_lines_leg]
        if title is not None:
            title = [title]
        if legend is not None:
            legend = [legend]
    elif _is_instance(numpy.ndarray, data, condition="any") \
            or _is_instance(numpy.ndarray, time, condition="any"):
        time = list(map(list, time))
        data = list(map(list, data))

    # Ensures the application or not of opensignals graphical style.
    if apply_opensignals_style is True:
        style_figure = {**opensignals_kwargs("figure"), **_filter_keywords(FigureOptions, kwargs,
                                                                           is_class=True,
                                                                           warn_print=warn_print)}
        style_line = {**opensignals_kwargs("line"), **_filter_keywords(Line, kwargs,
                                                                       warn_print=warn_print)}
        style_gridplot = {**opensignals_kwargs("gridplot"),
                          **_filter_keywords(gridplot, kwargs, warn_print=warn_print)}
    else:
        style_figure = _filter_keywords(FigureOptions, kwargs, is_class=True, warn_print=warn_print)
        style_line = _filter_keywords(Line, kwargs, warn_print=warn_print)
        style_gridplot = _filter_keywords(gridplot, kwargs, warn_print=warn_print)

    # ------------------------ Verification if the input arguments are valid ----------------------
    if legend is not None:
        if isinstance(legend, list):
            if len(legend) != len(time) or len(legend) != len(data):
                raise RuntimeError("The shape of legend does not match with time input.")
        else:
            raise RuntimeError("The specified data type of legend field is not valid. Input must "
                               "be a list.")
    else:
        legend = [None] * len(time)

    if title is not None:
        if isinstance(title, list):
            if len(title) != len(time) or len(title) != len(data):
                raise RuntimeError("The shape of title does not match with time input.")
        elif isinstance(title, str):
            if grid_plot is True:
                raise RuntimeError("Each figure of the gridplot must have a title, i.e., the shape"
                                   " of time, data and title inputs needs to match.")
            else:
                title = [title] * len(time)
        elif grid_plot is False and len(title) != 1:
            raise RuntimeError("The number of titles is not compatible with the number of figures "
                               "(only one title is needed).")
        else:
            raise RuntimeError("The specified data type of title field is not valid. Input must be "
                               "a list.")
    else:
        title = [None] * len(time)

    if y_axis_label is not None:
        if isinstance(y_axis_label, list):
            if len(y_axis_label) != len(time) or len(y_axis_label) != len(data):
                raise RuntimeError("The shape of y_axis_label does not match with time input.")
        elif isinstance(y_axis_label, str):
            y_axis_label = [y_axis_label] * len(time)
        elif grid_plot is False and len(y_axis_label) != 1:
            raise RuntimeError("The number of y axis labels is not compatible with the number of "
                               "figures.")
        else:
            raise RuntimeError("The specified data type of y_axis_label field is not valid. Input "
                               "must be a list or a string when grid_plot field is False.")
    else:
        y_axis_label = [None] * len(time)

    # Coherence between grid_plot, grid_lines and grid_columns inputs.
    if grid_lines is not None or grid_columns is not None:
        if grid_plot is not True:
            raise RuntimeError("When grid_lines and grid_columns inputs are used the field grid_"
                               "plot must be True.")
        else:
            if not isinstance(grid_lines, int) or not isinstance(grid_columns, int):
                raise RuntimeError("At least one of the grid_lines or grid_columns values is not "
                                   "an integer.")

    # Horizontal Lines.
    if hor_lines is not None:
        if isinstance(hor_lines, list):
            if len(hor_lines) != len(time) or len(hor_lines) != len(data):
                raise RuntimeError("The shape of hor_lines does not match with time input.")
        else:
            raise RuntimeError("The specified data type of hor_lines field is not valid. Input "
                               "must be a list of lists.")

        # Each sublist entry must be numeric.
        for cell in hor_lines:
            if not _is_instance(Number, cell, condition="all") \
                    or _is_instance(bool, cell, condition="any"):
                raise RuntimeError("At least one of the list elements, specified in hor_lines, "
                                   "is not numeric.")
            elif vert_lines_leg is not None:
                if len(hor_lines) != len(hor_lines_leg):
                    raise RuntimeError("The shape of hor_lines and hor_lines_leg is not the same.")

    # Vertical Lines.
    if vert_lines is not None:
        if isinstance(vert_lines, list):
            if len(vert_lines) != len(time) or len(vert_lines) != len(data):
                raise RuntimeError("The shape of vert_lines does not match with time input.")
        else:
            raise RuntimeError("The specified data type of vert_lines field is not valid. "
                               "Input must be a list of lists.")

        # Each sublist entry must be numeric.
        for cell in vert_lines:
            if not _is_instance(Number, cell, condition="all") \
                    or _is_instance(bool, cell, condition="any"):
                raise RuntimeError("At least one of the list elements, specified in vert_lines, "
                                   "is not numeric.")
            elif vert_lines_leg is not None:
                if len(vert_lines) != len(vert_lines_leg):
                    raise RuntimeError("The shape of vert_lines and vert_lines_leg is not "
                                       "the same.")

    # --------------------------------------- Plotting Stage --------------------------------------
    fig_list = []
    # If all data entries are lists, then it is considered that we are in a multiplot situation.
    if _is_instance(list, data, condition="all") and _is_instance(list, time, condition="all"):
        for list_entry in range(0, len(time)):  # Generation of a figure per plot.
            # Verification if all elements inside list are numbers.
            if _is_instance(Number, data[list_entry], condition="all") \
                    and not _is_instance(bool, data[list_entry], condition="any") \
                    and _is_instance(Number, time[list_entry], condition="all") \
                    and not _is_instance(bool, time[list_entry], condition="any"):
                if len(time[list_entry]) == len(data[list_entry]):  # Shape verification
                    if grid_plot is True:  # Generation of multiple figures.
                        fig_list.append(figure(title=title[list_entry],
                                               y_axis_label=y_axis_label[list_entry],
                                               x_axis_label=x_axis_label,
                                               **style_figure))
                    elif grid_plot is False and list_entry == 0:
                        fig_list.append(figure(title=title[list_entry],
                                               y_axis_label=y_axis_label[list_entry],
                                               x_axis_label=x_axis_label,
                                               sizing_mode='scale_both',
                                               **style_figure))

                    fig_list[-1].line(time[list_entry], data[list_entry], legend=legend[list_entry],
                                      **style_line)

                    # Representation of horizontal lines.
                    if hor_lines is not None:
                        for hor_line_nbr, hor_line in enumerate(hor_lines[list_entry]):
                            if hor_lines_leg is not None:
                                fig_list[-1].line([time[list_entry][0], time[list_entry][-1]],
                                                  [hor_line, hor_line],
                                                  legend=hor_lines_leg[list_entry][hor_line_nbr],
                                                  **opensignals_kwargs("line"))
                            else:
                                fig_list[-1].line([time[list_entry][0], time[list_entry][-1]],
                                                  [hor_line, hor_line],
                                                  **opensignals_kwargs("line"))

                    # Representation of vertical lines.
                    if vert_lines is not None:
                        for vert_line_nbr, vert_line in enumerate(vert_lines[list_entry]):
                            if vert_lines_leg is not None:
                                fig_list[-1].line([vert_line, vert_line],
                                                  [numpy.min(data[list_entry]),
                                                   numpy.max(data[list_entry])],
                                                  legend=vert_lines_leg[list_entry][vert_line_nbr],
                                                  **opensignals_kwargs("line"))
                            else:
                                fig_list[-1].line([vert_line, vert_line],
                                                  [numpy.min(data[list_entry]),
                                                   numpy.max(data[list_entry])],
                                                  **opensignals_kwargs("line"))

                    # Update of line style.
                    if apply_opensignals_style is True:
                        style_line = {**opensignals_kwargs("line"),
                                      **_filter_keywords(Line, kwargs, warn_print=warn_print)}
                    else:
                        style_line = _filter_keywords(Line, kwargs, warn_print=warn_print)

                else:
                    raise RuntimeError("The shape of time and data inputs does not match.")
            else:
                raise RuntimeError("At least one of the list elements, specified in data or time, "
                                   "is not numeric.")

    # If this happen, then we receive as input a single list for time and data
    # (Single plot perspective).
    elif _is_instance(Number, data, condition="all") \
            and not _is_instance(bool, data, condition="any") \
            and _is_instance(Number, time, condition="all") \
            and not _is_instance(bool, time, condition="any"):
        grid_plot = False

        # Verification if all elements inside list are numbers.
        if _is_instance(Number, data, condition="all") \
                and not _is_instance(bool, data, condition="any") \
                and _is_instance(Number, time, condition="all") \
                and not _is_instance(bool, time, condition="any"):
            if len(time) == len(data):  # Shape verification
                fig_list.append(figure(title=title[0], y_axis_label=y_axis_label[0],
                                       x_axis_label=x_axis_label, **style_figure))
                fig_list[-1].line(time, data, legend=legend[0], **style_line)
            else:
                raise RuntimeError("The shape of time and data inputs does not match.")
        else:
            raise RuntimeError("At least one of the list elements, specified in data or time, is "
                               "not numeric.")

    else:
        raise RuntimeError("The input 'data' or/and 'time' does not have a valid format. It should "
                           "be a list of numbers or a list of lists.")

    # Application of the OpenSignals Style.
    if apply_opensignals_style is True:
        opensignals_style(fig_list)

    # Show of plots.
    if grid_plot is True:
        nbr_of_spaces = grid_lines * grid_columns
        nbr_of_figures = len(fig_list)

        if nbr_of_spaces >= nbr_of_figures > (grid_lines - 1) * grid_columns:
            # Organization of data accordingly to the number of rows and columns specified as input
            # arguments.
            grid_layout = []
            fig_nbr = 0
            for row in range(0, grid_lines):
                grid_layout.append([])
                for column in range(0, grid_columns):
                    if fig_nbr <= nbr_of_figures - 1:
                        grid_layout[-1].append(fig_list[fig_nbr])
                    else:
                        grid_layout[-1].append(None)

                    # Update of auxiliary variable.
                    fig_nbr += 1

            # Generation of the gridplot.
            grid = gridplot(grid_layout, **style_gridplot)

            if show_plot is True:
                show(grid)
            #else:
            #    save(grid)
                #return HTML('<iframe width=100% height=350 src="generated_plots/' + file_name + '"></iframe>')
        else:
            raise RuntimeError("The specified number of lines and columns for the grid plot is not "
                               "compatible.")

    else:
        if show_plot is True:
            show(fig_list[-1])
        #else:
        #    save(fig_list[-1])
            #return HTML('<iframe width=100% height="' + str(fig_list[-1].plot_height) + '" src="generated_plots/' + file_name + '"></iframe>')

    if get_fig_list is True:
        return fig_list