import copy
import random
from typing import Optional, Union

import abjad

from ..utilities.remove_repeated_time_signatures import (
    remove_repeated_time_signatures,
)
from .Fader import Fader


class Adrifter():
    r"""``Adrifter`` takes two ``abjad.Container``'s and gradually drifts from
    one into the other, by fading out the first while fading in the second. It
    makes uses of ``auxjad.Fader`` for that.

    Example:
        Calling the object will return a tuple of two ``abjad.Selection``'s
        generated by the fading process. Each call of the object will apply the
        fading process to one of the two containers of the previous results.
        That is, either a logical tie of ``container_out`` is removed or a
        logical tie of ``container_in`` is added.

        >>> container_out = abjad.Container(r"fs'4 g'2 bf'4")
        >>> container_in = abjad.Container(r"\times 4/5 {cs''4 d''1}")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> selection_a, selection_b = adrifter()
        >>> score = abjad.Score([
        ...     abjad.Staff(selection_a),
        ...     abjad.Staff(selection_b),
        ... ])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                fs'4
                g'2
                bf'4
            }
            \new Staff
            {
                \time 4/4
                R1
            }
        >>

        .. figure:: ../_images/image-Adrifter-1.png

        >>> selection_a, selection_b = adrifter()
        >>> score = abjad.Score([
        ...     abjad.Staff(selection_a),
        ...     abjad.Staff(selection_b),
        ... ])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                fs'4
                g'2
                bf'4
            }
            \new Staff
            {
                \times 4/5 {
                    \time 4/4
                    r4
                    d''1
                }
            }
        >>

        .. figure:: ../_images/image-Adrifter-2.png

        >>> selection_a, selection_b = adrifter()
        >>> score = abjad.Score([
        ...     abjad.Staff(selection_a),
        ...     abjad.Staff(selection_b),
        ... ])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                fs'4
                r2
                bf'4
            }
            \new Staff
            {
                \times 4/5 {
                    \time 4/4
                    r4
                    d''1
                }
            }
        >>

        .. figure:: ../_images/image-Adrifter-3.png

        The property ``current_window`` can be used to access the current
        window without moving the head forwards.

        >>> notes = adrifter.current_window()
        >>> score = abjad.Score([
        ...     abjad.Staff(selection_a),
        ...     abjad.Staff(selection_b),
        ... ])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                fs'4
                r2
                bf'4
            }
            \new Staff
            {
                \times 4/5 {
                    \time 4/4
                    r4
                    d''1
                }
            }
        >>

        .. figure:: ../_images/image-Adrifter-4.png

    Example:
        To run through the whole process and output it as a tuple of two
        ``abjad.Staff``'s, use the method ``output_all()``.

        >>> container_out = abjad.Container(r"fs'4 g'2 bf'4")
        >>> container_in = abjad.Container(r"\times 4/5 {cs''4 d'1}")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                fs'4
                g'2
                bf'4
                fs'4
                g'2
                bf'4
                fs'4
                r2
                bf'4
                fs'4
                r2
                bf'4
                r2.
                bf'4
                R1
            }
            \new Staff
            {
                \time 4/4
                R1
                \times 4/5 {
                    cs''4
                    r1
                }
                \times 4/5 {
                    cs''4
                    r1
                }
                \times 4/5 {
                    cs''4
                    d'1
                }
                \times 4/5 {
                    cs''4
                    d'1
                }
                \times 4/5 {
                    cs''4
                    d'1
                }
            }
        >>

        .. figure:: ../_images/image-Adrifter-5.png

    Example:
        To run through just part of the process and output it as a tuple of two
        ``abjad.Staff``'s, use the method ``output_n()`` and pass the number of
        iterations as argument.

        >>> container_out = abjad.Container(r"e'8 fs'4. r2")
        >>> container_in = abjad.Container(r"c''2 ~ c''8 d''4.")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> staff_a, staff_b = adrifter.output_n(3)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                e'8
                fs'4.
                r2
                e'8
                fs'4.
                r2
                e'8
                r2..
            }
            \new Staff
            {
                \time 4/4
                R1
                c''2
                ~
                c''8
                r4.
                c''2
                ~
                c''8
                r4.
            }
        >>

        .. figure:: ../_images/image-Adrifter-6.png

    Example:
        The instances of ``Adrifter`` can also be used as an iterator, which
        can then be used in a for loop to run through the whole process. Note
        that unlike the methods ``output_n()`` and ``output_all()``, time
        signatures are added to each window returned by the adrifter. Use the
        function ``auxjad.remove_repeated_time_signatures()`` to clean the
        output when using ``Adrifter`` in this way.

        >>> container_out = abjad.Container(r"e'8 fs'4. r2")
        >>> container_in = abjad.Container(r"c''2 ~ c''8 d''4.")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> staff_a = abjad.Staff()
        >>> staff_b = abjad.Staff()
        >>> for selection_a, selection_b in adrifter:
        ...     staff_a.extend(selection_a)
        ...     staff_b.extend(selection_b)
        >>> auxjad.remove_repeated_time_signatures(staff_a)
        >>> auxjad.remove_repeated_time_signatures(staff_b)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                e'8
                fs'4.
                r2
                e'8
                fs'4.
                r2
                e'8
                r2..
                e'8
                r2..
                R1
            }
            \new Staff
            {
                \time 4/4
                R1
                c''2
                ~
                c''8
                r4.
                c''2
                ~
                c''8
                r4.
                c''2
                ~
                c''8
                d''4.
                c''2
                ~
                c''8
                d''4.
            }
        >>

        .. figure:: ../_images/image-Adrifter-7.png

    Example:
        This class can take many optional keyword arguments during its
        creation. Setting ``fade_in_first`` to ``True`` will ensure that a
        logical tie from ``container_in`` must be faded in before any logical
        ties of ``container_out`` are removed (default ``False``). Setting
        ``fade_out_last`` to ``True`` will ensure that all logical ties from
        ``container_in`` must be faded in before the very last logical tie of
        ``container_out`` is removed (default ``False``). The arguments
        ``initial_repetitions`` and ``final_repetitions`` set the number of
        repetitions of the initial and final measures (default ``1`` for both).
        ``repetition_chance`` sets the chance of a measure repeating itself,
        from ``0.0`` to ``1.0`` (default is ``0.0``, i.e. no repetitions).
        Setting ``weighted_duration`` to ``True`` will give more weight to the
        larger container (considering both its number of logical  ties as well
        its length), making it more likely that both will fade in and out at a
        similar rate (default value is ``False``). ``disable_rewrite_meter``
        disables the ``rewrite_meter()`` mutation which is applied to the
        container after every call, and ``omit_time_signatures`` will remove
        all time signatures from the output (both are ``False`` by default). By
        default, the first time signature is attached only to the first leaf of
        the first call (unless time signature changes require it). Any measure
        filled with rests will be rewritten using a multi-measure rest; set the
        ``use_multimeasure_rests`` to ``False`` to disable this behaviour. The
        properties ``boundary_depth``, ``maximum_dot_count``, and
        ``rewrite_tuplets`` are passed as arguments to abjad's
        ``rewrite_meter()``, see its documentation for more information.

        >>> container_out = abjad.Container(r"fs'4 g'2 bf'4")
        >>> container_in = abjad.Container(r"\times 4/5 {cs''4 d''1}")
        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            fade_in_first=True,
        ...                            fade_out_last=True,
        ...                            initial_repetitions=3,
        ...                            final_repetitions=3,
        ...                            repetition_chance=0.7,
        ...                            weighted_duration=True,
        ...                            disable_rewrite_meter=True,
        ...                            omit_time_signatures=True,
        ...                            use_multimeasure_rests=True,
        ...                            boundary_depth=True,
        ...                            maximum_dot_count=True,
        ...                            rewrite_tuplets=True,
        ...                            )
        >>> adrifter.fade_in_first
        True
        >>> adrifter.fade_out_last
        True
        >>> adrifter.initial_repetitions
        3
        >>> adrifter.final_repetitions
        3
        >>> adrifter.repetition_chance
        0.7
        >>> adrifter.weighted_duration
        True
        >>> adrifter.disable_rewrite_meter
        True
        >>> adrifter.omit_time_signatures
        True
        >>> adrifter.use_multimeasure_rests
        True
        >>> adrifter.boundary_depth
        True
        >>> adrifter.maximum_dot_count
        True
        >>> adrifter.rewrite_tuplets
        True

        Use the properties below to change these values after initialisation.

        >>> adrifter.fade_in_first = False
        >>> adrifter.fade_out_last = False
        >>> adrifter.initial_repetitions = 4
        >>> adrifter.final_repetitions = 7
        >>> adrifter.repetition_chance = 0.23
        >>> adrifter.weighted_duration = False
        >>> adrifter.disable_rewrite_meter = False
        >>> adrifter.omit_time_signatures = False
        >>> adrifter.use_multimeasure_rests = False
        >>> adrifter.boundary_depth = False
        >>> adrifter.maximum_dot_count = False
        >>> adrifter.rewrite_tuplets = False
        >>> adrifter.fade_in_first
        False
        >>> adrifter.fade_out_last
        False
        >>> assert adrifter.initial_repetitions
        4
        >>> assert adrifter.final_repetitions
        7
        >>> assert adrifter.repetition_chance
        0.23
        >>> adrifter.weighted_duration
        False
        >>> adrifter.disable_rewrite_meter
        False
        >>> adrifter.omit_time_signatures
        False
        >>> adrifter.use_multimeasure_rests
        False
        >>> adrifter.boundary_depth
        False
        >>> adrifter.maximum_dot_count
        False
        >>> adrifter.rewrite_tuplets
        False

    Example:
        Use the ``reset()`` method to reset the adrifter to its initial state.
        This can be used to restart the process at any time.

        >>> container_out = abjad.Container(r"fs'4 g'2 bf'4")
        >>> container_in = abjad.Container(r"\times 4/5 {cs''4 d'1}")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> staff_a = abjad.Staff()
        >>> staff_b = abjad.Staff()
        >>> for _ in range(3):
        ...     selection_a, selection_b = adrifter()
        ...     staff_a.extend(selection_a)
        ...     staff_b.extend(selection_b)
        >>> adrifter.reset()
        >>> selection_a, selection_b = adrifter()
        >>> staff_a.extend(selection_a)
        >>> staff_b.extend(selection_b)
        >>> auxjad.remove_repeated_time_signatures(staff_a)
        >>> auxjad.remove_repeated_time_signatures(staff_b)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                fs'4
                g'2
                bf'4
                fs'4
                g'2
                bf'4
                fs'4
                r2
                bf'4
                fs'4
                g'2
                bf'4
            }
            \new Staff
            {
                \time 4/4
                R1
                \times 4/5 {
                    cs''4
                    r1
                }
                \times 4/5 {
                    cs''4
                    r1
                }
                R1
            }
        >>

        .. figure:: ../_images/image-Adrifter-8.png

    Example:
        The function ``len()`` returns the sum of the number of pitched logical
        ties of both ``contents_in`` and ``contents_out``.

        >>> container_out = abjad.Container(r"c'4 d'4 ~ d'4 r4")
        >>> container_in = abjad.Container(r"r2 c''2")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> len(adrifter)
        3
        >>> container_out = abjad.Container(r"fs'4 g'2 bf'4")
        >>> container_in = abjad.Container(r"\times 4/5 {cs''4 d''1}")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> len(adrifter)
        5
        >>> container_out = abjad.Container(r"c'4 d'4 ~ d'4 r4")
        >>> container_in = abjad.Container(r"r2 <c'' e'' g''>2")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> len(adrifter)
        3

    Example:
        Setting the property ``fade_in_first`` to ``True`` will ensure that a
        logical tie from ``container_in`` must be faded in before any logical
        ties of ``container_out`` are removed (default ``False``). Setting
        ``fade_out_last`` to ``True`` will ensure that all logical ties from
        ``container_in`` must be faded in before the very last logical tie of
        ``container_out`` is removed (default ``False``).

        >>> container_out = abjad.Container(r"\time 3/4 r4 c'4 d'4")
        >>> container_in = abjad.Container(r"\time 3/4 a''4 g''2")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 3/4
                r4
                c'4
                d'4
                r2
                d'4
                r2
                d'4
                R1 * 3/4
                R1 * 3/4
            }
            \new Staff
            {
                \time 3/4
                R1 * 3/4
                R1 * 3/4
                a''4
                r2
                a''4
                r2
                a''4
                g''2
            }
        >>

        .. figure:: ../_images/image-Adrifter-9.png

        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            fade_out_last=True,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 3/4
                r4
                c'4
                d'4
                r2
                d'4
                r2
                d'4
                r2
                d'4
                R1 * 3/4
            }
            \new Staff
            {
                \time 3/4
                R1 * 3/4
                R1 * 3/4
                a''4
                r2
                a''4
                g''2
                a''4
                g''2
            }
        >>

        .. figure:: ../_images/image-Adrifter-10.png

        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            fade_in_first=True,
        ...                            fade_out_last=True,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 3/4
                r4
                c'4
                d'4
                r4
                c'4
                d'4
                r4
                c'4
                d'4
                r2
                d'4
                R1 * 3/4
            }
            \new Staff
            {
                \time 3/4
                R1 * 3/4
                a''4
                r2
                a''4
                g''2
                a''4
                g''2
                a''4
                g''2
            }
        >>

        .. figure:: ../_images/image-Adrifter-11.png

    Example:
        Setting ``weighted_duration`` to ``True`` will give more weight to the
        larger container (considering both its number of logical  ties as well
        its length), making it more likely that both will fade in and out at a
        similar rate (default value is ``False``).

        >>> container_out = abjad.Container(r"e'2 c'2")
        >>> container_in = abjad.Container(
        ...     r"c''8 d''8 e''8 f''8 g''8 a''8 b''8 c'''8")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                e'2
                c'2
                e'2
                c'2
                r2
                c'2
                r2
                c'2
                r2
                c'2
                R1
                R1
                R1
                R1
                R1
                R1
            }
            \new Staff
            {
                \time 4/4
                R1
                r4.
                f''8
                r2
                r4.
                f''8
                r2
                r4.
                f''8
                r8
                a''8
                r4
                r4.
                f''8
                r8
                a''8
                b''8
                r8
                r4.
                f''8
                r8
                a''8
                b''8
                r8
                r4.
                f''8
                r8
                a''8
                b''8
                c'''8
                r4
                e''8
                f''8
                r8
                a''8
                b''8
                c'''8
                r8
                d''8
                e''8
                f''8
                r8
                a''8
                b''8
                c'''8
                c''8
                d''8
                e''8
                f''8
                r8
                a''8
                b''8
                c'''8
                c''8
                d''8
                e''8
                f''8
                g''8
                a''8
                b''8
                c'''8
            }
        >>

        .. figure:: ../_images/image-Adrifter-12.png

        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            weighted_duration=True,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                e'2
                c'2
                e'2
                c'2
                r2
                c'2
                r2
                c'2
                r2
                c'2
                r2
                c'2
                r2
                c'2
                r2
                c'2
                r2
                c'2
                r2
                c'2
                R1
            }
            \new Staff
            {
                \time 4/4
                R1
                r4.
                f''8
                r2
                r4.
                f''8
                r2
                r4.
                f''8
                r8
                a''8
                r4
                r4.
                f''8
                r8
                a''8
                b''8
                r8
                r4
                e''8
                f''8
                r8
                a''8
                b''8
                r8
                r4
                e''8
                f''8
                g''8
                a''8
                b''8
                r8
                c''8
                r8
                e''8
                f''8
                g''8
                a''8
                b''8
                r8
                c''8
                r8
                e''8
                f''8
                g''8
                a''8
                b''8
                c'''8
                c''8
                d''8
                e''8
                f''8
                g''8
                a''8
                b''8
                c'''8
                c''8
                d''8
                e''8
                f''8
                g''8
                a''8
                b''8
                c'''8
            }
        >>

        .. figure:: ../_images/image-Adrifter-13.png

    Example:
        The properties ``initial_repetitions`` and ``final_repetitions`` set
        the number of repetitions of the initial and final measures (default is
        ``1`` for both).

        >>> container_out = abjad.Container(r"a'4 bf'2 r4")
        >>> container_in = abjad.Container(r"c''2 d''2")
        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            initial_repetitions=2,
        ...                            final_repetitions=3,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                a'4
                bf'2
                r4
                a'4
                bf'2
                r4
                r4
                bf'2
                r4
                r4
                bf'2
                r4
                r4
                bf'2
                r4
                R1
                R1
                R1
            }
            \new Staff
            {
                \time 4/4
                R1
                R1
                R1
                r2
                d''2
                c''2
                d''2
                c''2
                d''2
                c''2
                d''2
                c''2
                d''2
            }
        >>

        .. figure:: ../_images/image-Adrifter-14.png

    Example:
        Use ``repetition_chance`` to set the chance of a measure repeating
        itself, ranging from ``0.0`` to ``1.0`` (default is ``0.0``,
        i.e. no repetitions).

        >>> container_out = abjad.Container(r"a'4 bf'2 r4")
        >>> container_in = abjad.Container(r"c''2 d''2")
        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            repetition_chance=0.8,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_n(4)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                a'4
                bf'2
                r4
                a'4
                bf'2
                r4
                a'4
                bf'2
                r4
                a'4
                bf'2
                r4
            }
            \new Staff
            {
                \time 4/4
                R1
                r2
                d''2
                r2
                d''2
                r2
                d''2
            }
        >>

        .. figure:: ../_images/image-Adrifter-15.png

    Example:
        To disable time signatures altogether, initialise this class with the
        keyword argument ``omit_time_signatures`` set to ``True`` (default is
        ``False``), or use the ``omit_time_signatures`` property after
        initialisation.

        >>> container_out = abjad.Container(r"fs'4 g'2 bf'4")
        >>> container_in = abjad.Container(r"\times 4/5 {cs''4 d''1}")
        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            omit_time_signatures=True,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_n(3)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                fs'4
                g'2
                bf'4
                fs'4
                g'2
                r4
                fs'4
                g'2
                r4
            }
            \new Staff
            {
                R1
                R1
                \times 4/5 {
                    cs''4
                    r1
                }
            }
        >>

        .. figure:: ../_images/image-Adrifter-16.png

    ..  tip::

        All methods that return an ``abjad.Selection`` will add an initial time
        signature to it. The ``output_n()`` and ``output_all()`` methods
        automatically remove repeated time signatures. When joining selections
        output by multiple method calls, use
        ``auxjad.remove_repeated_time_signatures()`` on the whole container
        after fusing the selections to remove any unecessary time signature
        changes.

    Example:
        This function uses the default logical tie splitting algorithm from
        abjad's ``rewrite_meter()``. It can be disabled with the property
        ``disable_rewrite_meter``.

        >>> container_out = abjad.Container(r"c'8 d'4 e'8 ~ e'2")
        >>> container_in = abjad.Container(r"c'2 d'2")
        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            disable_rewrite_meter=True,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_n(3)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                c'8
                d'4
                e'8
                ~
                e'2
                r8
                d'4
                e'8
                ~
                e'2
                r8
                d'4
                e'8
                ~
                e'2
            }
            \new Staff
            {
                \time 4/4
                R1
                R1
                r2
                d'2
            }
        >>

        .. figure:: ../_images/image-Adrifter-17.png

        Arguments available for tweaking the output of abjad's
        ``rewrite_meter()`` are ``boundary_depth``, ``maximum_dot_count`` and
        ``rewrite_tuplets``, which work exactly as the identically named
        arguments of ``rewrite_meter()``.

    Example:
        Containers can be switched after initialisation. This will reset the
        process.

        >>> container_out = abjad.Container(
        ...     r"\time 3/4 e2 \times 2/3 {fs8 gs4}")
        >>> container_in = abjad.Container(r"\time 3/4 c'8 d' e' f' g' a'")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> adrifter.contents_out = abjad.Container(r"\time 3/4 a4. bf4.")
        >>> print(adrifter)
        {
            %%% \time 3/4 %%%
            a4.
            bf4.
        }
        {
            %%% \time 3/4 %%%
            c'8
            d'8
            e'8
            f'8
            g'8
            a'8
        }

    ..  warning::

        Do note that elements that span multiple notes (such as hairpins,
        ottava indicators, manual beams, etc.) can become problematic when
        notes containing them are split into two. Whenever possible, it is
        always better to attach those to the music after the phasing process is
        concluded.

    Example:
        This class can handle time signature changes.

        >>> container_out = abjad.Container(
        ...     r"\time 3/4 a'4 bf'2 ~ \time 2/4 bf'4 f'4")
        >>> container_in = abjad.Container(
        ...     r"\time 3/4 r16 cs''4.. e''4 \time 2/4 d''2")
        >>> adrifter = auxjad.Adrifter(container_out, container_in)
        >>> staff_a, staff_b = adrifter.output_n(3)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 3/4
                a'4
                bf'2
                ~
                \time 2/4
                bf'4
                f'4
                \time 3/4
                a'4
                bf'2
                ~
                \time 2/4
                bf'4
                f'4
                \time 3/4
                a'4
                bf'2
                ~
                \time 2/4
                bf'4
                r4
            }
            \new Staff
            {
                \time 3/4
                R1 * 3/4
                \time 2/4
                R1 * 1/2
                \time 3/4
                r16
                cs''4..
                r4
                \time 2/4
                R1 * 1/2
                \time 3/4
                r16
                cs''4..
                r4
                \time 2/4
                R1 * 1/2
            }
        >>

        .. figure:: ../_images/image-Adrifter-18.png

    Example:
        This class can also handle dynamics, articulations, chords, and
        tuplets. Hairpins might need manual tweaking if the leaf under which
        they terminate is removed.

        >>> container_out = abjad.Container(r"<c' e' g'>4.\p e'8--\f ~ e'2")
        >>> container_in = abjad.Container(
        ...     r"\times 2/3 {f'4-.\pp r4 <d' ef'>4->\f ~ } <d' ef'>2")
        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            fade_in_first=True,
        ...                            fade_out_last=True,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_all()
        >>> score = abjad.Score([staff_a, staff_b])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                <c' e' g'>4.
                \p
                e'8
                \f
                - \tenuto
                ~
                e'2
                <c' e' g'>4.
                \p
                e'8
                \f
                - \tenuto
                ~
                e'2
                r4.
                e'8
                \f
                - \tenuto
                ~
                e'2
                r4.
                e'8
                \f
                - \tenuto
                ~
                e'2
                R1
            }
            \new Staff
            {
                \time 4/4
                R1
                \times 2/3 {
                    f'4
                    \pp
                    - \staccato
                    r2
                }
                r2
                \times 2/3 {
                    f'4
                    \pp
                    - \staccato
                    r2
                }
                r2
                \times 2/3 {
                    f'4
                    \pp
                    - \staccato
                    r4
                    <d' ef'>4
                    \f
                    - \accent
                    ~
                }
                <d' ef'>2
                \times 2/3 {
                    f'4
                    \pp
                    - \staccato
                    r4
                    <d' ef'>4
                    \f
                    - \accent
                    ~
                }
                <d' ef'>2
            }
        >>

        .. figure:: ../_images/image-Adrifter-19.png

    .. tip::

        The functions ``auxjad.remove_repeated_dynamics()`` and
        ``auxjad.reposition_clefs()`` can be used to clean the output and
        remove repeated dynamics and unnecessary clef changes.

    ..  warning::

        Do note that elements that span multiple notes (such as hairpins,
        ottava indicators, manual beams, etc.) can become problematic when
        notes containing them are split into two. As a rule of thumb, it is
        always better to attach those to the music after the drifting process
        has ended.

    Example:
        It is possible to use this class with containers of different lengths
        and time signatures, although this feature is not fully implemented and
        should be considered experimental. LilyPond must be set up to allow
        different simultaneous time signatures, and
        ``auxjad.sync_containers()`` can be used to add rests to the end of the
        shorter staff.

        >>> container_out = abjad.Container(r"\time 3/4 c'4 d'4 e'4")
        >>> container_in = abjad.Container(r"\time 4/4 g'2 a'2")
        >>> adrifter = auxjad.Adrifter(container_out,
        ...                            container_in,
        ...                            fade_in_first=True,
        ...                            fade_out_last=True,
        ...                            weighted_duration=True,
        ...                            )
        >>> staff_a, staff_b = adrifter.output_all()
        >>> auxjad.sync_containers(staff_a, staff_b)
        >>> score = abjad.Score([staff_a, staff_b])
        >>> lilypond_file = abjad.LilyPondFile.new()
        >>> score_block = abjad.Block(name='score')
        >>> layout_block = abjad.Block(name='layout')
        >>> score_block.items.append(score)
        >>> score_block.items.append(layout_block)
        >>> lilypond_file.items.append(score_block)
        >>> layout_block.items.append(
        ...     r'''\context {
        ...         \Score
        ...         \remove "Timing_translator"
        ...         \remove "Default_bar_line_engraver"
        ...     }
        ...     \context {
        ...         \Staff
        ...         \consists "Timing_translator"
        ...         \consists "Default_bar_line_engraver"
        ...     }''')
        >>> abjad.f(score)
        \version "2.19.82"  %! abjad.LilyPondFile._get_format_pieces()
        \language "english" %! abjad.LilyPondFile._get_format_pieces()
        \header { %! abjad.LilyPondFile._get_formatted_blocks()
            tagline = ##f
        } %! abjad.LilyPondFile._get_formatted_blocks()
        \layout {}
        \paper {}
        \score { %! abjad.LilyPondFile._get_formatted_blocks()
            \new Score
            <<
                \new Staff
                {
                    \time 3/4
                    c'4
                    d'4
                    e'4
                    c'4
                    d'4
                    e'4
                    c'4
                    r4
                    e'4
                    c'4
                    r2
                    c'4
                    r2
                    R1 * 3/4
                    R1 * 3/4
                    R1 * 3/4
                }
                \new Staff
                {
                    \time 4/4
                    R1
                    r2
                    a'2
                    r2
                    a'2
                    r2
                    a'2
                    g'2
                    a'2
                    g'2
                    a'2
                }
            >>
            \layout {
                \context {
                    \Score
                    \remove "Timing_translator"
                    \remove "Default_bar_line_engraver"
                }
                \context {
                    \Staff
                    \consists "Timing_translator"
                    \consists "Default_bar_line_engraver"
                }
            }
        } %! abjad.LilyPondFile._get_formatted_blocks()

        .. figure:: ../_images/image-Adrifter-20.png
    """

    ### CLASS VARIABLES ###

    __slots__ = ('_contents_out',
                 '_contents_in',
                 '_fader_in',
                 '_fader_out',
                 '_faders',
                 '_weights',
                 '_weighted_duration',
                 '_is_first_window',
                 '_is_first_process',
                 '_current_window',
                 '_repetition_chance',
                 '_initial_repetitions',
                 '_final_repetitions',
                 '_initial_repetitions_counter',
                 '_final_repetitions_counter',
                 '_disable_rewrite_meter',
                 '_omit_time_signatures',
                 '_use_multimeasure_rests',
                 '_boundary_depth',
                 '_maximum_dot_count',
                 '_rewrite_tuplets',
                 '_fade_in_first',
                 '_fade_out_last',
                 )

    ### INITIALISER ###

    def __init__(self,
                 contents_out: abjad.Container,
                 contents_in: abjad.Container,
                 *,
                 fade_in_first: bool = False,
                 fade_out_last: bool = False,
                 initial_repetitions: int = 1,
                 final_repetitions: int = 1,
                 repetition_chance: float = 0.0,
                 weighted_duration: bool = False,
                 disable_rewrite_meter: bool = False,
                 omit_time_signatures: bool = False,
                 use_multimeasure_rests: bool = True,
                 boundary_depth: Optional[int] = None,
                 maximum_dot_count: Optional[int] = None,
                 rewrite_tuplets: bool = True,
                 ):
        r'Initialises self.'
        if not isinstance(contents_out, abjad.Container):
            raise TypeError("'contents_out' must be 'abjad.Container' or "
                            "child class")
        if not isinstance(contents_in, abjad.Container):
            raise TypeError("'contents_in' must be 'abjad.Container' or "
                            "child class")
        self._contents_out = contents_out
        self._contents_in = contents_in
        self._fader_out = Fader(self._contents_out, fader_type='out')
        self._fader_in = Fader(self._contents_in, fader_type='in')
        self._faders = (self._fader_in, self._fader_out)
        self._is_first_window = True
        self._is_first_process = True
        self._initial_repetitions_counter = 0
        self._final_repetitions_counter = 0
        self.fade_in_first = fade_in_first
        self.fade_out_last = fade_out_last
        self.disable_rewrite_meter = disable_rewrite_meter
        self.omit_time_signatures = omit_time_signatures
        self.use_multimeasure_rests = use_multimeasure_rests
        self.boundary_depth = boundary_depth
        self.maximum_dot_count = maximum_dot_count
        self.rewrite_tuplets = rewrite_tuplets
        self.initial_repetitions = initial_repetitions
        self.final_repetitions = final_repetitions
        self.repetition_chance = repetition_chance
        self.weighted_duration = weighted_duration

    ### SPECIAL METHODS ###

    def __repr__(self) -> str:
        r'Returns interpret representation of  both contents.'
        return format(self._fader_out) + '\n' + format(self._fader_in)

    def __len__(self) -> int:
        r'Returns the number of pitched logical ties in both contents.'
        return len(self._fader_in) + len(self._fader_out)

    def __call__(self) -> tuple:
        r'Calls the drifting process, returning a `tuple` of ``abjad.Staff``.'
        self._adrift_process()
        self._current_window = (self._fader_out.current_window,
                                self._fader_in.current_window)
        return self.current_window

    def __next__(self) -> tuple:
        r"""Calls the drifting process for one iteration, returning a `tuple`
        of ``abjad.Staff``.
        """
        try:
            return self.__call__()
        except RuntimeError:
            raise StopIteration

    def __iter__(self):
        r'Returns an iterator, allowing instances to be used as iterators.'
        return self

    ### PUBLIC METHODS ###

    def output_all(self) -> abjad.Selection:
        r"""Goes through the whole fading process and outputs a single
        ``abjad.Selection``.
        """
        self.reset()
        output_staff_a = abjad.Staff()
        output_staff_b = abjad.Staff()
        while True:
            try:
                result_a, result_b = self.__call__()
                output_staff_a.extend(result_a)
                output_staff_b.extend(result_b)
            except RuntimeError:
                break
        remove_repeated_time_signatures(output_staff_a)
        remove_repeated_time_signatures(output_staff_b)
        return (output_staff_a, output_staff_b)

    def output_n(self,
                 n: int,
                 ) -> abjad.Selection:
        r"""Goes through ``n`` iterations of the fading process and outputs a
        single ``abjad.Selection``.
        """
        if not isinstance(n, int):
            raise TypeError("first positional argument must be 'int'")
        if n < 1:
            raise ValueError("first positional argument must be a positive "
                             "'int'")
        self.reset()
        output_staff_a = abjad.Staff()
        output_staff_b = abjad.Staff()
        for _ in range(n):
            try:
                result_a, result_b = self.__call__()
                output_staff_a.extend(result_a)
                output_staff_b.extend(result_b)
            except RuntimeError:
                break
        remove_repeated_time_signatures(output_staff_a)
        remove_repeated_time_signatures(output_staff_b)
        return (output_staff_a, output_staff_b)

    def reset(self):
        r'Resets to the initial state.'
        self._is_first_window = True
        self._is_first_process = True
        self._initial_repetitions_counter = 0
        self._final_repetitions_counter = 0
        self._fader_in.reset_mask()
        self._fader_out.reset_mask()

    ### PRIVATE METHODS ###

    def _adrift_process(self):
        r'Processes both faders according to the adrift process.'
        if self._is_first_window:
            self._fader_out()
            self._fader_in()
            self._initial_repetitions_counter += 1
            self._is_first_window = False
        elif self._initial_repetitions_counter < self._initial_repetitions:
            self._initial_repetitions_counter += 1
        elif self._done:
            if self._final_repetitions_counter < self._final_repetitions - 1:
                self._final_repetitions_counter += 1
            else:
                raise RuntimeError("both faders have been exhausted")
        else:
            if (self._is_first_process or self._repetition_chance == 0.0
                    or random.random() > self._repetition_chance):
                if self._fade_in_first and self._is_first_process:
                    self._fader_in()
                    self._is_first_process = False
                elif (self._fade_out_last and sum(self._fader_out._mask) == 1
                        and not self._fader_in._done):
                    self._fader_in()
                else:
                    try:
                        random_fader = random.choices(self._faders,
                                                      weights=self._weights,
                                                      )[0]
                        random_fader()
                    except RuntimeError:
                        other_fader = self._faders[0]
                        if other_fader is random_fader:
                            other_fader = self._faders[1]
                        other_fader()
                    finally:
                        self._is_first_process = False

    ### PUBLIC PROPERTIES ###

    @property
    def contents_out(self) -> abjad.Container:
        r'The ``abjad.Container`` to be faded out.'
        return copy.deepcopy(self._contents_out)

    @contents_out.setter
    def contents_out(self,
                     contents_out: abjad.Container,
                     ):
        if not isinstance(contents_out, abjad.Container):
            raise TypeError("'contents_out' must be 'abjad.Container' or "
                            "child class")
        self._contents_out = copy.deepcopy(contents_out)
        self._fader_out.contents = self._contents_out
        self.reset()

    @property
    def contents_in(self) -> abjad.Container:
        r'The ``abjad.Container`` to be faded in.'
        return copy.deepcopy(self._contents_in)

    @contents_in.setter
    def contents_in(self,
                    contents_in: abjad.Container,
                    ):
        if not isinstance(contents_in, abjad.Container):
            raise TypeError("'contents_in' must be 'abjad.Container' or "
                            "child class")
        self._contents_in = copy.deepcopy(contents_in)
        self._fader_in.contents = self._contents_in
        self.reset()

    @property
    def current_window(self) -> Union[tuple, None]:
        r"""Read-only property, returns the result of the last operation as a
        `tuple` of ``abjad.Staff``.
        """
        return copy.deepcopy(self._current_window)

    @property
    def weighted_duration(self) -> bool:
        r"""Weights the choice of fader according to its number of logical ties
        and total duration.
        """
        return self._weighted_duration

    @weighted_duration.setter
    def weighted_duration(self,
                          weighted_duration: bool,
                          ):
        if not isinstance(weighted_duration, bool):
            raise TypeError("'weighted_duration' must be 'bool'")
        self._weighted_duration = weighted_duration
        if self._weighted_duration:
            self._weights = []
            for container in [self._contents_in, self._contents_out]:
                notes = abjad.select(container).logical_ties(pitched=True)
                duration = abjad.inspect(container).duration()
                value = len(notes) * float(duration)
                self._weights.append(value)
        else:
            self._weights = [1.0, 1.0]

    @property
    def repetition_chance(self) -> float:
        r'The chance of the head not moving, thus repeating the output.'
        return self._repetition_chance

    @repetition_chance.setter
    def repetition_chance(self,
                          repetition_chance: float,
                          ):
        if not isinstance(repetition_chance, float):
            raise TypeError("'repetition_chance' must be 'float'")
        if repetition_chance < 0.0 or repetition_chance > 1.0:
            raise ValueError("'repetition_chance' must be between 0.0 and 1.0")
        self._repetition_chance = repetition_chance

    @property
    def initial_repetitions(self) -> int:
        r"""The number of times the initial containers are repeated before the
        drifting process starts.
        """
        return self._initial_repetitions

    @initial_repetitions.setter
    def initial_repetitions(self,
                            initial_repetitions: int,
                            ):
        if not isinstance(initial_repetitions, int):
            raise TypeError("'initial_repetitions' must be 'int'")
        if initial_repetitions < 1:
            raise ValueError("'initial_repetitions' must be greater than zero")
        self._initial_repetitions = initial_repetitions

    @property
    def final_repetitions(self) -> int:
        r"""The number of times the final containers are repeated after the
        drifting process ends.
        """
        return self._final_repetitions

    @final_repetitions.setter
    def final_repetitions(self,
                          final_repetitions: int,
                          ):
        if not isinstance(final_repetitions, int):
            raise TypeError("'final_repetitions' must be 'int'")
        if final_repetitions < 1:
            raise ValueError("'final_repetitions' must be greater than zero")
        self._final_repetitions = final_repetitions

    @property
    def initial_repetitions_counter(self) -> int:
        r'Read-only property, returns the counter of ``initial_repetitions``.'
        return self._initial_repetitions_counter

    @property
    def final_repetitions_counter(self) -> int:
        r'Read-only property, returns the counter of ``final_repetitions``.'
        return self._final_repetitions_counter

    @property
    def fade_in_first(self) -> bool:
        r"""When ``True``, the first note of the fade in content will be added
        before a note from the fade out content is removed.
        """
        return self._fade_in_first

    @fade_in_first.setter
    def fade_in_first(self,
                      fade_in_first: bool,
                      ):
        if not isinstance(fade_in_first, bool):
            raise TypeError("'fade_in_first' must be 'bool'")
        self._fade_in_first = fade_in_first

    @property
    def fade_out_last(self) -> bool:
        r"""When ``True``, the last note of the fade out content will be
        removed only after the full fade in content is added.
        """
        return self._fade_out_last

    @fade_out_last.setter
    def fade_out_last(self,
                      fade_out_last: bool,
                      ):
        if not isinstance(fade_out_last, bool):
            raise TypeError("'fade_out_last' must be 'bool'")
        self._fade_out_last = fade_out_last

    @property
    def disable_rewrite_meter(self) -> bool:
        r"""When ``True``, the durations of the notes in the output will not be
        rewritten by the ``rewrite_meter`` mutation.
        """
        return self._disable_rewrite_meter

    @disable_rewrite_meter.setter
    def disable_rewrite_meter(self,
                              disable_rewrite_meter: bool,
                              ):
        if not isinstance(disable_rewrite_meter, bool):
            raise TypeError("'disable_rewrite_meter' must be 'bool'")
        self._disable_rewrite_meter = disable_rewrite_meter
        self._fader_in.disable_rewrite_meter = disable_rewrite_meter
        self._fader_out.disable_rewrite_meter = disable_rewrite_meter

    @property
    def omit_time_signatures(self) -> bool:
        r'When ``True``, all time signatures will be omitted from the output.'
        return self._omit_time_signatures

    @omit_time_signatures.setter
    def omit_time_signatures(self,
                             omit_time_signatures: bool,
                             ):
        if not isinstance(omit_time_signatures, bool):
            raise TypeError("'omit_time_signatures' must be 'bool'")
        self._omit_time_signatures = omit_time_signatures
        self._fader_in.omit_time_signatures = omit_time_signatures
        self._fader_out.omit_time_signatures = omit_time_signatures

    @property
    def use_multimeasure_rests(self) -> bool:
        r'When ``True``, multimeasure rests will be used for silent measures.'
        return self._use_multimeasure_rests

    @use_multimeasure_rests.setter
    def use_multimeasure_rests(self,
                               use_multimeasure_rests: bool,
                               ):
        if not isinstance(use_multimeasure_rests, bool):
            raise TypeError("'use_multimeasure_rests' must be 'bool'")
        self._use_multimeasure_rests = use_multimeasure_rests
        self._fader_in.use_multimeasure_rests = use_multimeasure_rests
        self._fader_out.use_multimeasure_rests = use_multimeasure_rests

    @property
    def boundary_depth(self) -> Union[int, None]:
        r"Sets the argument ``boundary_depth`` of abjad's ``rewrite_meter()``."
        return self._boundary_depth

    @boundary_depth.setter
    def boundary_depth(self,
                       boundary_depth: Optional[int],
                       ):
        if boundary_depth is not None:
            if not isinstance(boundary_depth, int):
                raise TypeError("'boundary_depth' must be 'int'")
        self._boundary_depth = boundary_depth
        self._fader_in.boundary_depth = boundary_depth
        self._fader_out.boundary_depth = boundary_depth

    @property
    def maximum_dot_count(self) -> Union[int, None]:
        r"""Sets the argument ``maximum_dot_count`` of abjad's
        ``rewrite_meter()``.
        """
        return self._maximum_dot_count

    @maximum_dot_count.setter
    def maximum_dot_count(self,
                          maximum_dot_count: Optional[int],
                          ):
        if maximum_dot_count is not None:
            if not isinstance(maximum_dot_count, int):
                raise TypeError("'maximum_dot_count' must be 'int'")
        self._maximum_dot_count = maximum_dot_count
        self._fader_in.maximum_dot_count = maximum_dot_count
        self._fader_out.maximum_dot_count = maximum_dot_count

    @property
    def rewrite_tuplets(self) -> bool:
        r"""Sets the argument ``rewrite_tuplets`` of abjad's
        ``rewrite_meter()``.
        """
        return self._rewrite_tuplets

    @rewrite_tuplets.setter
    def rewrite_tuplets(self,
                        rewrite_tuplets: bool,
                        ):
        if not isinstance(rewrite_tuplets, bool):
            raise TypeError("'rewrite_tuplets' must be 'bool'")
        self._rewrite_tuplets = rewrite_tuplets
        self._fader_in.rewrite_tuplets = rewrite_tuplets
        self._fader_out.rewrite_tuplets = rewrite_tuplets

    ### PRIVATE PROPERTIES ###

    @property
    def _done(self) -> bool:
        r"""Boolean indicating whether the process is done, which is when both
        faders are done.
        """
        return all(fader._done for fader in self._faders)
