from manifest_edit.plugin.mpd import ManifestIteratorPlugin
import re
from schema import Schema, Or


class Plugin(ManifestIteratorPlugin):
    """
    manifest_order plugin.

    The purpose of this plugin is to rearrange Periods, Adaptation Sets or
    Representations in manifest files based on several possible criteria.

    Limitations: only implemented for .mpd manifests

    Uses ManifestIterator to "select" the parts that need to be reordered.

    Supported ordering strategies are:

    - full sort based on a provided key and "desc" or "asc" criteria
    - manual sorting, where the user must provide an ordered list of
      elements that need to match a given <key>, <regular expression> pair.
    """

    _name = __name__

    _keys_full_sort = ["orderBy", "orderCriteria"]

    _schema_full_sort = Schema(
        {_keys_full_sort[0]: Or(str, int), _keys_full_sort[1]: Or("desc", "asc")}
    )

    _keys_manual_order = ["manualOrder"]

    _schema_manual_order = Schema(
        {_keys_manual_order[0]: [{str: lambda regex: re.compile(regex)}]}
    )

    def schema(self):
        return Schema(Or(self._schema_full_sort, self._schema_manual_order))

    def _intersection(self, lst1, lst2):
        lst3 = [value for value in lst1 if value in lst2]
        return lst3

    def reorderManifestLists(self, manifest, storage):

        # We will just use manifest_iterator with all possible keywords.It will
        # return nothing in case the keyword is not present in self.config() .
        # Maybe this can be optimized and only use it on what we know it's
        # present.
        for order_config, list_to_order in self.config(manifest, storage):
            if all([k in order_config.keys() for k in self._keys_full_sort]):
                list_to_order.sort(
                    key=lambda repr: getattr(
                        repr, order_config[self._keys_full_sort[0]]
                    ),
                    reverse=True
                    if order_config.get(self._keys_full_sort[1], "") == "desc"
                    else False,
                )
            elif all([k in order_config.keys() for k in self._keys_manual_order]):
                # iterate on list of key,value and take note of index of list
                for idx, config_item in enumerate(
                    order_config.get(self._keys_manual_order[0], {})
                ):
                    # get from config the key to look for
                    # get from config the re to match the value
                    found_at = []
                    for key, expression in config_item.items():
                        # See if an element of the manifest matches the expression
                        # and, if yes, get its index in the list it belongs
                        regex = re.compile(expression)

                        found_at.append(
                            [
                                i
                                for i, x in enumerate(list_to_order)
                                if regex.search(str(getattr(x, key, None)))
                            ]
                        )

                    # Intersect the lists where element was found by the individual
                    #  selection criteria to find those that match all of them.
                    found_at_by_all_criteria = found_at[0]
                    for index_list in found_at:
                        found_at_by_all_criteria = self._intersection(
                            found_at_by_all_criteria, index_list
                        )

                    if len(found_at_by_all_criteria):
                        # if a matching element is found, we will move it to
                        # the desired location. If it is found multiple times,
                        # only the first occurence is considered. If it is not
                        # found, nothing happens
                        list_to_order.insert(
                            idx, list_to_order.pop(found_at_by_all_criteria[0])
                        )

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