"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var Curve_1 = require("./api/Curve");
var Interpolate_1 = require("./Interpolate");
var StateTransitionManager = /** @class */ (function () {
    function StateTransitionManager(states, clock) {
        this.states = states;
        this.clock = clock;
        // Store running state transitions
        this.transitions = {};
    }
    /**
     * Create a new state transition.
     */
    StateTransitionManager.prototype.setState = function (transitionEnd, parameter) {
        var _a, _b, _c, _d;
        // If not a transition, execute it right away
        if (!parameter) {
            for (var key in transitionEnd) {
                delete this.transitions[key];
            }
            this.setStates(transitionEnd);
            return;
        }
        // Get current time
        var currentTime = this.clock.getTime();
        // Set default values than assign parameters
        var transitionParameter = { duration: 0, curve: Curve_1.Curve.Linear, queue: false };
        Object.assign(transitionParameter, parameter);
        // Copy current states as transition start (needed to calculate interpolation)
        for (var key in transitionEnd) {
            // Ignore state if it doesn't pre exist
            if (key in this.states) {
                // queued transitions are add into queue
                // If parameter.queue is true, it is a queued setState
                // If state transition for key is not created, process like a queued SetState
                if (transitionParameter.queue && this.transitions[key]) {
                    this.transitions[key].push({
                        transitionParameter: transitionParameter,
                        transitionEnd: (_a = {}, _a[key] = transitionEnd[key], _a),
                        transitionStart: (_b = {}, _b[key] = this.states[key], _b),
                        startTime: currentTime,
                        endTime: currentTime + transitionParameter.duration,
                        duration: transitionParameter.duration,
                    });
                    // non queued transitions are overwrite transition queue
                }
                else {
                    this.transitions[key] = [{
                            transitionParameter: transitionParameter,
                            transitionEnd: (_c = {}, _c[key] = transitionEnd[key], _c),
                            transitionStart: (_d = {}, _d[key] = this.states[key], _d),
                            startTime: currentTime,
                            endTime: currentTime + transitionParameter.duration,
                            duration: transitionParameter.duration,
                        }];
                }
            }
        }
        // Make sure state is update on setState call
        this.tickStateTransitions();
    };
    // We technically could call HaikuComponent::setStates, but passing only
    // state parameters instead whole HaikuComponent to contructor make this class
    // less coupled
    StateTransitionManager.prototype.setStates = function (states) {
        for (var key in states) {
            this.states[key] = states[key];
        }
    };
    StateTransitionManager.prototype.isExpired = function (transition, currentTime) {
        return this.clock.getTime() >= transition.endTime;
    };
    /**
     * Should be called on every tick. It cleans expired state transitions
     * and execute interpolation of running state transitions.
     */
    StateTransitionManager.prototype.tickStateTransitions = function () {
        var _a;
        var currentTime = this.clock.getTime();
        var interpolatedStates = {};
        // For each state, process state transition queue
        for (var stateName in this.transitions) {
            // On queued states, only first transition is processed, other transitions are in queue.
            if (this.transitions[stateName].length > 0) {
                var transition = this.transitions[stateName][0];
                if (this.isExpired(transition, currentTime)) {
                    // If expired, assign transitionEnd.
                    // NOTE: In the future, with custom transition function implemented calculating
                    // interpolation at endTime will be necessary (eg. a user defined curve that at
                    // endTime isn't 100%, but let's say 60%)
                    Object.assign(interpolatedStates, transition.transitionEnd);
                    // Remove expired transition.
                    this.transitions[stateName].splice(0, 1);
                    // Update next queued state transition or delete empty transition vector for performance reasons
                    if (this.transitions[stateName].length > 0) {
                        this.transitions[stateName][0].transitionStart = (_a = {}, _a[stateName] = interpolatedStates[stateName], _a);
                        this.transitions[stateName][0].startTime = currentTime;
                        this.transitions[stateName][0].endTime = currentTime + this.transitions[stateName][0].duration;
                    }
                    else {
                        delete this.transitions[stateName];
                    }
                }
                else {
                    // Calculate interpolated states.
                    Object.assign(interpolatedStates, Interpolate_1.default.interpolate(currentTime, transition.transitionParameter.curve, transition.startTime, transition.endTime, transition.transitionStart, transition.transitionEnd));
                }
            }
        }
        this.setStates(interpolatedStates);
    };
    /**
     * Delete every running transition
     */
    StateTransitionManager.prototype.deleteAllStateTransitions = function () {
        this.transitions = {};
    };
    Object.defineProperty(StateTransitionManager.prototype, "numQueuedTransitions", {
        get: function () {
            var numQueuedTransition = 0;
            for (var stateName in this.transitions) {
                numQueuedTransition += this.transitions[stateName].length;
            }
            return numQueuedTransition;
        },
        enumerable: true,
        configurable: true
    });
    return StateTransitionManager;
}());
exports.default = StateTransitionManager;
//# sourceMappingURL=StateTransitionManager.js.map