"""Module containing a preprocessor that removes metadata from code cells"""

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from traitlets import Bool, Set
from .base import Preprocessor

class ClearMetadataPreprocessor(Preprocessor):
    """
    Removes all the metadata from all code cells in a notebook.
    """

    clear_cell_metadata = Bool(True,
        help=("Flag to choose if cell metadata is to be cleared "
              "in addition to notebook metadata.")).tag(config=True)
    clear_notebook_metadata = Bool(True,
        help=("Flag to choose if notebook metadata is to be cleared "
              "in addition to cell metadata.")).tag(config=True)
    preserve_nb_metadata_mask = Set([('language_info', 'name')],
        help=("Indicates the key paths to preserve when deleting metadata "
               "across both cells and notebook metadata fields. Tuples of "
               "keys can be passed to preserved specific nested values")).tag(config=True)
    preserve_cell_metadata_mask = Set(
        help=("Indicates the key paths to preserve when deleting metadata "
               "across both cells and notebook metadata fields. Tuples of "
               "keys can be passed to preserved specific nested values")).tag(config=True)

    def current_key(self, mask_key):
        if isinstance(mask_key, str):
            return mask_key
        elif len(mask_key) == 0:
            # Safeguard
            return None
        else:
            return mask_key[0]

    def current_mask(self, mask):
        return { self.current_key(k) for k in mask if self.current_key(k) is not None }

    def nested_masks(self, mask):
        return { self.current_key(k[0]): k[1:] for k in mask if k and not isinstance(k, str) and len(k) > 1 }

    def nested_filter(self, items, mask):
        keep_current = self.current_mask(mask)
        keep_nested_lookup = self.nested_masks(mask)
        for k, v in items:
            keep_nested = keep_nested_lookup.get(k)
            if k in keep_current:
                if keep_nested is not None:
                    if isinstance(v, dict):
                        yield k, dict(self.nested_filter(v.items(), keep_nested))
                else:
                    yield k, v

    def preprocess_cell(self, cell, resources, cell_index):
        """
        All the code cells are returned with an empty metadata field.
        """
        if self.clear_cell_metadata:
            if cell.cell_type == 'code':
                # Remove metadata
                if 'metadata' in cell:
                    cell.metadata = dict(self.nested_filter(cell.metadata.items(), self.preserve_cell_metadata_mask))
        return cell, resources

    def preprocess(self, nb, resources):
        """
        Preprocessing to apply on each notebook.
        
        Must return modified nb, resources.
        
        Parameters
        ----------
        nb : NotebookNode
            Notebook being converted
        resources : dictionary
            Additional resources used in the conversion process.  Allows
            preprocessors to pass variables into the Jinja engine.
        """
        nb, resources = super().preprocess(nb, resources)
        if self.clear_notebook_metadata:
            if 'metadata' in nb:
                nb.metadata = dict(self.nested_filter(nb.metadata.items(), self.preserve_nb_metadata_mask))
        return nb, resources
