
import Interaction from "./Interaction";
import InteractionFactory from "./InteractionFactory";
import StepNFA from "./StepNFA";
import StepState, { transitionType } from "./StepState";

export default class Step {
    public name: string;
    public expression: string;
    public stepDFA: StepNFA;

    constructor(name: string, expression: string, dfa: StepNFA) {
        this.name = name;
        this.expression = expression;
        this.stepDFA = dfa;
    }

    get enteringInteractionList(): Interaction[] {
        return Array.from(this.stepDFA.transitions.get(this.stepDFA.startState).keys())
                    .map((label) => InteractionFactory.parseInteraction(label));
    }

    public followSequence(sequence: Interaction[]): { state: StepState, interactionsNotConsumed: Interaction[]} {
        let state = this.stepDFA.startState;
        const interactionsNotConsumed = sequence.slice();
        while (interactionsNotConsumed.length > 0 && !this.isEnteringInteraction(interactionsNotConsumed[0])) {
            interactionsNotConsumed.shift();
        }
        while (interactionsNotConsumed.length > 0) {
            const interaction = interactionsNotConsumed.shift();
            state = this.followInteraction(state, interaction);
            if (state === null) {
                return {
                    state: null,
                    interactionsNotConsumed,
                };
            }
            if (this.stepDFA.isFinalState(state)) {
                break;
            }
        }
        return {
            state,
            interactionsNotConsumed,
        };
    }

    private isEnteringInteraction(interaction: Interaction): boolean {

        return this.enteringInteractionList.some((enteringInteraction) => {
            if (enteringInteraction.getLabel() === interaction.getLabel()) {
                return true;
            } else if (enteringInteraction.toString() === interaction.toString()) {
                return true;
            } else if (enteringInteraction.toString() === transitionType.star) {
                return true;
            }
        });
    }

    private followInteraction(currentState: StepState, interaction: Interaction): StepState {
        let successor = this.getSuccessor(currentState, interaction);
        if (successor) {
            return successor;
        }
        successor = this.getSuccessor(this.stepDFA.startState, interaction);
        if (successor) {
            return successor;
        }
        return this.stepDFA.startState;
    }

    private getSuccessor(state: StepState, interaction: Interaction): StepState {
        if (this.stepDFA.transitions.has(state)) {
            const transition = this.stepDFA.transitions.get(state);
            if (transition.has(interaction.getLabel())) {
                return transition.get(interaction.getLabel())[0];
            } else if (transition.has(interaction.toString())) {
                return transition.get(interaction.toString())[0];
            } else if (transition.has(transitionType.star)) {
                return transition.get(transitionType.star)[0];
            } else {
                return null;
            }
        } else {
            return null;
        }
    }
}
