import { jsx as _jsx } from "react/jsx-runtime";
import { FlowStepSequence } from "./FlowStepSequence";
import { DataUtilities } from "../../Common/DataUtilities";
import { FlowStepOutputInstructionType } from "./FlowStepOutputInstructions";
import { FlowConstants } from "../CommonUI/FlowConstants";
import { RegisterFlowElements } from "../registerElements";
import { MultiExecutable } from "../../Common/Execution/MultiExecutable";
export class FlowConnection {
    constructor(fromId, toId, isCandidate, fromSequence, fromInstruction, fromStep) {
        this.fromSequence = fromSequence;
        this.fromInstruction = fromInstruction;
        this.fromStep = fromStep;
        this.fromId = fromId;
        this.toId = toId;
        this.isCandidate = isCandidate;
    }
}
export class FlowExtraConnection {
    constructor(fromId, toId, fromStep, fromInstruction) {
        this.fromId = fromId;
        this.toId = toId;
        this.fromStep = fromStep;
        this.fromInstruction = fromInstruction;
    }
}
export class XFlowConfiguration extends MultiExecutable {
    constructor() {
        super();
        this._id = DataUtilities.generateUniqueId();
        this.visibleSteps = [];
        this.favoriteSteps = [];
        // ensure flow steps are registered
        RegisterFlowElements.doRegistration();
        this._sequences = [];
        let initialSeq = new FlowStepSequence(FlowConstants.DEFAULT_PRIMARY_SEQUENCE_COLOR);
        initialSeq.desiredX = FlowConstants.PRIMARY_SEQUENCE_POSITION.x;
        initialSeq.desiredY = FlowConstants.PRIMARY_SEQUENCE_POSITION.y;
        initialSeq._id = FlowConstants.PRIMARY_SEQUENCE;
        this.addSequence(initialSeq);
        this.endSequence = new FlowStepSequence(FlowConstants.DEFAULT_END_SEQUENCE_COLOR);
        this.endSequence._id = FlowConstants.END_SEQUENCE;
        this.endSequence.isCollapsed = true;
        this.endSequence.name = "Flow End";
        this.errorSequence = new FlowStepSequence(FlowConstants.DEFAULT_ERROR_SEQUENCE_COLOR);
        this.errorSequence._id = FlowConstants.ERROR_SEQUENCE;
        this.errorSequence.isCollapsed = true;
        this.errorSequence.name = "Error";
    }
    get sequences() {
        return this._sequences;
    }
    toggleShowDescription(show) {
        for (let s of this._sequences) {
            s.toggleShowDescriptionForSteps(show);
        }
    }
    toggleCollapsed(isCollapsed) {
        for (let s of this._sequences) {
            s.isCollapsed = isCollapsed;
        }
    }
    find(id) {
        for (let s of [...this._sequences, this.errorSequence, this.endSequence]) {
            if (s._id === id)
                return s;
            for (let step of [...s.steps, s.hiddenStep]) {
                if (step._id === id)
                    return step;
            }
        }
        return null;
    }
    findSequenceByStepId(stepId) {
        for (let s of [...this._sequences, this.errorSequence, this.endSequence]) {
            if (s.find(stepId))
                return s;
        }
        return null;
    }
    add(step, seq, index) {
        var _a;
        let sequence = seq instanceof FlowStepSequence ? seq : this.getSequence(seq);
        if (!sequence && typeof seq === "string") {
            sequence = new FlowStepSequence();
            sequence._id = seq;
            this.addSequence(sequence);
            (_a = this.context) === null || _a === void 0 ? void 0 : _a.positionSequenceToFarRight(sequence);
        }
        if (sequence) {
            sequence.addStep(step, index);
        }
    }
    addStepToCandidate(step, candidate) {
        var _a;
        let s = candidate.createSequence();
        let instruction = Reflect.get(candidate, "instruction");
        if (instruction) {
            // hook the paths together
            if (instruction.pathName && instruction.stepId) {
                instruction.connectedSequenceId = s._id;
            }
        }
        this.addSequence(s);
        if (this.context) {
            s.strategy = (_a = this.context) === null || _a === void 0 ? void 0 : _a.displayBehavior.getInitialSequenceStrategy();
        }
        s.addStep(step);
    }
    moveStepToCandidate(step, sequence, candidate) {
        if (!this.canMoveToSequence(step, candidate)) {
            throw new Error("You can not move this step");
        }
        sequence.removeStep(step);
        this.addStepToCandidate(step, candidate);
    }
    remove(step, sequenceId) {
        let seq = null;
        if (sequenceId) {
            seq = this.getSequence(sequenceId);
        }
        else {
            seq = this.findSequenceByStepId(step._id);
        }
        if (seq) {
            seq.removeStep(step);
        }
    }
    moveInSequence(step, sequence, index) {
        let s = this.getSequence(sequence);
        if (s) {
            s.moveStep(step, index);
        }
    }
    canMoveToSequence(step, toSequence) {
        let seqs = this.getUpstreamSequencesFromStep(step);
        return !(seqs.length > 0 && seqs.includes(toSequence));
    }
    moveToSequence(step, fromSequence, toSequence, index) {
        let fromS = fromSequence instanceof FlowStepSequence ? fromSequence : this.getSequence(fromSequence);
        let toS = toSequence instanceof FlowStepSequence ? toSequence : this.getSequence(toSequence);
        if (!fromS || !toS)
            return;
        // check if sequence is connected to step.
        if (!this.canMoveToSequence(step, toS)) {
            throw new Error("You can not move this step");
        }
        fromS.removeStep(step);
        toS.addStep(step, index);
    }
    getSequence(id) {
        for (let s of [...this._sequences, this.endSequence, this.errorSequence]) {
            if (s._id === id)
                return s;
        }
        return null;
    }
    renderEditUI() {
        return _jsx("div", { children: "edit flow" }, void 0);
    }
    addSequence(newSeq) {
        this._sequences.push(newSeq);
        newSeq.flow = this;
    }
    deleteSequenceByIndex(idx) {
        var _a;
        let s = this._sequences.splice(idx, 1);
        (_a = this.context) === null || _a === void 0 ? void 0 : _a.onSequenceRemoved(s[0]);
    }
    getAlternateOutputPaths() {
        let output = [];
        for (let seq of this._sequences) {
            output = [...output, ...seq.instructions];
        }
        return output;
    }
    getConnections() {
        var _a, _b;
        let conn = [];
        for (let seq of this._sequences) {
            for (let step of [...seq.steps, seq.hiddenStep]) {
                // hide the line while dragging
                if (step._id === ((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.draggingControl) === null || _b === void 0 ? void 0 : _b.source.current)) {
                    continue;
                }
                for (let inst of step.getAlternateOutcomeInstructions()) {
                    if (inst.strategy === FlowStepOutputInstructionType.BRANCH && inst.connectedSequenceId) {
                        let startId = Reflect.get(step, "hidden") ? seq._id : inst.getElementId();
                        conn.push(new FlowConnection(startId, inst.connectedSequenceId, false, seq, inst, step));
                    }
                }
            }
        }
        return conn;
    }
    getEndFlowConnections() {
        var _a, _b, _c;
        let conn = [];
        // showErrorOrEndConnections is set END FLOW or end stack is expanded.
        if (((_a = this.context) === null || _a === void 0 ? void 0 : _a.showErrorOrEndConnections) === FlowStepOutputInstructionType.END_FLOW ||
            !((_b = this.context) === null || _b === void 0 ? void 0 : _b.displayBehavior.isCollapsed(this.endSequence)) || ((_c = this.context) === null || _c === void 0 ? void 0 : _c.isShowErrorBranch)) {
            for (let s of this.sequences) {
                for (let step of [...s.steps, s.hiddenStep]) {
                    step.getOutcomeInstructions().forEach((ins) => {
                        if (ins.strategy === FlowStepOutputInstructionType.END_FLOW) {
                            let fromStep = this.find(ins.stepId);
                            if (fromStep) {
                                conn.push(new FlowExtraConnection(ins.getElementId(), this.endSequence._id, fromStep, ins));
                            }
                        }
                    });
                }
            }
        }
        return conn;
    }
    getErrorFlowConnections() {
        var _a, _b, _c;
        let conn = [];
        // showErrorOrEndConnections is set THROW or error stack is expanded.
        if (((_a = this.context) === null || _a === void 0 ? void 0 : _a.showErrorOrEndConnections) === FlowStepOutputInstructionType.THROW_EXCEPTION ||
            !((_b = this.context) === null || _b === void 0 ? void 0 : _b.displayBehavior.isCollapsed(this.errorSequence)) || ((_c = this.context) === null || _c === void 0 ? void 0 : _c.isShowErrorBranch)) {
            for (let s of this.sequences) {
                for (let step of [...s.steps, s.hiddenStep]) {
                    step.getOutcomeInstructions().forEach((ins) => {
                        if (ins.strategy === FlowStepOutputInstructionType.THROW_EXCEPTION) {
                            let fromStep = this.find(ins.stepId);
                            if (fromStep) {
                                conn.push(new FlowExtraConnection(ins.getElementId(), this.errorSequence._id, fromStep, ins));
                            }
                        }
                    });
                }
            }
        }
        return conn;
    }
    getUpstreamSequences(s) {
        let seqs = [];
        if (!s.instructions)
            return [];
        let insts = s.instructions.filter((ins) => ins.strategy === FlowStepOutputInstructionType.BRANCH);
        if (insts.length) {
            insts.forEach((ins) => {
                var _a;
                if (ins.connectedSequenceId) {
                    let ss = (_a = this.context) === null || _a === void 0 ? void 0 : _a.getCandidateOrRealSequence(ins.connectedSequenceId);
                    if (ss) {
                        seqs.push(ss);
                        if (ss instanceof FlowStepSequence) {
                            let upstreamSeqs = this.getUpstreamSequences(ss);
                            for (let us of upstreamSeqs) {
                                if (!seqs.includes(us)) {
                                    seqs.push(us);
                                }
                            }
                        }
                    }
                }
            });
        }
        return seqs;
    }
    getUpstreamSequencesFromStep(step) {
        var _a;
        let seqs = [];
        for (let inst of step.getOutcomeInstructions()) {
            if (inst.strategy === FlowStepOutputInstructionType.BRANCH) {
                if (inst.connectedSequenceId) {
                    let ss = (_a = this.context) === null || _a === void 0 ? void 0 : _a.getCandidateOrRealSequence(inst.connectedSequenceId);
                    if (ss) {
                        seqs.push(ss);
                        if (ss instanceof FlowStepSequence) {
                            seqs = [...seqs, ...this.getUpstreamSequences(ss)];
                        }
                    }
                }
            }
        }
        return seqs;
    }
    canLink(start, end) {
        return !this.getUpstreamSequences(end).includes(start);
    }
    isDraggingOverSequence(sequence) {
        if (this.context) {
            if (this.context.draggingControl &&
                this.context.draggingControl.destination &&
                this.context.draggingControl.destination === sequence._id)
                return true;
        }
        return false;
    }
    getOutcomes() {
        return [];
    }
    run(data, testContext) {
        throw new Error('not implemented');
    }
}
//# sourceMappingURL=XFlowConfiguration.js.map