Source: atom.js

'use strict';

import { Reporter } from './reporter/reporter.js';
import { InformationSource } from './reporter/information_source.js';

/** Atom */

class Atom extends InformationSource {

    /** @typedef {string} state */

    /** @typedef {Object<state,Array<state>>} StatesAndTransitions */

    /**
     * @param {Reporter} reporter
     * @param {string} name
     * @param {StatesAndTransitions} stateTransistions
     * @param {state} initialState
     */

    constructor(reporter, name, stateTransistions, initialState) {

        super(reporter, {
            name
        });

        this.addEvent('change');

        for (const state in stateTransistions) {

            const transitions = stateTransistions[state];

            if (!Array.isArray(transitions)) {
                throw new Error('Missing state transitions array');
            }

            for (const transition of transitions) {
                if (!stateTransistions.hasOwnProperty(transition)) {
                    throw new Error('State transition to unknown state specified');
                }
            }
        }

        if ( ! stateTransistions[ initialState ]) {
            throw new Error( 'Unknown initial state ' + initialState );
        }

        this._states = stateTransistions;

        this._state = initialState
    }


    /**
     * @private
     * @type {string} 
     */

    _state;


    /** 
     * @type {Array<Object>}
     * @private
     */

    _states;

    /**
     * @param {string} newState 
     * @returns {Promise<Array<any>>}
     */

    async setState( newState, bubbleState ) {

        if (newState === this._state) {
            return;
        }

        if (this._states[newState]) {

            const previousState = this._state;

            if (previousState && !this._states[previousState].includes(newState)) {
                const msg = `Transition from state ${previousState} to state ${newState} not allowed`;

                this.report({
                    msg,
                    level: 'error'
                });

                throw new Error(msg);
            }

            this._state = newState;

            return this.emit('change', { state: this._state, previousState, bubbleState });
        }
        else {
            // console.error( 'Requested unknown state', newState);
            throw new Error('Unknown state ' + newState);
        }
    }

    /**
     * @type {state}
     */

    get state() {
        return this._state
    }

}

export { Atom };