import { PluginReceiver } from '../editor/plugin/receiver.js';
import { Runner } from '../editor/runner.js';
import { OutputModule } from './module.js';
import { DefaultOutputViewProvider } from './default.js';
import { PartsManager } from '../parts/manager.js';
import { EventEmitter } from '@kano/common/index.js';
let configResolver = null;
export const OutputConfig = {
    get(key, fallback) {
        if (!configResolver) {
            return fallback;
        }
        const value = configResolver(key);
        if (!value) {
            return fallback;
        }
        return value;
    }
};
export class Output extends PluginReceiver {
    constructor() {
        super();
        this._running = false;
        this._fullscreen = false;
        this._code = '';
        // Events
        this._onDidRunningStateChange = new EventEmitter();
        this._onDidFullscreenChange = new EventEmitter();
        this.runner = new Runner();
        this.runner.addModule(OutputModule);
        this.parts = new PartsManager(this);
        this.addPlugin(this.runner);
    }
    get visuals() {
        if (!this.outputViewProvider) {
            throw new Error('Could not get visuals: Output view was not registered');
        }
        return this.outputViewProvider.getVisuals();
    }
    get audio() {
        if (!this.outputViewProvider) {
            throw new Error('Could not get audio: Output view was not registered');
        }
        return this.outputViewProvider.getAudio();
    }
    get dom() {
        if (!this.outputViewProvider) {
            throw new Error('Could not get DOM: Output view was not registered');
        }
        return this.outputViewProvider.getDOM();
    }
    get onDidRunningStateChange() { return this._onDidRunningStateChange.event; }
    get onDidFullscreenChange() { return this._onDidFullscreenChange.event; }
    static get config() {
        return OutputConfig;
    }
    /**
     * Set the global configuration resolver
     * @param resolver A configuration resolver
     */
    static setConfigResolver(resolver) {
        configResolver = resolver;
    }
    runPluginTask(taskName, ...args) {
        super.runPluginTask(taskName, ...args);
    }
    addPart(partClass) {
        this.parts.addPart(partClass);
    }
    addPlugin(plugin) {
        super.addPlugin(plugin);
        plugin.onInstall(this);
    }
    registerOutputViewProvider(provider) {
        this.outputViewProvider = provider;
        this.outputViewProvider.onInstall(this);
    }
    registerProfile(outputProfile) {
        // No outputProfile was provider, fallback to default
        if (!outputProfile) {
            return;
        }
        if (outputProfile.onInstall) {
            outputProfile.onInstall(this);
        }
        if (!outputProfile.id) {
            throw new Error('Could not register OutputProfile: missing id');
        }
        if (outputProfile && !Array.isArray(outputProfile.modules)) {
            throw new Error('Could not register OutputProfile: modules is not an array');
        }
        if (outputProfile.plugins && !Array.isArray(outputProfile.plugins)) {
            throw new Error('Could not register OutputProfile: plugins is not an array');
        }
        this.outputProfile = outputProfile;
        // Missing outputViewProvider, using default one
        if (!this.outputProfile.outputViewProvider) {
            this.registerOutputViewProvider(new DefaultOutputViewProvider());
        }
        else {
            this.registerOutputViewProvider(this.outputProfile.outputViewProvider);
        }
        // Add defined plugins
        if (this.outputProfile.plugins) {
            this.outputProfile.plugins.forEach((p) => this.addPlugin(p));
        }
        // Add defined modules to the runner
        if (this.outputProfile.modules) {
            this.outputProfile.modules.forEach((m) => this.runner.addModule(m));
        }
        if (this.outputProfile.parts) {
            this.outputProfile.parts.forEach(p => this.parts.registerPart(p));
        }
    }
    ensureOutputView() {
        if (!this.outputViewProvider) {
            this.outputViewProvider = new DefaultOutputViewProvider();
        }
    }
    setRunningState(running) {
        this._running = running;
        this._onDidRunningStateChange.fire(this._running);
    }
    getRunningState() {
        return this._running;
    }
    toggleRunningState() {
        this.setRunningState(!this.getRunningState());
    }
    setFullscreen(state) {
        this._fullscreen = state;
        this._onDidFullscreenChange.fire(this._fullscreen);
    }
    getFullscreen() {
        return this._fullscreen;
    }
    toggleFullscreen() {
        this.setFullscreen(!this.getFullscreen());
    }
    restart() {
        this.setRunningState(false);
        this.setRunningState(true);
    }
    setCode(code) {
        this._code = code || '';
    }
    getCode() {
        return this._code;
    }
    get id() {
        if (!this.outputProfile) {
            throw new Error('Could not get profile id: An output pforile was not registered');
        }
        return this.outputProfile.id;
    }
    get outputView() {
        return this.outputViewProvider;
    }
    onInject() {
        this.ensureOutputView();
        if (this.outputProfile) {
            this.outputProfile.onInject();
        }
        this.parts.onInject();
        this.runPluginTask('onInject');
    }
    onExport(data) {
        const parts = this.parts.save();
        const exp = this.plugins.reduce((d, plugin) => plugin.onExport(d), data);
        exp.parts = parts;
        return exp;
    }
    onImport(data) {
        this.parts.load(data.parts || []);
        this.runPluginTask('onImport', data);
    }
    onCreationExport(data) {
        data.code = this.getCode();
        data.profile = this.id;
        data.parts = this.parts.save();
        return data;
    }
    onCreationImport(data) {
        return this.runPluginChainTask('onCreationImport', data)
            .then(() => {
            if (this.outputView) {
                return this.outputView.onCreationImport(data);
            }
        })
            .then(() => {
            this.parts.load(data.parts || []);
            this.setCode(data.code);
        });
    }
    render(...args) {
        if (this.outputViewProvider) {
            return this.outputViewProvider.render(...args);
        }
        return null;
    }
    dispose() {
        this.runner.dispose();
        if (this.outputViewProvider) {
            this.outputViewProvider.onDispose();
        }
        this.runPluginTask('onDispose');
    }
}
export default Output;
//# sourceMappingURL=output.js.map