"""
Module containing a preprocessor that removes cells if they match
one or more regular expression.
"""

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

from traitlets import Set, Unicode
from .base import Preprocessor


class TagRemovePreprocessor(Preprocessor):
    """
    Removes inputs, outputs, or cells from a notebook that
    have tags that designate they are to be removed prior to exporting
    the notebook.

    remove_cell_tags
        removes cells tagged with these values

    remove_all_outputs_tags
        removes entire output areas on cells
        tagged with these values

    remove_single_output_tags
        removes individual output objects on
        outputs tagged with these values

    remove_input_tags
        removes inputs tagged with these values
    """

    remove_cell_tags = Set(Unicode(), default_value=[],
            help=("Tags indicating which cells are to be removed,"
                  "matches tags in ``cell.metadata.tags``.")).tag(config=True)
    remove_all_outputs_tags = Set(Unicode(), default_value=[],
            help=("Tags indicating cells for which the outputs are to be removed,"
                  "matches tags in ``cell.metadata.tags``.")).tag(config=True)
    remove_single_output_tags = Set(Unicode(), default_value=[],
            help=("Tags indicating which individual outputs are to be removed,"
                  "matches output *i* tags in ``cell.outputs[i].metadata.tags``.")
            ).tag(config=True)
    remove_input_tags = Set(Unicode(), default_value=[],
            help=("Tags indicating cells for which input is to be removed,"
                  "matches tags in ``cell.metadata.tags``.")).tag(config=True)
    remove_metadata_fields = Set(
        {'collapsed', 'scrolled'}
    ).tag(config=True)

    def check_cell_conditions(self, cell, resources, index):
        """
        Checks that a cell has a tag that is to be removed

        Returns: Boolean.
        True means cell should *not* be removed.
        """

        # Return true if any of the tags in the cell are removable.
        return not self.remove_cell_tags.intersection(
                cell.get('metadata', {}).get('tags', []))

    def preprocess(self, nb, resources):
        """
        Preprocessing to apply to each notebook. See base.py for details.
        """
        # Skip preprocessing if the list of patterns is empty
        if not any([self.remove_cell_tags,
                    self.remove_all_outputs_tags,
                    self.remove_single_output_tags,
                    self.remove_input_tags
                    ]):
            return nb, resources

        # Filter out cells that meet the conditions
        nb.cells = [self.preprocess_cell(cell, resources, index)[0]
                    for index, cell in enumerate(nb.cells)
                    if self.check_cell_conditions(cell, resources, index)]

        return nb, resources

    def preprocess_cell(self, cell, resources, cell_index):
        """
        Apply a transformation on each cell. See base.py for details.
        """
        
        if (self.remove_all_outputs_tags.intersection(
            cell.get('metadata', {}).get('tags', []))
            and cell.cell_type == 'code'):

            cell.outputs = []
            cell.execution_count = None
            # Remove metadata associated with output
            if 'metadata' in cell:
                for field in self.remove_metadata_fields:
                    cell.metadata.pop(field, None)
        
        if (self.remove_input_tags.intersection(
                cell.get('metadata', {}).get('tags', []))):
            cell.transient = {
                'remove_source': True
                }

        if cell.get('outputs', []):
            cell.outputs = [output
                            for output_index, output in enumerate(cell.outputs)
                            if self.check_output_conditions(output,
                                                            resources,
                                                            cell_index,
                                                            output_index)
                            ]
        return cell, resources

    def check_output_conditions(self, output, resources,
                                cell_index, output_index):
        """
        Checks that an output has a tag that indicates removal.

        Returns: Boolean.
        True means output should *not* be removed.
        """
        return not self.remove_single_output_tags.intersection(
                output.get('metadata', {}).get('tags', []))
