import { ByteArrayInput, ByteArrayOutput, Option } from "@opendaw/lib-std";
import { Pointers } from "@opendaw/studio-enums";
import { NoteEventBox } from "@opendaw/studio-boxes";
import { NoteEventBoxAdapter } from "@opendaw/studio-adapters";
import { ClipboardUtils } from "../ClipboardUtils";
export var NotesClipboard;
(function (NotesClipboard) {
    const encodeMetadata = (min, max) => {
        const output = ByteArrayOutput.create();
        output.writeFloat(min);
        output.writeFloat(max);
        return output.toArrayBuffer();
    };
    const decodeMetadata = (buffer) => {
        const input = new ByteArrayInput(buffer);
        return { min: input.readFloat(), max: input.readFloat() };
    };
    NotesClipboard.createHandler = ({ getEnabled, getPosition, setPosition, editing, selection, targetAddress, boxGraph, boxAdapters }) => {
        const copyNotes = () => {
            const selected = selection.selected();
            if (selected.length === 0) {
                return Option.None;
            }
            const min = selected.reduce((acc, { position }) => Math.min(acc, position), Number.POSITIVE_INFINITY);
            const max = selected.reduce((acc, { complete }) => Math.max(acc, complete), Number.NEGATIVE_INFINITY);
            const eventBoxes = selected.map(adapter => adapter.box);
            const dependencies = eventBoxes.flatMap(box => Array.from(boxGraph.dependenciesOf(box, {
                alwaysFollowMandatory: true,
                excludeBox: (dep) => dep.ephemeral
            }).boxes));
            const allBoxes = [...eventBoxes, ...dependencies];
            const data = ClipboardUtils.serializeBoxes(allBoxes, encodeMetadata(min, max));
            setPosition(max);
            return Option.wrap({ type: "notes", data });
        };
        return {
            canCopy: () => getEnabled() && selection.nonEmpty(),
            canCut: () => getEnabled() && selection.nonEmpty(),
            canPaste: (entry) => getEnabled() && entry.type === "notes",
            copy: copyNotes,
            cut: () => {
                const result = copyNotes();
                result.ifSome(() => editing.modify(() => selection.selected().forEach(adapter => adapter.box.delete())));
                return result;
            },
            paste: (entry) => {
                if (entry.type !== "notes" || !getEnabled()) {
                    return;
                }
                const position = getPosition();
                const { min, max } = decodeMetadata(ClipboardUtils.extractMetadata(entry.data));
                const positionOffset = Math.max(0, position) - min;
                editing.modify(() => {
                    selection.deselectAll();
                    const boxes = ClipboardUtils.deserializeBoxes(entry.data, boxGraph, {
                        mapPointer: pointer => pointer.pointerType === Pointers.NoteEvents
                            ? Option.wrap(targetAddress)
                            : Option.None,
                        modifyBox: box => {
                            if (box instanceof NoteEventBox) {
                                box.position.setValue(box.position.getValue() + positionOffset);
                            }
                        }
                    });
                    const noteEventBoxes = boxes.filter((box) => box instanceof NoteEventBox);
                    selection.select(...noteEventBoxes.map(box => boxAdapters.adapterFor(box, NoteEventBoxAdapter)));
                    setPosition(Math.max(0, position) + (max - min));
                });
            }
        };
    };
})(NotesClipboard || (NotesClipboard = {}));
