"""
Tools to "play notes for the editor clef", which may be thought of as "executing editor commands".

NOTE: in the below, we often connect notes together "manually", i.e. using NoteSlur(..., previous_hash). As an
alternative, we could consider `nouts_for_notes`.
"""

from s_address import node_for_s_address, s_dfs

from dsn.s_expr.legato import NoteSlur, NoteCapo

from dsn.s_expr.utils import (
    bubble_history_up,
    calc_possibility,
    insert_text_at,
    insert_node_at,
    replace_text_at,
    weave_disjoint_replaces,
)

from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode

from dsn.s_expr.structure import TreeNode

from dsn.editor.clef import (
    CursorChild,
    CursorDFS,
    CursorParent,
    CursorSet,
    EDelete,
    EncloseWithParent,
    InsertNodeChild,
    InsertNodeSibbling,
    MoveSelectionChild,
    MoveSelectionSibbling,
    LeaveChildrenBehind,
    SwapSibbling,
    TextInsert,
    TextReplace,
)


def edit_note_play(structure, edit_note):
    # :: EditStructure, EditNote => (new) s_cursor, posacts, error
    def an_error():
        return structure.s_cursor, [], True

    if isinstance(edit_note, TextInsert):
        posacts = insert_text_at(structure.tree, edit_note.parent_s_address, edit_note.index, edit_note.text)
        new_s_cursor = edit_note.parent_s_address + [edit_note.index]
        return new_s_cursor, posacts, False

    if isinstance(edit_note, TextReplace):
        posacts = replace_text_at(structure.tree, edit_note.s_address, edit_note.text)
        return edit_note.s_address, posacts, False

    if isinstance(edit_note, InsertNodeSibbling):
        if structure.s_cursor == []:
            return an_error()  # adding sibblings to the root is not possible (it would lead to a forest)

        # There is no need to check that the new index is a valid one. (Assuming: the cursor is valid, and direction is
        # in the range [0, 1]; such assumptions fit with the general idea of "we only check that the user's command can
        # be executed at this point, we do not check for arbitrary programming errors here). The proof flows directly
        # from the idea that, for lists of length n, insertions at [0, n] are valid (insertion at n being an append).
        index = structure.s_cursor[-1] + edit_note.direction

        posacts = insert_node_at(structure.tree, structure.s_cursor[:-1], index)
        new_s_cursor = structure.s_cursor[:-1] + [index]

        return new_s_cursor, posacts, False

    if isinstance(edit_note, InsertNodeChild):
        cursor_node = node_for_s_address(structure.tree, structure.s_cursor)
        if not isinstance(cursor_node, TreeNode):
            # for now... we just silently ignore the user's request when they ask to add a child node to a non-node
            return an_error()

        index = len(cursor_node.children)
        posacts = insert_node_at(structure.tree, structure.s_cursor, index)
        new_s_cursor = structure.s_cursor + [index]

        return new_s_cursor, posacts, False

    if isinstance(edit_note, EDelete):
        if structure.s_cursor == []:
            # silently ignored ('delete root' is not defined, because the root is assumed to exist.)
            return an_error()

        delete_from = structure.s_cursor[:-1]
        delete_at_index = structure.s_cursor[-1]
        delete_from_hash = node_for_s_address(structure.tree, delete_from).metadata.nout_hash

        p, h = calc_possibility(NoteSlur(Delete(delete_at_index), delete_from_hash))

        if delete_at_index == len(node_for_s_address(structure.tree, delete_from).children) - 1:
            # deletion makes cursor pos invalid: up to parent (alternative: sibbling-up first, until no more sibblings)
            new_s_cursor = delete_from
        else:
            new_s_cursor = structure.s_cursor  # "stay in place (although new contents slide into the cursor position)

        posacts = [p] + bubble_history_up(h, structure.tree, delete_from)

        return new_s_cursor, posacts, False

    if isinstance(edit_note, SwapSibbling):
        if structure.s_cursor == []:
            return an_error()  # root has no sibblings

        parent = node_for_s_address(structure.tree, structure.s_cursor[:-1])
        index = structure.s_cursor[-1] + edit_note.direction

        if not (0 <= index <= len(parent.children) - 1):
            return an_error()

        # For now, SwapSibbling is simply implemented as a "delete and insert"; if (or when) we'll introduce "Move" into
        # the Clef, we should note the move here.

        parent_s_address = structure.s_cursor[:-1]
        delete_at_index = structure.s_cursor[-1]
        delete_from_hash = node_for_s_address(structure.tree, parent_s_address).metadata.nout_hash
        reinsert_later_hash = node_for_s_address(structure.tree, structure.s_cursor).metadata.nout_hash

        p0, hash_after_deletion = calc_possibility(NoteSlur(Delete(delete_at_index), delete_from_hash))
        p1, hash_after_insertion = calc_possibility(NoteSlur(Insert(index, reinsert_later_hash), hash_after_deletion))

        new_cursor = structure.s_cursor[:-1] + [index]
        posacts = [p0, p1] + bubble_history_up(hash_after_insertion, structure.tree, parent_s_address)
        return new_cursor, posacts, False

    if isinstance(edit_note, MoveSelectionChild):
        cursor_node = node_for_s_address(structure.tree, structure.s_cursor)

        if not hasattr(cursor_node, 'children'):
            return an_error()  # The target must be a node to be able to add as a child

        return do_move(structure, edit_note, structure.s_cursor, len(cursor_node.children))

    if isinstance(edit_note, MoveSelectionSibbling):
        if len(structure.s_cursor) == 0:
            return an_error()  # there is no sibbling of the root node

        # edit_note.direction points to a valid insertion point for the same reasons detailed in the comment on
        # InsertNodeSibbling
        return do_move(structure, edit_note, structure.s_cursor[:-1], structure.s_cursor[-1] + edit_note.direction)

    if isinstance(edit_note, LeaveChildrenBehind):
        cursor_node = node_for_s_address(structure.tree, structure.s_cursor)
        if not hasattr(cursor_node, 'children'):
            return an_error()  # Leave _children_ behind presupposes the existance of children

        if structure.s_cursor == []:
            return an_error()  # Root cannot die

        # For now, LeaveChildrenBehind is simply implemented as a "delete and insert"; if (or when) we'll introduce
        # "Move" into the Clef, we should note the move here.

        parent_s_address = structure.s_cursor[:-1]
        delete_at_index = structure.s_cursor[-1]
        delete_from_hash = node_for_s_address(structure.tree, parent_s_address).metadata.nout_hash

        p, hash_ = calc_possibility(NoteSlur(Delete(delete_at_index), delete_from_hash))
        posacts = [p]

        removed_node = node_for_s_address(structure.tree, structure.s_cursor)
        for i, child in enumerate(removed_node.children):
            p, hash_ = calc_possibility(NoteSlur(Insert(structure.s_cursor[-1] + i, child.metadata.nout_hash), hash_))
            posacts.append(p)

        # In general, leaving the cursor at the same s_address will be great: post-deletion you'll be in the right spot
        new_cursor = structure.s_cursor
        if len(removed_node.children) == 0:
            # ... however, if there are no children to leave behind... this "right spot" may be illegal
            parent_node = node_for_s_address(structure.tree, parent_s_address)
            if len(parent_node.children) == 1:
                # if the deleted node was the only node: fall back to the parent
                new_cursor = parent_s_address
            else:
                # otherwise, make sure to stay in bounds.
                new_cursor[len(new_cursor) - 1] = min(
                    len(parent_node.children) - 1 - 1,  # len - 1 idiom; -1 for deletion.
                    new_cursor[len(new_cursor) - 1])

        posacts += bubble_history_up(hash_, structure.tree, parent_s_address)

        return new_cursor, posacts, False

    if isinstance(edit_note, EncloseWithParent):
        cursor_node = node_for_s_address(structure.tree, structure.s_cursor)

        if structure.s_cursor == []:
            # I am not sure about this one yet: should we have the option to create a new root? I don't see any direct
            # objections (by which I mean: it's possible in terms of the math), but I still have a sense that it may
            # create some asymmetries. For now I'm disallowing it; we'll see whether a use case arises.
            return an_error()

        # For now, EncloseWithParent is simply implemented as a "replace with the parent"; if (or when) we'll introduce
        # "Move" (in particular: the MoveReplace) into the Clef, we should note the move here.

        parent_s_address = structure.s_cursor[:-1]
        replace_at_index = structure.s_cursor[-1]
        replace_on_hash = node_for_s_address(structure.tree, parent_s_address).metadata.nout_hash

        reinsert_later_hash = node_for_s_address(structure.tree, structure.s_cursor).metadata.nout_hash

        p_capo, hash_capo = calc_possibility(NoteCapo())
        p_create, hash_create = calc_possibility(NoteSlur(BecomeNode(), hash_capo))

        p_enclosure, hash_enclosure = calc_possibility(NoteSlur(Insert(0, reinsert_later_hash), hash_create))

        p_replace, hash_replace = calc_possibility(
            NoteSlur(Replace(replace_at_index, hash_enclosure), replace_on_hash))

        posacts = [p_capo, p_create, p_enclosure, p_replace] + bubble_history_up(
            hash_replace, structure.tree, parent_s_address)

        # We jump the cursor to the newly enclosed location:
        new_cursor = structure.s_cursor + [0]

        return new_cursor, posacts, False

    def move_cursor(new_cursor):
        return new_cursor, [], False

    if isinstance(edit_note, CursorDFS):
        dfs = s_dfs(structure.tree, [])
        dfs_index = dfs.index(structure.s_cursor) + edit_note.direction
        if not (0 <= dfs_index <= len(dfs) - 1):
            return an_error()
        return move_cursor(dfs[dfs_index])

    """At some point I had "regular sibbling" (as opposed to DFS sibbling) in the edit_clef. It looks like this:

        if structure.s_cursor == []:
            return an_error()  # root has no sibblings

        parent = node_for_s_address(structure.tree, s_cursor[:-1])
        index = s_cursor[-1] + edit_node.direction

        if not (0 <= index <= len(parent.children) - 1):
            return an_error()
        return move_cursor(s_cursor[:-1] + [index])
    """
    if isinstance(edit_note, CursorSet):
        return move_cursor(edit_note.s_address)

    if isinstance(edit_note, CursorParent):
        if structure.s_cursor == []:
            return an_error()
        return move_cursor(structure.s_cursor[:-1])

    if isinstance(edit_note, CursorChild):
        cursor_node = node_for_s_address(structure.tree, structure.s_cursor)
        if not hasattr(cursor_node, 'children') or len(cursor_node.children) == 0:
            return an_error()
        return move_cursor(structure.s_cursor + [0])

    raise Exception("Unknown Note")


def do_move(structure, edit_note, target_parent_path, target_index):
    selection_edge_0 = edit_note.selection_edge_0
    selection_edge_1 = edit_note.selection_edge_1

    def an_error():
        return structure.s_cursor, [], True

    if selection_edge_0[:-1] != selection_edge_1[:-1]:
        # i.e. if not same-parent: this is an error. This may very well be too restrictive, but I'd rather move in the
        # direction of "relax constraints later" than in the other directions. One particular reason I'm so restrictive
        # for now: if I ever want to express a note "move" using a target_node, a source node and to indices in the
        # source node, such a single-parent restriction is indeed a necessity.

        # Note that "single parent" implies "same depth", but not vice versa. One possible relaxation is: make the
        # restriction on "same depth" instead.

        # Generally, the paths towards relaxation are to either [a] "be smart about the meaning of the selection's
        # edges", i.e. find the first common ancestor and the relevant children of that ancestor or [b] to not care so
        # much about single-parent.

        return an_error()

    if selection_edge_0 <= (target_parent_path + [target_index])[:len(selection_edge_0)] <= selection_edge_1:
        # If the full target location, truncated to the length of the sources, is (inclusively) in the source's range,
        # you're trying to move to [a descendant of] yourself. This is illegal. Moving something to a child of itself:
        # I simply don't know what it would mean. Moving something to the same location (single source item, target path
        # identical to the source path) could at least be understood to mean the no-op, so it's slightly less
        # meaningless, but here I don't find that enough, so I'm just calling both scenarios error-scenarios.

        # This implies protection against moving the root node around (because everything descends from the root node)
        return an_error()

    source_parent_path = selection_edge_0[:-1]
    source_parent = node_for_s_address(structure.tree, source_parent_path)

    target_parent = node_for_s_address(structure.tree, target_parent_path)

    # For now, the "edit move" operations are simply implemented as a "insert and delete"; if (or when) we'll introduce
    # "Move" into the Clef, we should note the move here.

    posacts = []

    source_index_lo, source_index_hi = sorted([selection_edge_0[-1], selection_edge_1[-1]])

    hash_ = target_parent.metadata.nout_hash

    for target_offset, source_index in enumerate(range(source_index_lo, source_index_hi + 1)):  # edge-inclusive range
        insert_hash = node_for_s_address(structure.tree, source_parent_path + [source_index]).metadata.nout_hash
        p, hash_ = calc_possibility(NoteSlur(Insert(target_index + target_offset, insert_hash), hash_))

        posacts.append(p)

    weave_correction = 0
    cursor_correction = 0

    # TODO this part is still broken:

    # Not only if the parents are exactly the same, but also if one parent is a prefix of the other (said differently:
    # the longest_common_prefix of both parents matches one of them).

    # In that case, we need to somehow connect the parents....

    # (For the case of "parents match exactly", I did this using the idea "just don't reset hash_"... which works,
    # because it allows you to continue operating on the the same "future". But in the case of shared prefix, this won't
    # work.
    if source_parent_path != target_parent_path:
        wdr_hash = hash_
        hash_ = source_parent.metadata.nout_hash

    else:
        if target_index < source_index_lo:
            # We insert before we delete. If we do this on the same parent, and the insertions happen at lower indices
            # than the deletions, they will affect the locations where the deletions must take place, by precisely the
            # number of insertions that happened. (If we reverse the order of operations, we have the opposite problem)

            # The reason we have this problem at all, is because we implement something that is atomic from the user's
            # point of view in a non-atomic way in the clef. The problem may auto-disappear if we add "Move" to the
            # clef.

            # Another way we could handle the problem is once we have some tools to "realinearize while preserving
            # meaning". I.e. we have deletions, we have insertions: at one point (e.g. once we build the cooperative
            # editor) we should be able to express "weave those together, rewriting indices as required".

            # In the if-statement above, we could pick either lo/hi for the comparison; source_index_lo and
            # source_index_hi will never straddle target_index, because of the child-of-yourself checks at the top.

            weave_correction = source_index_hi - source_index_lo + 1
        else:
            cursor_correction = source_index_hi - source_index_lo + 1

        # we do _not_ fetch hash_ here, the idea being: it's the hash we just created.
        # nor do we bubble up (yet); we can do a single bubble-up

    for source_index in range(source_index_lo, source_index_hi + 1):  # edge-inclusive range
        # Note: we just Delete n times at the "lo" index (everything shifting to the left after each deletion)
        p, hash_ = calc_possibility(NoteSlur(Delete(source_index_lo + weave_correction), hash_))
        posacts.append(p)

    if source_parent_path != target_parent_path:
        posacts = posacts + weave_disjoint_replaces(
            structure.tree,
            target_parent_path, wdr_hash,
            source_parent_path, hash_)

    else:
        posacts = posacts + bubble_history_up(hash_, structure.tree, source_parent_path)

    # The current solution for "where to put the cursor after the move" is "at the end". This "seems intuitive" (but
    # that may just be habituation). In any case, it's wat e.g. LibreOffice does when cut/pasting. (However, for a
    # mouse-drag initiated move in LibreOffice, the selection is preserved).

    # As it stands: the selection disappears automatically, because it points at a no-longer existing location.  If we
    # want to make the selection appear at the target-location, we need to change the interface of edit_note_play to
    # include the resulting selection.

    new_cursor = target_parent_path + [target_index + target_offset - cursor_correction]

    return new_cursor, posacts, False

[((293, 20, 293, 74), 's_address.node_for_s_address', 'node_for_s_address', ({(293, 39, 293, 53): 'structure.tree', (293, 55, 293, 73): 'source_parent_path'}, {}), '(structure.tree, source_parent_path)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((295, 20, 295, 74), 's_address.node_for_s_address', 'node_for_s_address', ({(295, 39, 295, 53): 'structure.tree', (295, 55, 295, 73): 'target_parent_path'}, {}), '(structure.tree, target_parent_path)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((49, 18, 49, 109), 'dsn.s_expr.utils.insert_text_at', 'insert_text_at', ({(49, 33, 49, 47): 'structure.tree', (49, 49, 49, 75): 'edit_note.parent_s_address', (49, 77, 49, 92): 'edit_note.index', (49, 94, 49, 108): 'edit_note.text'}, {}), '(structure.tree, edit_note.parent_s_address, edit_note.index,\n    edit_note.text)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((54, 18, 54, 86), 'dsn.s_expr.utils.replace_text_at', 'replace_text_at', ({(54, 34, 54, 48): 'structure.tree', (54, 50, 54, 69): 'edit_note.s_address', (54, 71, 54, 85): 'edit_note.text'}, {}), '(structure.tree, edit_note.s_address, edit_note.text)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((67, 18, 67, 80), 'dsn.s_expr.utils.insert_node_at', 'insert_node_at', ({(67, 33, 67, 47): 'structure.tree', (67, 49, 67, 72): 'structure.s_cursor[:-1]', (67, 74, 67, 79): 'index'}, {}), '(structure.tree, structure.s_cursor[:-1], index)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((73, 22, 73, 76), 's_address.node_for_s_address', 'node_for_s_address', ({(73, 41, 73, 55): 'structure.tree', (73, 57, 73, 75): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((79, 18, 79, 75), 'dsn.s_expr.utils.insert_node_at', 'insert_node_at', ({(79, 33, 79, 47): 'structure.tree', (79, 49, 79, 67): 'structure.s_cursor', (79, 69, 79, 74): 'index'}, {}), '(structure.tree, structure.s_cursor, index)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((109, 17, 109, 76), 's_address.node_for_s_address', 'node_for_s_address', ({(109, 36, 109, 50): 'structure.tree', (109, 52, 109, 75): 'structure.s_cursor[:-1]'}, {}), '(structure.tree, structure.s_cursor[:-1])', False, 'from s_address import node_for_s_address, s_dfs\n'), ((131, 22, 131, 76), 's_address.node_for_s_address', 'node_for_s_address', ({(131, 41, 131, 55): 'structure.tree', (131, 57, 131, 75): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((147, 22, 147, 76), 's_address.node_for_s_address', 'node_for_s_address', ({(147, 41, 147, 55): 'structure.tree', (147, 57, 147, 75): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((164, 23, 164, 77), 's_address.node_for_s_address', 'node_for_s_address', ({(164, 42, 164, 56): 'structure.tree', (164, 58, 164, 76): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((183, 19, 183, 77), 'dsn.s_expr.utils.bubble_history_up', 'bubble_history_up', ({(183, 37, 183, 42): 'hash_', (183, 44, 183, 58): 'structure.tree', (183, 60, 183, 76): 'parent_s_address'}, {}), '(hash_, structure.tree, parent_s_address)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((188, 22, 188, 76), 's_address.node_for_s_address', 'node_for_s_address', ({(188, 41, 188, 55): 'structure.tree', (188, 57, 188, 75): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((225, 14, 225, 39), 's_address.s_dfs', 's_dfs', ({(225, 20, 225, 34): 'structure.tree', (225, 36, 225, 38): '[]'}, {}), '(structure.tree, [])', False, 'from s_address import node_for_s_address, s_dfs\n'), ((252, 22, 252, 76), 's_address.node_for_s_address', 'node_for_s_address', ({(252, 41, 252, 55): 'structure.tree', (252, 57, 252, 75): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((101, 24, 101, 73), 'dsn.s_expr.utils.bubble_history_up', 'bubble_history_up', ({(101, 42, 101, 43): 'h', (101, 45, 101, 59): 'structure.tree', (101, 61, 101, 72): 'delete_from'}, {}), '(h, structure.tree, delete_from)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((127, 29, 127, 102), 'dsn.s_expr.utils.bubble_history_up', 'bubble_history_up', ({(127, 47, 127, 67): 'hash_after_insertion', (127, 69, 127, 83): 'structure.tree', (127, 85, 127, 101): 'parent_s_address'}, {}), '(hash_after_insertion, structure.tree, parent_s_address)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((173, 26, 173, 78), 's_address.node_for_s_address', 'node_for_s_address', ({(173, 45, 173, 59): 'structure.tree', (173, 61, 173, 77): 'parent_s_address'}, {}), '(structure.tree, parent_s_address)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((205, 45, 205, 55), 'dsn.s_expr.legato.NoteCapo', 'NoteCapo', ({}, {}), '()', False, 'from dsn.s_expr.legato import NoteSlur, NoteCapo\n'), ((213, 63, 214, 59), 'dsn.s_expr.utils.bubble_history_up', 'bubble_history_up', ({(214, 12, 214, 24): 'hash_replace', (214, 26, 214, 40): 'structure.tree', (214, 42, 214, 58): 'parent_s_address'}, {}), '(hash_replace, structure.tree, parent_s_address)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((359, 28, 362, 38), 'dsn.s_expr.utils.weave_disjoint_replaces', 'weave_disjoint_replaces', ({(360, 12, 360, 26): 'structure.tree', (361, 12, 361, 30): 'target_parent_path', (361, 32, 361, 40): 'wdr_hash', (362, 12, 362, 30): 'source_parent_path', (362, 32, 362, 37): 'hash_'}, {}), '(structure.tree, target_parent_path, wdr_hash,\n    source_parent_path, hash_)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((365, 28, 365, 88), 'dsn.s_expr.utils.bubble_history_up', 'bubble_history_up', ({(365, 46, 365, 51): 'hash_', (365, 53, 365, 67): 'structure.tree', (365, 69, 365, 87): 'source_parent_path'}, {}), '(hash_, structure.tree, source_parent_path)', False, 'from dsn.s_expr.utils import bubble_history_up, calc_possibility, insert_text_at, insert_node_at, replace_text_at, weave_disjoint_replaces\n'), ((91, 27, 91, 74), 's_address.node_for_s_address', 'node_for_s_address', ({(91, 46, 91, 60): 'structure.tree', (91, 62, 91, 73): 'delete_from'}, {}), '(structure.tree, delete_from)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((93, 41, 93, 64), 'dsn.s_expr.clef.Delete', 'Delete', ({(93, 48, 93, 63): 'delete_at_index'}, {}), '(delete_at_index)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((120, 27, 120, 79), 's_address.node_for_s_address', 'node_for_s_address', ({(120, 46, 120, 60): 'structure.tree', (120, 62, 120, 78): 'parent_s_address'}, {}), '(structure.tree, parent_s_address)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((121, 30, 121, 84), 's_address.node_for_s_address', 'node_for_s_address', ({(121, 49, 121, 63): 'structure.tree', (121, 65, 121, 83): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((123, 60, 123, 83), 'dsn.s_expr.clef.Delete', 'Delete', ({(123, 67, 123, 82): 'delete_at_index'}, {}), '(delete_at_index)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((124, 61, 124, 95), 'dsn.s_expr.clef.Insert', 'Insert', ({(124, 68, 124, 73): 'index', (124, 75, 124, 94): 'reinsert_later_hash'}, {}), '(index, reinsert_later_hash)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((159, 27, 159, 79), 's_address.node_for_s_address', 'node_for_s_address', ({(159, 46, 159, 60): 'structure.tree', (159, 62, 159, 78): 'parent_s_address'}, {}), '(structure.tree, parent_s_address)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((161, 45, 161, 68), 'dsn.s_expr.clef.Delete', 'Delete', ({(161, 52, 161, 67): 'delete_at_index'}, {}), '(delete_at_index)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((201, 26, 201, 78), 's_address.node_for_s_address', 'node_for_s_address', ({(201, 45, 201, 59): 'structure.tree', (201, 61, 201, 77): 'parent_s_address'}, {}), '(structure.tree, parent_s_address)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((203, 30, 203, 84), 's_address.node_for_s_address', 'node_for_s_address', ({(203, 49, 203, 63): 'structure.tree', (203, 65, 203, 83): 'structure.s_cursor'}, {}), '(structure.tree, structure.s_cursor)', False, 'from s_address import node_for_s_address, s_dfs\n'), ((206, 58, 206, 70), 'dsn.s_expr.clef.BecomeNode', 'BecomeNode', ({}, {}), '()', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((208, 64, 208, 94), 'dsn.s_expr.clef.Insert', 'Insert', ({(208, 71, 208, 72): '0', (208, 74, 208, 93): 'reinsert_later_hash'}, {}), '(0, reinsert_later_hash)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((211, 21, 211, 62), 'dsn.s_expr.clef.Replace', 'Replace', ({(211, 29, 211, 45): 'replace_at_index', (211, 47, 211, 61): 'hash_enclosure'}, {}), '(replace_at_index, hash_enclosure)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((307, 22, 307, 93), 's_address.node_for_s_address', 'node_for_s_address', ({(307, 41, 307, 55): 'structure.tree', (307, 57, 307, 92): '(source_parent_path + [source_index])'}, {}), '(structure.tree, source_parent_path + [source_index])', False, 'from s_address import node_for_s_address, s_dfs\n'), ((308, 45, 308, 94), 'dsn.s_expr.clef.Insert', 'Insert', ({(308, 52, 308, 80): 'target_index + target_offset', (308, 82, 308, 93): 'insert_hash'}, {}), '(target_index + target_offset, insert_hash)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((355, 45, 355, 87), 'dsn.s_expr.clef.Delete', 'Delete', ({(355, 52, 355, 86): 'source_index_lo + weave_correction'}, {}), '(source_index_lo + weave_correction)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((166, 49, 166, 109), 'dsn.s_expr.clef.Insert', 'Insert', ({(166, 56, 166, 82): 'structure.s_cursor[-1] + i', (166, 84, 166, 108): 'child.metadata.nout_hash'}, {}), '(structure.s_cursor[-1] + i, child.metadata.nout_hash)', False, 'from dsn.s_expr.clef import Delete, Insert, Replace, BecomeNode\n'), ((95, 34, 95, 81), 's_address.node_for_s_address', 'node_for_s_address', ({(95, 53, 95, 67): 'structure.tree', (95, 69, 95, 80): 'delete_from'}, {}), '(structure.tree, delete_from)', False, 'from s_address import node_for_s_address, s_dfs\n')]