# -*- coding: utf-8 -*-
import warnings
from pathlib import Path
import seaborn as sns
import pandas as pd
from typing import Union
import numpy as np
import matplotlib.pyplot as plt

from qa4sm_reader.img import QA4SMImg
import qa4sm_reader.globals as globals
from qa4sm_reader import plotting_methods as plm

from warnings import warn


class QA4SMPlotter():
    """
    Class to create image files of plots from the validation results in a QA4SMImage
    """
    def __init__(self, image, out_dir:str=None):
        """
        Create box plots from results in a qa4sm output file.

        Parameters
        ----------
        image : QA4SMImg
            The results object.
        out_dir : str, optional (default: None)
            Path to output generated plot. If None, defaults to the current working directory.
        """
        self.img = image
        self.out_dir = self.get_dir(out_dir=out_dir)

        self.ref = image.datasets.ref

        try:
            self.img.vars
        except:
            warn("The initialized QA4SMImg object has not been loaded. 'load_data' needs to be "
                 "set to 'True' in the initialization of the Image.")

    def get_dir(self, out_dir:str) -> Path:
        """Use output path if specified, otherwise same directory as the one storing the netCDF file"""
        if out_dir:
            out_dir = Path(out_dir)  # use directory if specified
            if not out_dir.exists():
                out_dir.mkdir()  # make if not existing
        else:
            out_dir = self.img.filepath.parent  # use default otherwise

        return out_dir

    def _standard_filename(self, out_name:str, out_type:str='png') -> Path:
        """
        Standardized behaviour for filenames: if provided name has extension, it is kept; otherwise, it is saved as
        .png to self.out_dir

        Parameters
        ----------
        out_name : str
            output filename (with or without extension)
        out_type : str, optional
            contains file extensions to be plotted. If None, uses 'png'

        Returns
        -------
        outname: pathlib.Path
            correct path of the file
        """
        out_name = Path(out_name)
        # provide output directory
        out_path = self.out_dir.joinpath(out_name)

        # provide output file type
        if not out_path.suffix:
            if out_type[0] != '.':
                out_type = '.' + out_type
            out_path = out_path.with_suffix(out_type)

        return out_path

    @staticmethod
    def _box_caption(Var, tc:bool=False) -> str:
        """
        Create the dataset part of the box (axis) caption

        Parameters
        ----------
        Var: MetricVar
            variable for a metric
        tc: bool, default is False
            True if TC. Then, caption starts with "Other Data:"

        Returns
        -------
        capt: str
            box caption
        """
        ref_meta, mds_meta, other_meta = Var.get_varmeta()
        ds_parts = []
        id, meta = mds_meta
        if tc:
            id, meta = other_meta
        ds_parts.append('{}-{}\n({})\nVariable: {} [{}]'.format(
            id,
            meta['pretty_name'],
            meta['pretty_version'],
            meta['pretty_variable'],
            meta['mu'])
        )
        capt = '\n and \n'.join(ds_parts)

        if tc:
            capt = 'Other Data:\n' + capt

        return capt

    @staticmethod
    def _get_parts_name(Var, type='boxplot_basic') -> list:
        """
        Create parts for title according to the type of plot

        Parameters
        ----------
        Var: MetricVar
            variable for a metric
        type: str
            type of plot

        Returns
        -------
        parts: list
            list of parts for title
        """
        parts = []
        ref, mds, other = [meta for meta in Var.get_varmeta()]
        if type == 'boxplot_basic':
            parts.append(ref[0])
            parts.extend([ref[1]['pretty_name'], ref[1]['pretty_version']])

        elif type in ['boxplot_tc', 'mapplot_basic', 'mapplot_tc']:
            parts.append(mds[0])
            parts.extend([mds[1]['pretty_name'], mds[1]['pretty_version']])
            parts.append(ref[0])
            parts.extend([ref[1]['pretty_name'], ref[1]['pretty_version']])

            if type == 'mapplot_tc':
                parts.append(other[0])
                parts.extend([other[1]['pretty_name'], other[1]['pretty_version']])

        return parts

    @staticmethod
    def _titles_lut(type:str) -> str:
        """
        Lookup table for plot titles

        Parameters
        ----------
        type: str
            type of plot
        """
        titles = {
            'boxplot_basic': 'Intercomparison of {} \nwith {}-{} ({}) as the reference\n ',
            'boxplot_tc': 'Intercomparison of {} \nfor {}-{} ({}) \nwith {}-{} ({}) as the reference\n ',
            'mapplot_basic': '{} for {}-{} ({}) with {}-{} ({}) as the reference',
            'mapplot_tc': '{} for {}-{} ({}) with {}-{} ({}) and {}-{} ({}) as the references',
            'metadata': 'Intercomparison of {} by {}\nwith reference: {}',
        }

        try:
            return titles[type]

        except KeyError as e:
            message = "type '{}' is not in the lookup table".format(type)
            warn(message)

    @staticmethod
    def _filenames_lut(type:str) -> str:
        """
        Lookup table for file names

        Parameters
        ----------
        type: str
            type of plot
        """
        # we stick to old naming convention
        names = {
            'boxplot_basic': 'boxplot_{}',
            'mapplot_common': 'overview_{}',
            'boxplot_tc': 'boxplot_{}_for_{}-{}',
            'mapplot_double': 'overview_{}-{}_and_{}-{}_{}',
            'mapplot_tc': 'overview_{}-{}_and_{}-{}_and_{}-{}_{}_for_{}-{}',
            'metadata': 'boxplot_{}_metadata_{}',
        }

        try:
            return names[type]

        except KeyError as e:
            message = "type '{}' is not in the lookup table".format(type)
            warn(message)

    def create_title(self, Var, type:str) -> str:
        """
        Create title of the plot

        Parameters
        ----------
        Var: MetricVar
            variable for a metric
        type: str
            type of plot
        """
        parts = [globals._metric_name[Var.metric]]
        parts.extend(self._get_parts_name(Var=Var, type=type))
        title = self._titles_lut(type=type).format(*parts)

        return title

    def create_filename(self, Var, type:str) -> str:
        """
        Create name of the file

        Parameters
        ----------
        Var: MetricVar
            variable for a metric
        type: str
            type of plot
        """
        name = self._filenames_lut(type=type)
        ref_meta, mds_meta, other_meta = Var.get_varmeta()
        # fetch parts of the name for the variable
        if not type in ["mapplot_tc", "mapplot_double"]:
            parts = [Var.metric]
            if mds_meta:
                parts.extend([mds_meta[0], mds_meta[1]['short_name']])
        else:
            parts = [ref_meta[0], ref_meta[1]['short_name']]
            if type == "mapplot_tc":
                # necessary to respect old naming convention
                for dss in  Var.other_dss:
                    parts.extend([dss[0], dss[1]['short_name']])
                parts.extend([Var.metric, mds_meta[0], mds_meta[1]['short_name']])
            parts.extend([mds_meta[0], mds_meta[1]['short_name'], Var.metric])

        name = name.format(*parts)

        return name

    def _yield_values(
            self,
            metric: str,
            tc: bool = False,
            stats: bool = True,
            mean_ci: bool = True,
    ) -> tuple:
        """
        Get iterable with pandas dataframes for all variables of a metric to plot

        Parameters
        ----------
        metric: str
            metric name
        tc: bool, default is False
            True if TC. Then, caption starts with "Other Data:"
        stats: bool
            If true, append the statistics to the caption
        mean_ci: bool
            If True, 'Mean CI: {value}' is added to the caption

        Yield
        -----
        df: pd.DataFrame
            dataframe with variable values and caption name
        Var: QA4SMMetricVariable
            variable corresponding to the dataframe
        ci: pd.DataFrame
            dataframe with "upper" and "lower" CI
        """
        Vars = self.img._iter_vars(type="metric", filter_parms={"metric":metric})

        for n, Var in enumerate(Vars):
            values = Var.values[Var.varname]
            # changes if it's a common-type Var
            if Var.g == 0:
                box_cap_ds = 'All datasets'
            else:
                box_cap_ds = self._box_caption(Var, tc=tc)
            # setting in global for caption stats
            if globals.boxplot_printnumbers:
                box_cap = '{}'.format(box_cap_ds)
                if stats:
                    box_stats = plm._box_stats(values)
                    box_cap = box_cap + "\n{}".format(box_stats)
            else:
                box_cap = box_cap_ds
            df = values.to_frame(box_cap)

            ci = self.img.get_cis(Var)
            if ci:  # could be that variable doesn't have CIs - empty list
                ci = pd.concat(ci, axis=1)
                label = ""
                if mean_ci:
                    # get the mean CI range
                    diff = ci["upper"] - ci["lower"]
                    ci_range = float(diff.mean())
                    label = "\nMean CI range: {:.3g}".format(ci_range)
                df.columns = [
                    df.columns[0] + label
                ]
            else:
                ci = None
            # values are all Nan or NaNf - not plotted
            df_arr = df.to_numpy()
            if np.isnan(df_arr).all() or df_arr.size == 0:
                continue

            yield df, Var, ci

    def _boxplot_definition(
            self,
            metric:str,
            df:pd.DataFrame,
            type:str,
            ci=None,
            offset=0.07,
            Var=None,
            **kwargs
    ) -> tuple:
        """
        Define parameters of plot

        Parameters
        ----------
        df: pd.DataFrame
            dataframe to plot
        type: str
            one of _titles_lut
        ci : list
            list of Dataframes containing "upper" and "lower" CIs
        xticks: list
            caption to each boxplot (or triplet thereof)
        offset: float
            offset of boxplots
        Var: QA4SMMetricVariable, optional. Default is None
            Specified in case mds meta is needed
        """
        # plot label
        parts = [globals._metric_name[metric]]
        parts.append(globals._metric_description[metric].format(
            globals._metric_units[self.ref['short_name']]))
        label = "{}{}".format(*parts)
        # generate plot
        figwidth = globals.boxplot_width * (len(df.columns) + 1)
        # otherwise it's too narrow
        if metric == "n_obs":
            figwidth = figwidth + 0.2
        figsize = [figwidth, globals.boxplot_height]
        fig, ax = plm.boxplot(
            df=df,
            ci=ci,
            label=label,
            figsize=figsize,
            dpi=globals.dpi
        )
        if not Var:
            # when we only need reference dataset from variables (i.e. is the same):
            for Var in self.img._iter_vars(type="metric", filter_parms={"metric":metric}):
                Var = Var
                break
        if not type=="metadata":
            title = self.create_title(Var, type=type)
            ax.set_title(title, pad=globals.title_pad)
        # add watermark
        if self.img.has_CIs:
            offset = 0.08  # offset smaller as CI variables have a larger caption
        if Var.g == 0:
            offset = 0.03  # offset larger as common metrics have a shorter caption
        if globals.watermark_pos not in [None, False]:
            plm.make_watermark(fig, offset=offset)

        return fig, ax

    def _save_plot(self, out_name:str, out_types:str='png') -> list:
        """
        Save plot with name to self.out_dir

        Parameters
        ----------
        out_name: str
            name of output file
        out_types: str or list
            extensions which the files should be saved in

        Returns
        -------
        fnames: list
            list of file names with all the extensions
        """
        fnames = []
        if isinstance(out_types, str):
            out_types = [out_types]
        for ext in out_types:
            fname = self._standard_filename(out_name, out_type=ext)
            if fname.exists():
                warn('Overwriting file {}'.format(fname.name))
            try:
                plt.savefig(fname, dpi='figure', bbox_inches='tight')
            except ValueError:
                continue
            fnames.append(fname.absolute())

        return fnames

    def boxplot_basic(
            self, metric:str,
            out_name:str=None,
            out_types:str='png',
            save_files:bool=False,
            **plotting_kwargs
    ) -> Union[list, None]:
        """
        Creates a boxplot for common and double metrics. Saves a figure and returns Matplotlib fig and ax objects for
        further processing.

        Parameters
        ----------
        metric : str
            metric that is collected from the file for all datasets and combined
            into one plot.
        out_name: str
            name of output file
        out_types: str or list
            extensions which the files should be saved in
        save_file: bool, optional. Default is False
            wether to save the file in the output directory
        plotting_kwargs: arguments for _boxplot_definition function

        Returns
        -------
        fnames: list
            list of file names with all the extensions
        """
        fnames, values = [], []
        ci = []
        # we take the last iterated value for Var and use it for the file name
        for df, Var, var_ci in self._yield_values(metric=metric):
            values.append(df)
            if var_ci is not None:
                ci.append(var_ci)

        # handle empty results
        if not values:
            return None
        # put all Variables in the same dataframe
        values = pd.concat(values)
        # create plot
        fig, ax = self._boxplot_definition(
            metric=metric,
            df=values,
            type='boxplot_basic',
            ci=ci,
            **plotting_kwargs
        )
        if not out_name:
            out_name = self.create_filename(Var, type='boxplot_basic')
        # save or return plotting objects
        if save_files:
            fnames = self._save_plot(out_name, out_types=out_types)
            plt.close('all')

            return fnames

        else:
            return fig, ax

    def boxplot_tc(
            self, metric:str,
            out_name:str=None,
            out_types:str='png',
            save_files:bool=False,
            **plotting_kwargs
    ) -> list:
        """
        Creates a boxplot for TC metrics. Saves a figure and returns Matplotlib fig and ax objects for
        further processing.

        Parameters
        ----------
        metric : str
            metric that is collected from the file for all datasets and combined
            into one plot.
        out_name: str
            name of output file
        out_types: str or list
            extensions which the files should be saved in
        save_file: bool, optional. Default is False
            wether to save the file in the output directory
        plotting_kwargs: arguments for _boxplot_definition function

        Returns
        -------
        fnames: list
            list of file names with all the extensions
        """
        fnames = []
        # group Vars and CIs relative to the same dataset
        metric_tc, ci = {}, {}
        for df, Var, var_ci in self._yield_values(metric=metric, tc=True):
            id, names = Var.metric_ds
            if var_ci is not None:
                if id in ci.keys():
                    ci[id].append(var_ci)
                else:
                    ci[id] = [var_ci]
            if id in metric_tc.keys():
                metric_tc[id][0].append(df)
            else:
                metric_tc[id] = [df], Var

        for id, values in metric_tc.items():
            dfs, Var = values
            df = pd.concat(dfs)
            # values are all Nan or NaNf - not plotted
            if np.isnan(df.to_numpy()).all():
                continue
            # necessary if statement to prevent key error when no CIs are in the netCDF
            if ci:
                ci_id = ci[id]
            else:
                ci_id = None
            # create plot
            fig, ax = self._boxplot_definition(
                metric=metric,
                df=df,
                ci=ci_id,
                type='boxplot_tc',
                Var=Var,
                **plotting_kwargs
            )
            # save. Below workaround to avoid same names
            if not out_name:
                save_name = self.create_filename(Var, type='boxplot_tc')
            else:
                save_name = out_name
            # save or return plotting objects
            if save_files:
                fns = self._save_plot(save_name, out_types=out_types)
                fnames.extend(fns)
                plt.close('all')

        if save_files:
            return fnames

    def mapplot_var(
            self, Var,
            out_name:str=None,
            out_types:str='png',
            save_files:bool=False,
            **plotting_kwargs
    ) -> list:
        """
        Plots values to a map, using the values as color. Plots a scatterplot for
        ISMN and a image plot for other input values.

        Parameters
        ----------
        var : QA4SMMetricVariab;e
            Var in the image to make the map for.
        out_name: str
            name of output file
        out_types: str or list
            extensions which the files should be saved in
        save_file: bool, optional. Default is False
            wether to save the file in the output directory
        plotting_kwargs: arguments for mapplot function

        Returns
        -------
        fnames: list
            list of file names with all the extensions
        """
        ref_meta, mds_meta, other_meta = Var.get_varmeta()
        metric = Var.metric
        ref_grid_stepsize = self.img.ref_dataset_grid_stepsize
        # create mapplot
        fig, ax = plm.mapplot(df=Var.values[Var.varname],
                          metric=metric,
                          ref_short=ref_meta[1]['short_name'],
                          ref_grid_stepsize=ref_grid_stepsize,
                          plot_extent=None,  # if None, extent is sutomatically adjusted (as opposed to img.extent)
                          **plotting_kwargs)

        # title and plot settings depend on the metric group
        if Var.g == 0:
            title = "{} between all datasets".format(globals._metric_name[metric])
            out_name = self.create_filename(Var, type='mapplot_common')
        elif Var.g == 2:
            title = self.create_title(Var=Var, type='mapplot_basic')
            out_name = self.create_filename(Var, type='mapplot_double')
        else:
            title = self.create_title(Var=Var, type='mapplot_tc')
            out_name = self.create_filename(Var, type='mapplot_tc')

        # use title for plot, make watermark
        ax.set_title(title, pad=globals.title_pad)
        if globals.watermark_pos not in [None, False]:
            plm.make_watermark(fig, globals.watermark_pos, for_map=True, offset=0.04)

        # save file or just return the image
        if save_files:
            fnames = self._save_plot(out_name, out_types=out_types)

            return fnames

        else:
            return fig, ax

    def mapplot_metric(
            self, metric:str,
            out_types:str='png',
            save_files:bool=False,
            **plotting_kwargs
    ) -> list:
        """
        Mapplot for all variables for a given metric in the loaded file.

        Parameters
        ----------
        metric : str
            Name of a metric. File is searched for variables for that metric.
        out_name: str
            name of output file
        out_types: str or list
            extensions which the files should be saved in
        save_file: bool, optional. Default is False
            wether to save the file in the output directory
        plotting_kwargs: arguments for mapplot function

        Returns
        -------
        fnames : list
            List of files that were created
        """
        fnames = []
        for Var in self.img._iter_vars(type="metric", filter_parms={"metric":metric}):
            if not (np.isnan(Var.values.to_numpy()).all() or Var.is_CI):
                fns = self.mapplot_var(Var,
                                       out_name=None,
                                       out_types=out_types,
                                       save_files=save_files,
                                       **plotting_kwargs)
            # values are all Nan or NaNf - not plotted
            else:
                continue
            if save_files:
                fnames.extend(fns)
                plt.close('all')

        if fnames:
            return fnames

    def plot_metric(
            self, metric:str,
            out_types:str='png',
            save_all:bool=True,
            **plotting_kwargs
    ) -> tuple:
        """
        Plot and save boxplot and mapplot for a certain metric

        Parameters
        ----------
        metric: str
            name of the metric
        out_types: str or list
            extensions which the files should be saved in
        save_all: bool, optional. Default is True.
            all plotted images are saved to the output directory
        plotting_kwargs: arguments for mapplot function.
        """
        Metric = self.img.metrics[metric]
        if Metric.g == 0 or Metric.g == 2:
            fnames_bplot = self.boxplot_basic(metric=metric,
                                              out_types=out_types,
                                              save_files=save_all,
                                              **plotting_kwargs)
        elif Metric.g == 3:
            fnames_bplot = self.boxplot_tc(metric=metric,
                                           out_types=out_types,
                                           save_files=save_all,
                                           **plotting_kwargs)
        fnames_mapplot = self.mapplot_metric(metric=metric,
                                             out_types=out_types,
                                             save_files=save_all,
                                             **plotting_kwargs)

        return fnames_bplot, fnames_mapplot

    def meta_single(
            self,
            metric:str,
            metadata:str,
            df:pd.DataFrame=None,
            axis=None,
            plot_type:str="catplot",
            **plotting_kwargs
    ) -> Union[tuple, None]:
        """
        Boxplot of a metric grouped by the given metadata.

        Parameters
        ----------
        metric : str
            specified metric
        metadata : str
            specified metadata
        df : pd.DataFrame, optional
            metric values can be specified, in which case they will be used from here and
            not parsed from the metric name
        axis : matplotlib.axes.Axis, optional
            if provided, the function will create the plot on the specified axis
        plot_type : str, default is 'catplot'
            one of 'catplot' or 'multiplot', defines the type of plots for the 'classes' and 'continuous'
            metadata types

        Returns
        -------
        fig : matplotlib.figure.Figure
            the boxplot
        ax : matplotlib.axes.Axes
        """
        values = []
        for data, Var, var_ci in self._yield_values(metric=metric, stats=False, mean_ci=False):
            values.append(data)

        values = pd.concat(values, axis=1)
        # override values from metric
        if df is not None:
            values = df
        # get meta and select only metric values with metadata available
        meta_values = self.img.metadata[metadata].values.dropna()
        values = values.reindex(index=meta_values.index)
        mu = globals._metric_description[metric].format(globals._metric_units[self.ref['short_name']])

        out = plm.boxplot_metadata(
            df=values,
            metadata_values=meta_values,
            ax_label=Var.Metric.pretty_name + mu,
            axis=axis,
            plot_type=plot_type,
            **plotting_kwargs
        )

        if axis is None:
            fig, ax = out

            return fig, ax

    def meta_combo(
            self,
            metric:str,
            metadata:str,
            metadata_discrete:str,
    ):
        """
        Cross-boxplot between two given metadata types

        Parameters
        ----------
        metric : str
            specified metric
        metadata: str
            'continuous' or 'classes' metadata which provides the number of subplots (bins)
        metadata_discrete : str
            'discrete' metadata which is shown in the subplots

        Returns
        -------
        fig : matplotlib.figure.Figure
            the boxplot
        ax : matplotlib.axes.Axes
        """
        values = []
        for df, Var, ci in self._yield_values(metric=metric, stats=False, mean_ci=False):
            values.append(df)
        values = pd.concat(values, axis=1)

        metric_name = globals._metric_name[metric]
        metric_units = globals._metric_description[metric].format(
            globals._metric_units[self.img.datasets.ref["short_name"]]
        )

        Meta_cont = self.img.metadata[metadata]
        meta_values = Meta_cont.values.dropna()

        bin_funct = plm.bin_function_lut(globals.metadata[metadata][2])
        binned_values = bin_funct(
            df=values,
            metadata_values=meta_values,
            meta_key=metadata,
        )
        # dictionary with subset values
        values_subset = {
            a_bin: values.reindex(index=binned_values[a_bin].index) for a_bin in binned_values.keys()
        }
        kwargs = {
            "metric": metric,
            "metadata": metadata_discrete,
            "common_y": metric_name + metric_units
        }
        n_datasets = len(self.img.datasets.others)
        fig, axes = plm.aggregate_subplots(
            to_plot=values_subset,
            funct=self.meta_single,
            n_bars=n_datasets,
            **kwargs
        )

        return fig, axes

    def plot_metadata(
            self, metric:str,
            metadata:str,
            metadata_discrete:str=None,
            save_file:bool=False,
            out_types:str='png',
            **plotting_kwargs
    ):
        """
        Wrapper built around the 'meta_single' or 'meta_combo' functions to produce a metadata-based boxplot of a
        metric.

        Parameters
        __________
        metric : str
            name of metric to plot
        metadata : str
            name of metadata to subdivide the metric results
        metadata_discrete : str
            name of the metadata of the type 'discrete'

        Retrun
        ------
        fig : matplotlib.figure.Figure
            the boxplot
        ax : matplotlib.axes.Axes
        """
        if metadata_discrete is None:
            fig, ax = self.meta_single(
                metric=metric, metadata=metadata, **plotting_kwargs
            )
            metadata_tuple = [metadata]

        else:
            metadata_tuple = [metadata, metadata_discrete]
            if not any(globals.metadata[i][2] == "discrete" for i in metadata_tuple):
                raise ValueError(
                    "One of the provided metadata names should correspond to the 'discrete' type, see globals.py"
                )
            if all(globals.metadata[i][2] == "discrete" for i in metadata_tuple):
                raise ValueError(
                    "At least one of the provided metadata should not be of the 'continuous' type"
                )
            fig, ax = self.meta_combo(
                metric=metric, metadata=metadata,
                metadata_discrete=metadata_discrete, **plotting_kwargs
            )
        meta_names = [globals.metadata[i][0] for i in metadata_tuple]
        title = self._titles_lut("metadata").format(
            globals._metric_name[metric],
            ", ".join(meta_names),
            self.img.datasets.ref["pretty_title"]
        )
        fig.suptitle(title)

        plm.make_watermark(fig=fig, offset=0)

        if save_file:
            out_name = self._filenames_lut("metadata").format(
                metric,
                "_and_".join(metadata_tuple)
            )
            self._save_plot(out_name, out_types=out_types)

            return out_name

        else:
            return fig, ax

    def plot_save_metadata(self, metric):
        """
        Plots and saves three metadata boxplots per metric (defined in globals.py):

        1. Boxplot by land cover class (2010 map)
        2. Boxplot by Koeppen-Geiger climate classification
        3. Boxplot by instrument depth and soil type (granularity)

        Parameters
        ----------
        metric : str
            name of metric

        Return
        ------
        filenames: list
            list of file names
        """
        filenames = []

        # makes no sense to plot the metadata for some metrics
        if metric in globals._metadata_exclude:
            return filenames

        for type, meta_keys in globals.out_metadata_plots.items():
            # the presence of instrument_depth in the out file depends on the ismn release version
            if all(meta_key in self.img.metadata.keys() for meta_key in meta_keys):
                outfile = self.plot_metadata(metric, *meta_keys, save_file=True)
                filenames.append(outfile)
            else:
                warnings.warn(
                    "Not all: " + ", ".join(meta_keys) + " are present in the netCDF variables"
                )

        return filenames


matplotlib.pyplot.savefig matplotlib.pyplot.close