'use strict';

/* ────────────────────────────────────────────────────────────────
   Animation — Sprite / skeletal animation system
──────────────────────────────────────────────────────────────── */
/* ── Easings ──────────────────────────────────────────────────── */
const Easing = {
    linear: (t) => t,
    easeInQuad: (t) => t * t,
    easeOutQuad: (t) => t * (2 - t),
    easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
    easeInCubic: (t) => t * t * t,
    easeOutCubic: (t) => (--t) * t * t + 1,
    easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
    easeOutBounce: (t) => {
        const n1 = 7.5625, d1 = 2.75;
        if (t < 1 / d1)
            return n1 * t * t;
        if (t < 2 / d1)
            return n1 * (t -= 1.5 / d1) * t + 0.75;
        if (t < 2.5 / d1)
            return n1 * (t -= 2.25 / d1) * t + 0.9375;
        return n1 * (t -= 2.625 / d1) * t + 0.984375;
    },
    easeOutElastic: (t) => {
        if (t === 0 || t === 1)
            return t;
        return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;
    },
};
/* ── SpriteAnimator ───────────────────────────────────────────── */
class SpriteAnimator {
    constructor() {
        this.clips = new Map();
        this.currentClip = null;
        this.frameIndex = 0;
        this.elapsed = 0;
        this.playing = false;
        this.onEvent = null;
        this.finished = false;
    }
    get isPlaying() { return this.playing; }
    get isFinished() { return this.finished; }
    get currentClipName() { var _a, _b; return (_b = (_a = this.currentClip) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null; }
    get currentFrameIndex() { return this.frameIndex; }
    addClip(clip) {
        this.clips.set(clip.name, clip);
    }
    removeClip(name) {
        this.clips.delete(name);
    }
    play(name, restart = false) {
        const clip = this.clips.get(name);
        if (!clip)
            return;
        if (this.currentClip === clip && this.playing && !restart)
            return;
        this.currentClip = clip;
        this.frameIndex = 0;
        this.elapsed = 0;
        this.playing = true;
        this.finished = false;
        this.fireEvent();
    }
    stop() {
        this.playing = false;
    }
    resume() {
        if (this.currentClip)
            this.playing = true;
    }
    setEventCallback(cb) {
        this.onEvent = cb;
    }
    getCurrentFrame() {
        var _a;
        if (!this.currentClip)
            return null;
        return (_a = this.currentClip.frames[this.frameIndex]) !== null && _a !== void 0 ? _a : null;
    }
    update(dt) {
        if (!this.playing || !this.currentClip)
            return;
        const clip = this.currentClip;
        const frame = clip.frames[this.frameIndex];
        if (!frame)
            return;
        this.elapsed += dt * clip.speed;
        while (this.elapsed >= frame.duration && this.playing) {
            this.elapsed -= frame.duration;
            this.frameIndex++;
            if (this.frameIndex >= clip.frames.length) {
                if (clip.loop) {
                    this.frameIndex = 0;
                }
                else {
                    this.frameIndex = clip.frames.length - 1;
                    this.playing = false;
                    this.finished = true;
                    return;
                }
            }
            this.fireEvent();
        }
    }
    fireEvent() {
        if (!this.currentClip || !this.onEvent)
            return;
        const frame = this.currentClip.frames[this.frameIndex];
        if (frame === null || frame === void 0 ? void 0 : frame.event) {
            this.onEvent(this.currentClip.name, frame.event);
        }
    }
}
class TweenManager {
    constructor() {
        this.tweens = [];
    }
    create(config) {
        var _a, _b, _c;
        const from = {};
        const to = {};
        for (const key of Object.keys(config.to)) {
            const toVal = config.to[key];
            if (typeof toVal === 'number')
                to[key] = toVal;
            const fromVal = (_a = config.from) === null || _a === void 0 ? void 0 : _a[key];
            from[key] = typeof fromVal === 'number' ? fromVal : config.target[key];
        }
        this.tweens.push({
            target: config.target,
            from,
            to,
            duration: config.duration,
            elapsed: 0,
            delay: (_b = config.delay) !== null && _b !== void 0 ? _b : 0,
            easing: (_c = config.easing) !== null && _c !== void 0 ? _c : Easing.linear,
            onComplete: config.onComplete,
            onUpdate: config.onUpdate,
        });
    }
    update(dt) {
        var _a, _b;
        for (let i = this.tweens.length - 1; i >= 0; i--) {
            const tw = this.tweens[i];
            if (tw.delay > 0) {
                tw.delay -= dt;
                continue;
            }
            tw.elapsed += dt;
            const raw = Math.min(tw.elapsed / tw.duration, 1);
            const t = tw.easing(raw);
            for (const key of Object.keys(tw.to)) {
                tw.target[key] = tw.from[key] + (tw.to[key] - tw.from[key]) * t;
            }
            (_a = tw.onUpdate) === null || _a === void 0 ? void 0 : _a.call(tw, t);
            if (raw >= 1) {
                (_b = tw.onComplete) === null || _b === void 0 ? void 0 : _b.call(tw);
                this.tweens.splice(i, 1);
            }
        }
    }
    clear() {
        this.tweens.length = 0;
    }
    get count() {
        return this.tweens.length;
    }
}
class AnimationStateMachine {
    constructor(animator) {
        this.states = new Map();
        this.transitions = [];
        this.current = null;
        this.animator = animator;
    }
    addState(name, clipName) {
        this.states.set(name, { name, clipName });
        if (!this.current) {
            this.current = this.states.get(name);
            this.animator.play(clipName);
        }
    }
    addTransition(from, to, condition) {
        this.transitions.push({ from, to, condition });
    }
    get currentState() {
        var _a, _b;
        return (_b = (_a = this.current) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : null;
    }
    forceState(name) {
        const state = this.states.get(name);
        if (!state)
            return;
        this.current = state;
        this.animator.play(state.clipName, true);
    }
    update() {
        if (!this.current)
            return;
        for (const tr of this.transitions) {
            if (tr.from === this.current.name && tr.condition()) {
                const next = this.states.get(tr.to);
                if (next) {
                    this.current = next;
                    this.animator.play(next.clipName, true);
                }
                break;
            }
        }
    }
}
/* ── Sprite Sheet Utility ─────────────────────────────────────── */
function createClipFromSheet(name, sheetColumns, startIndex, frameCount, frameWidth, frameHeight, frameDuration = 0.1, loop = true) {
    const frames = [];
    for (let i = 0; i < frameCount; i++) {
        const idx = startIndex + i;
        const col = idx % sheetColumns;
        const row = Math.floor(idx / sheetColumns);
        frames.push({
            srcRect: { x: col * frameWidth, y: row * frameHeight, width: frameWidth, height: frameHeight },
            duration: frameDuration,
        });
    }
    return { name, frames, loop, speed: 1 };
}

exports.AnimationStateMachine = AnimationStateMachine;
exports.Easing = Easing;
exports.SpriteAnimator = SpriteAnimator;
exports.TweenManager = TweenManager;
exports.createClipFromSheet = createClipFromSheet;
//# sourceMappingURL=Animation.js.map
