"""
manifest_edit.libfmp4.mpd

Notice that this is just a tiny python wrapper around libfmp4. It is here just
to provide:

- a sort method to pybind11 vector types that has the same interface than
  python's native sort on list
- transparent access to adaptationSet and Representation "common" attributes,
  i.e. ``representation.mimeType`` instead of
  ``representation.common.mimeType``
- a module to scan for pydocs so that some libfmp4 documentation is produced

This package contains functionalities that are specific to .mpd (DASH)
manifests manipulation.

It contains data types that represent elements and attributes of a Media
Presentation as described by ISO/IEC FDIS 23009-1 4th edition.

When working with libfmp4.mpd, you abstract from the actual textual
representation format of the manifest (e.g. XML format) and deal directly with
nested data structures (similar to c++ struct) that represent the manifest
hierarchy.

Serialization/Deserialization to/from MPD is available for manifests with the
load_manifest and save_manifest functions. All other methods
included in this library to render object to strings are only to be used for
debug purposes and are not suited to produce a legal XML representation of a
manifest or any of its parts.

It is worth remembering this package's naming convention:

 - free functions use lower snake case (e.g. my_function)
 - class names use upper CamelCase convention (e.g. MyClass)
 - class attributes use lower camelCase convention (e.g. myAttribute).

When using manifest-edit.libfmp4.mpd data types, some effort has been put to
make it usable with simple mnemonic rules to find types and members that you
may need. In particular, this library's classes and attributes names default to
the same names specified in ISO/IEC FDIS 23009-1 (save for the lower camelCase
naming convention). Some examples:

 - the "Period" element (ISO/IEC FDIS 23009-1 5.3.2) is represented by the
   "libfmp4.mpd.Period" data type. The "AdaptationSet" element
   (ISO/IEC FDIS 23009-1 5.3.3) is represented by the
   "libfmp4.mpd.AdaptationSet" data type. A notable exception to this rule
   is that the root "MPD" element is represented in the "libfmp4.mpd.Manifest"
   data type.

 - the "id" attribute of the "MPD" element is represented by the "id" attribute
   of the Manifest data type; the "start" attribute of a "Period" element is
   represented by the "start" attribute of the Period data type.

 - the "BaseURL" element of the "MPD" element is represented by the "baseURL"
   attribute of the "libfmp4.mpd.Manifest" data type: notice the naming
   convention with lower capital initial letter. Of course this has no
   influence on the string that is going to be included in the XML
   representation of the manifest, which will always reflect what specified in
   the standard.

Another general naming rule to keep in mind is that wherever the standard
specifies that an element can occur more than once (i.e. "0 ... N" is
specified), you are going to find these occurrences in a vector member,
with the same name of the element in the plural form. Some examples:

 - you will find all "Period" elements included in "MPD" in the member
   "periods" of the "libfmp4.mpd.Manifest" data type

 - you will find all "AdaptationSet" elements included in a "Period" in the
   member "adaptationSets" of the "libfmp4.mpd.Period" data type

It must be noticed that even though these vectors behave for some aspects as
python native lists, they are indeed C++ std::vectors exposed to python in a
way that ``resembles`` a native python list.

You can modify them in place, use slice notation and sort them and they will
have most of the methods you'd expect from a python list, however, copies
will be involved any time you take/push an element to it and references to
vector elements will not be valid anymore if you change the vector. See
test_stl_vector_algorithm.py in the test folder for more deatils.
"""
# import everything from libfmp4.mpd
from libfmp4.mpd import *  # noqa: F403


def std_sort_wrapper(self, key=lambda _: _, reverse=False):
    """
    std::vectors are bound to python as opaque types and do not behave as
    python lists. However, using stl_bind.h they are usable enough.
    Unfortunately, sort is not included so it needs to be added.

    The goal is to mimic the python lists "sort" interface, but internally
    sorting using std:sort on the original std::vector to sort it in place
    without copies.

    This "std_sort_wrapper" has the expected python signature and the
    responsibility of wrapping the "key" lambda (which has an unknown
    return type, so hardly usable from C++) to one that has a predictable
    bool<elem1, elem2> signature. In this way, in fmp4_py.cpp you can
    cast its return value to a native C++ type (bool) easily.
    """
    if reverse:
        self.std_sort(lambda elem1, elem2: key(elem1) > key(elem2))
    else:
        self.std_sort(lambda elem1, elem2: key(elem1) < key(elem2))


setattr(Representations, "sort", std_sort_wrapper)

setattr(Descriptors, "sort", std_sort_wrapper)

setattr(ServiceDescriptions, "sort", std_sort_wrapper)

setattr(AdaptationSets, "sort", std_sort_wrapper)

setattr(Periods, "sort", std_sort_wrapper)

setattr(Events, "sort", std_sort_wrapper)

setattr(EventStreams, "sort", std_sort_wrapper)

setattr(PlaybackRates, "sort", std_sort_wrapper)

setattr(Labels, "sort", std_sort_wrapper)

setattr(BaseURLs, "sort", std_sort_wrapper)


# In order to make life easier to plugin developers we want to make sure that
# you can use a YAML configuration key name to directly access a member of any
# element in the manifest.
# i.e. (assuming your YAML is written to select an adaptation set by width)
# when handling
#
# adaptationSets:
#   - width: 720
#
# you want to be able to access the value of the adaptation set width by doing
#
# getattr(adaptationSet, "width")
#
# This is already the case for all data types in libfmp4, except for the
# AdaptationSet and Representation classes, where some attributes are stored
# instead in the "common" meber.
# Here we dynamically add properties so that you can get/set/modify
#
# ad_set.width
#
# as well as
#
# ad_set.common.width

setattr(
    AdaptationSet,
    "width",
    property(
        fget=lambda self: getattr(self.common, "width"),
        fset=lambda self, v: setattr(self.common, "width", v),
    ),
)
setattr(
    AdaptationSet,
    "height",
    property(
        fget=lambda self: getattr(self.common, "height"),
        fset=lambda self, v: setattr(self.common, "height", v),
    ),
)
setattr(
    AdaptationSet,
    "sar",
    property(
        fget=lambda self: getattr(self.common, "sar"),
        fset=lambda self, v: setattr(self.common, "sar", v),
    ),
)
setattr(
    AdaptationSet,
    "frameRate",
    property(
        fget=lambda self: getattr(self.common, "frameRate"),
        fset=lambda self, v: setattr(self.common, "frameRate", v),
    ),
)
setattr(
    AdaptationSet,
    "audioSamplingRate",
    property(
        fget=lambda self: getattr(self.common, "audioSamplingRate"),
        fset=lambda self, v: setattr(self.common, "audioSamplingRate", v),
    ),
)
setattr(
    AdaptationSet,
    "mimeType",
    property(
        fget=lambda self: getattr(self.common, "mimeType"),
        fset=lambda self, v: setattr(self.common, "mimeType", v),
    ),
)
setattr(
    AdaptationSet,
    "codecs",
    property(
        fget=lambda self: getattr(self.common, "codecs"),
        fset=lambda self, v: setattr(self.common, "codecs", v),
    ),
)
setattr(
    AdaptationSet,
    "startWithSAP",
    property(
        fget=lambda self: getattr(self.common, "startWithSAP"),
        fset=lambda self, v: setattr(self.common, "startWithSAP", v),
    ),
)
setattr(
    AdaptationSet,
    "maxPlayoutRate",
    property(
        fget=lambda self: getattr(self.common, "maxPlayoutRate"),
        fset=lambda self, v: setattr(self.common, "maxPlayoutRate", v),
    ),
)
setattr(
    AdaptationSet,
    "codingDependency",
    property(
        fget=lambda self: getattr(self.common, "codingDependency"),
        fset=lambda self, v: setattr(self.common, "codingDependency", v),
    ),
)
setattr(
    AdaptationSet,
    "scanType",
    property(
        fget=lambda self: getattr(self.common, "scanType"),
        fset=lambda self, v: setattr(self.common, "scanType", v),
    ),
)
setattr(
    AdaptationSet,
    "essentialProperties",
    property(
        fget=lambda self: getattr(self.common, "essentialProperties"),
        fset=lambda self, v: setattr(self.common, "essentialProperties", v),
    ),
)
setattr(
    AdaptationSet,
    "supplementalProperties",
    property(
        fget=lambda self: getattr(self.common, "supplementalProperties"),
        fset=lambda self, v: setattr(self.common, "supplementalProperties", v),
    ),
)
setattr(
    AdaptationSet,
    "audioChannelConfigurations",
    property(
        fget=lambda self: getattr(self.common, "audioChannelConfigurations"),
        fset=lambda self, v: setattr(self.common, "audioChannelConfigurations", v),
    ),
)
setattr(
    AdaptationSet,
    "labels",
    property(
        fget=lambda self: getattr(self.common, "labels"),
        fset=lambda self, v: setattr(self.common, "labels", v),
    ),
)
setattr(
    AdaptationSet,
    "groupLabels",
    property(
        fget=lambda self: getattr(self.common, "groupLabels"),
        fset=lambda self, v: setattr(self.common, "groupLabels", v),
    ),
)

setattr(
    Representation,
    "width",
    property(
        fget=lambda self: getattr(self.common, "width"),
        fset=lambda self, v: setattr(self.common, "width", v),
    ),
)
setattr(
    Representation,
    "height",
    property(
        fget=lambda self: getattr(self.common, "height"),
        fset=lambda self, v: setattr(self.common, "height", v),
    ),
)
setattr(
    Representation,
    "sar",
    property(
        fget=lambda self: getattr(self.common, "sar"),
        fset=lambda self, v: setattr(self.common, "sar", v),
    ),
)
setattr(
    Representation,
    "frameRate",
    property(
        fget=lambda self: getattr(self.common, "frameRate"),
        fset=lambda self, v: setattr(self.common, "frameRate", v),
    ),
)
setattr(
    Representation,
    "audioSamplingRate",
    property(
        fget=lambda self: getattr(self.common, "audioSamplingRate"),
        fset=lambda self, v: setattr(self.common, "audioSamplingRate", v),
    ),
)
setattr(
    Representation,
    "mimeType",
    property(
        fget=lambda self: getattr(self.common, "mimeType"),
        fset=lambda self, v: setattr(self.common, "mimeType", v),
    ),
)
setattr(
    Representation,
    "codecs",
    property(
        fget=lambda self: getattr(self.common, "codecs"),
        fset=lambda self, v: setattr(self.common, "codecs", v),
    ),
)
setattr(
    Representation,
    "startWithSAP",
    property(
        fget=lambda self: getattr(self.common, "startWithSAP"),
        fset=lambda self, v: setattr(self.common, "startWithSAP", v),
    ),
)
setattr(
    Representation,
    "maxPlayoutRate",
    property(
        fget=lambda self: getattr(self.common, "maxPlayoutRate"),
        fset=lambda self, v: setattr(self.common, "maxPlayoutRate", v),
    ),
)
setattr(
    Representation,
    "codingDependency",
    property(
        fget=lambda self: getattr(self.common, "codingDependency"),
        fset=lambda self, v: setattr(self.common, "codingDependency", v),
    ),
)
setattr(
    Representation,
    "scanType",
    property(
        fget=lambda self: getattr(self.common, "scanType"),
        fset=lambda self, v: setattr(self.common, "scanType", v),
    ),
)
setattr(
    Representation,
    "essentialProperties",
    property(
        fget=lambda self: getattr(self.common, "essentialProperties"),
        fset=lambda self, v: setattr(self.common, "essentialProperties", v),
    ),
)
setattr(
    Representation,
    "supplementalProperties",
    property(
        fget=lambda self: getattr(self.common, "supplementalProperties"),
        fset=lambda self, v: setattr(self.common, "supplementalProperties", v),
    ),
)
setattr(
    Representation,
    "audioChannelConfigurations",
    property(
        fget=lambda self: getattr(self.common, "audioChannelConfigurations"),
        fset=lambda self, v: setattr(self.common, "audioChannelConfigurations", v),
    ),
)
setattr(
    Representation,
    "labels",
    property(
        fget=lambda self: getattr(self.common, "labels"),
        fset=lambda self, v: setattr(self.common, "labels", v),
    ),
)
setattr(
    Representation,
    "groupLabels",
    property(
        fget=lambda self: getattr(self.common, "groupLabels"),
        fset=lambda self, v: setattr(self.common, "groupLabels", v),
    ),
)
