from manifest_edit.plugin.mpd import ManifestIteratorPlugin
from manifest_edit.context import Context
from manifest_edit import libfmp4
from schema import Schema, Optional, Or
import re


class Plugin(ManifestIteratorPlugin):
    """
    descriptor remove plugin.

    The purpose of this plugin is to remove a generic descriptor from a DASH
    manifest, in a specific position.

    The position is determined by the usual mechanisms implemented by
    ManifestIterator.

    The descriptor to be removed is specified by providing one or more from:

    - name: "type" (mandatory)
    - schemeIdUri: "schemeIdUri_content" (optional)
    - value: "value_content" (optional)
    - id: "id_content" (optional)

    If more than one of the above are provided, the match will be successful
    if and only if all of the selection conditions are met.

    If the selected element of the manifest contains more than one descriptor
    matching the selection condition, they all will be removed.

    "type" must be one of the supported descriptors, that is one of:

    - EssentialProperty
    - SupplementalProperty
    - Accessibility
    - Roles
    - AudioChannelConfiguration
    - UTCTiming

    """

    _name = __name__

    _keys = ["name", "schemeIdUri", "value", "id"]

    # This is just the specific ["how" config] for this plugin
    def schema(self):
        return Schema(
            {
                self._keys[0]: Or(
                    "EssentialProperty",
                    "SupplementalProperty",
                    "Accessibility",
                    "Role",
                    "AudioChannelConfiguration",
                    "UTCTiming",
                ),
                # Empty string in libfmp4 is equivalent to attribute
                # not present
                Optional(self._keys[1], default=".*"): lambda s: re.compile(s),
                Optional(self._keys[2], default=".*"): lambda s: re.compile(s),
                Optional(self._keys[3], default=".*"): lambda s: re.compile(s)
            }
        )

    # maps a descriptor name to the corresponding member in libfmp4. We
    # will use this to search for descriptors to remove
    descriptor_rules = {
        "EssentialProperty": "essentialProperties",
        "SupplementalProperty": "supplementalProperties",
        "Accessibility": "accessibilities",
        "Role": "roles",
        "AudioChannelConfiguration": "audioChannelConfigurations",
        "UTCTiming": "utcTimings", 
    }
    def _descriptorMatches(self, desc, descriptor_config):
        return (
            re.fullmatch(descriptor_config[self._keys[1]], str(getattr(desc, self._keys[1]))) and
            re.fullmatch(descriptor_config[self._keys[2]], str(getattr(desc, self._keys[2]))) and
            re.fullmatch(descriptor_config[self._keys[3]], str(getattr(desc, self._keys[3])))
        )

    def removeDescriptor(self, manifest, storage):
        for descriptor_config, element in self.config(manifest, storage):
            descriptors = getattr(element, self.descriptor_rules[descriptor_config["name"]])

            if descriptors:
                # remove == create a new list with all those element that do 
                # not match the description.
                edited_descriptors = libfmp4.mpd.Descriptors([
                    desc for desc in descriptors if not self._descriptorMatches(desc, descriptor_config)
                ])

                setattr(element, self.descriptor_rules[descriptor_config["name"]], edited_descriptors)


    def process(self, manifest, storage):
        self.removeDescriptor(manifest, storage)
