import abc


class BasePlugin(abc.ABC):
    """
    BasePlugin is the simplest base class that a Plugin can inherit from.
    It's the class you should use if your plugin is very simple or if you
    want total freedom on the design of your Plugin subclass.

    If you need to write a plugin that acts on dynamically selected
    representations, adaptationSets or periods, you should have a look
    at ManifestIteratorPlugin base class instead.

    BasePlugin is not specialized in any particular manifest format. It does
    not implement the complete Plugin interface and as such it is not enough
    to inherit from this base class alone to implement your own plugin. It is
    recommended to inherit from child classes libfmp4.mpd.BasePlugin,
    libfmp4.m3u8_main.BasePlugin, or libfmp4.m3u8_media.BasePlugin instead.

    """

    def onCreate(self, manifest_format, config):

        if not hasattr(self, "supported_format"):
            raise Exception(
                "Error in plugin creation! The plugin does not"
                " specify a sypported format. Please inherit"
                " from one of the supported format classes"
                " from the manifest_edit.plugin subpackage."
            )

        elif self.supported_format != manifest_format:
            raise Exception(
                "Error in plugin creation!"
                f" You have included a manifest with format {self.supported_format}"
                f" in a {manifest_format} pipeline!"
            )

        self._config = config

    def config(self, storage):
        # The "config" method of a BasePlugin will return a dictionary type
        if self._config is not None:
            return self._config
        else:
            # Plugins are allowed to have a None config, for cases where the
            #  configurations will be created by other plugins upstream the
            #  pipeline.
            # Plugins will pass a configuration down the pipeling by putting it
            #  into a {<plugin name> : {<config>}} dictionary. The split is
            #  there to remove the path. They would use the name of the plugin
            #  that must receive the configuration.
            return storage.get(self._name.split(".")[-1], {})

    def validate_config(self):
        self._config = self.schema().validate(self._config)
        return self._config

    @property
    @abc.abstractmethod
    def schema(self):
        raise NotImplementedError

    @abc.abstractmethod
    def process(self, manifest, storage):
        raise NotImplementedError
