import { Color } from '../Color';
import * as DrawUtil from '../Util/DrawUtil';
import logoImg from './Loader.logo.png';
import loaderCss from './Loader.css';
import { delay } from '../Util/Util';
import { EventEmitter } from '../EventEmitter';
import { DefaultLoader } from './DefaultLoader';
import { Logger } from '../Util/Log';
import { Future } from '../Util/Future';
/**
 * Pre-loading assets
 *
 * The loader provides a mechanism to preload multiple resources at
 * one time. The loader must be passed to the engine in order to
 * trigger the loading progress bar.
 *
 * The [[Loader]] itself implements [[Loadable]] so you can load loaders.
 *
 * ## Example: Pre-loading resources for a game
 *
 * ```js
 * // create a loader
 * var loader = new ex.Loader();
 *
 * // create a resource dictionary (best practice is to keep a separate file)
 * var resources = {
 *   TextureGround: new ex.Texture("/images/textures/ground.png"),
 *   SoundDeath: new ex.Sound("/sound/death.wav", "/sound/death.mp3")
 * };
 *
 * // loop through dictionary and add to loader
 * for (var loadable in resources) {
 *   if (resources.hasOwnProperty(loadable)) {
 *     loader.addResource(resources[loadable]);
 *   }
 * }
 *
 * // start game
 * game.start(loader).then(function () {
 *   console.log("Game started!");
 * });
 * ```
 *
 * ## Customize the Loader
 *
 * The loader can be customized to show different, text, logo, background color, and button.
 *
 * ```typescript
 * const loader = new ex.Loader([playerTexture]);
 *
 * // The loaders button text can simply modified using this
 * loader.playButtonText = 'Start the best game ever';
 *
 * // The logo can be changed by inserting a base64 image string here
 *
 * loader.logo = 'data:image/png;base64,iVBORw...';
 * loader.logoWidth = 15;
 * loader.logoHeight = 14;
 *
 * // The background color can be changed like so by supplying a valid CSS color string
 *
 * loader.backgroundColor = 'red'
 * loader.backgroundColor = '#176BAA'
 *
 * // To build a completely new button
 * loader.startButtonFactory = () => {
 *     let myButton = document.createElement('button');
 *     myButton.textContent = 'The best button';
 *     return myButton;
 * };
 *
 * engine.start(loader).then(() => {});
 * ```
 */
export class Loader extends DefaultLoader {
    get _image() {
        if (!this._imageElement) {
            this._imageElement = new Image();
            this._imageElement.onload = () => this._imageLoaded.resolve();
            this._imageElement.src = this.logo;
        }
        return this._imageElement;
    }
    get playButtonRootElement() {
        return this._playButtonRootElement;
    }
    get playButtonElement() {
        return this._playButtonElement;
    }
    get _playButton() {
        const existingRoot = document.getElementById('excalibur-play-root');
        if (existingRoot) {
            this._playButtonRootElement = existingRoot;
        }
        if (!this._playButtonRootElement) {
            this._playButtonRootElement = document.createElement('div');
            this._playButtonRootElement.id = 'excalibur-play-root';
            this._playButtonRootElement.style.position = 'absolute';
            document.body.appendChild(this._playButtonRootElement);
        }
        if (!this._styleBlock) {
            this._styleBlock = document.createElement('style');
            this._styleBlock.textContent = this._playButtonStyles;
            document.head.appendChild(this._styleBlock);
        }
        if (!this._playButtonElement) {
            this._playButtonElement = this.startButtonFactory();
            this._playButtonRootElement.appendChild(this._playButtonElement);
        }
        return this._playButtonElement;
    }
    constructor(loadablesOrOptions) {
        const options = Array.isArray(loadablesOrOptions)
            ? {
                loadables: loadablesOrOptions
            }
            : loadablesOrOptions;
        super(options);
        this._logger = Logger.getInstance();
        this._originalOptions = { loadables: [] };
        this.events = new EventEmitter();
        this._playButtonShown = false;
        // logo drawing stuff
        // base64 string encoding of the excalibur logo (logo-white.png)
        this.logo = logoImg;
        this.logoWidth = 468;
        this.logoHeight = 118;
        /**
         * Gets or sets the color of the loading bar, default is [[Color.White]]
         */
        this.loadingBarColor = Color.White;
        /**
         * Gets or sets the background color of the loader as a hex string
         */
        this.backgroundColor = '#176BAA';
        this._imageLoaded = new Future();
        this.suppressPlayButton = false;
        /** Loads the css from Loader.css */
        this._playButtonStyles = loaderCss.toString();
        /**
         * Get/set play button text
         */
        this.playButtonText = 'Play game';
        /**
         * Return a html button element for excalibur to use as a play button
         */
        this.startButtonFactory = () => {
            let buttonElement = document.getElementById('excalibur-play');
            if (!buttonElement) {
                buttonElement = document.createElement('button');
            }
            buttonElement.id = 'excalibur-play';
            buttonElement.textContent = this.playButtonText;
            buttonElement.style.display = 'none';
            return buttonElement;
        };
        this._configuredPixelRatio = null;
        this._originalOptions = { ...Loader._DEFAULT_LOADER_OPTIONS, ...options };
    }
    onInitialize(engine) {
        this.engine = engine;
        this.screen = engine.screen;
        this.canvas.width = this.engine.canvas.width;
        this.canvas.height = this.engine.canvas.height;
        this.screen.events.on('resize', () => {
            this.canvas.width = this.engine.canvas.width;
            this.canvas.height = this.engine.canvas.height;
        });
    }
    /**
     * Shows the play button and returns a promise that resolves when clicked
     */
    async showPlayButton() {
        var _a, _b;
        if (this.suppressPlayButton) {
            this.hidePlayButton();
            // Delay is to give the logo a chance to show, otherwise don't delay
            await delay(500, (_a = this.engine) === null || _a === void 0 ? void 0 : _a.clock);
        }
        else {
            const resizeHandler = () => {
                try {
                    this._positionPlayButton();
                }
                catch (_a) {
                    // swallow if can't position
                }
            };
            if ((_b = this.engine) === null || _b === void 0 ? void 0 : _b.browser) {
                this.engine.browser.window.on('resize', resizeHandler);
            }
            this._playButtonShown = true;
            this._playButton.style.display = 'block';
            document.body.addEventListener('keyup', (evt) => {
                if (evt.key === 'Enter') {
                    this._playButton.click();
                }
            });
            this._positionPlayButton();
            const playButtonClicked = new Promise((resolve) => {
                const startButtonHandler = (e) => {
                    var _a;
                    // We want to stop propagation to keep bubbling to the engine pointer handlers
                    e.stopPropagation();
                    // Hide Button after click
                    this.hidePlayButton();
                    if ((_a = this.engine) === null || _a === void 0 ? void 0 : _a.browser) {
                        this.engine.browser.window.off('resize', resizeHandler);
                    }
                    if (this._originalOptions.fullscreenAfterLoad) {
                        try {
                            this._logger.info('requesting fullscreen');
                            if (this._originalOptions.fullscreenContainer instanceof HTMLElement) {
                                this._originalOptions.fullscreenContainer.requestFullscreen();
                            }
                            else {
                                this.engine.screen.goFullScreen(this._originalOptions.fullscreenContainer);
                            }
                        }
                        catch (error) {
                            this._logger.error('could not go fullscreen', error);
                        }
                    }
                    resolve();
                };
                this._playButton.addEventListener('click', startButtonHandler);
                this._playButton.addEventListener('touchend', startButtonHandler);
                this._playButton.addEventListener('pointerup', startButtonHandler);
            });
            return await playButtonClicked;
        }
    }
    hidePlayButton() {
        this._playButtonShown = false;
        this._playButton.style.display = 'none';
    }
    /**
     * Clean up generated elements for the loader
     */
    dispose() {
        if (this._playButtonRootElement.parentElement) {
            this._playButtonRootElement.removeChild(this._playButtonElement);
            document.body.removeChild(this._playButtonRootElement);
            document.head.removeChild(this._styleBlock);
            this._playButtonRootElement = null;
            this._playButtonElement = null;
            this._styleBlock = null;
        }
    }
    async onUserAction() {
        var _a;
        // short delay in showing the button for aesthetics
        await delay(200, (_a = this.engine) === null || _a === void 0 ? void 0 : _a.clock);
        this.canvas.flagDirty();
        // show play button
        await this.showPlayButton();
    }
    async onBeforeLoad() {
        const image = this._image;
        await this._imageLoaded.promise;
        await (image === null || image === void 0 ? void 0 : image.decode()); // decode logo if it exists
    }
    // eslint-disable-next-line require-await
    async onAfterLoad() {
        this.screen.pixelRatioOverride = this._configuredPixelRatio;
        this.screen.popResolutionAndViewport();
        this.screen.applyResolutionAndViewport();
        this.dispose();
    }
    _positionPlayButton() {
        if (this.engine) {
            const { x: left, y: top, width: screenWidth, height: screenHeight } = this.engine.canvas.getBoundingClientRect();
            if (this._playButtonRootElement) {
                const buttonWidth = this._playButton.clientWidth;
                const buttonHeight = this._playButton.clientHeight;
                if (this.playButtonPosition) {
                    this._playButtonRootElement.style.left = `${this.playButtonPosition.x}px`;
                    this._playButtonRootElement.style.top = `${this.playButtonPosition.y}px`;
                }
                else {
                    this._playButtonRootElement.style.left = `${left + screenWidth / 2 - buttonWidth / 2}px`;
                    this._playButtonRootElement.style.top = `${top + screenHeight / 2 - buttonHeight / 2 + 100}px`;
                }
            }
        }
    }
    /**
     * Loader draw function. Draws the default Excalibur loading screen.
     * Override `logo`, `logoWidth`, `logoHeight` and `backgroundColor` properties
     * to customize the drawing, or just override entire method.
     */
    onDraw(ctx) {
        const canvasHeight = this.engine.canvasHeight / this.engine.pixelRatio;
        const canvasWidth = this.engine.canvasWidth / this.engine.pixelRatio;
        this._positionPlayButton();
        ctx.fillStyle = this.backgroundColor;
        ctx.fillRect(0, 0, canvasWidth, canvasHeight);
        let logoY = canvasHeight / 2;
        const width = Math.min(this.logoWidth, canvasWidth * 0.75);
        let logoX = canvasWidth / 2 - width / 2;
        if (this.logoPosition) {
            logoX = this.logoPosition.x;
            logoY = this.logoPosition.y;
        }
        const imageHeight = Math.floor(width * (this.logoHeight / this.logoWidth)); // OG height/width factor
        const oldAntialias = this.engine.getAntialiasing();
        this.engine.setAntialiasing(true);
        if (!this.logoPosition) {
            ctx.drawImage(this._image, 0, 0, this.logoWidth, this.logoHeight, logoX, logoY - imageHeight - 20, width, imageHeight);
        }
        else {
            ctx.drawImage(this._image, 0, 0, this.logoWidth, this.logoHeight, logoX, logoY, width, imageHeight);
        }
        // loading box
        if (!this.suppressPlayButton && this._playButtonShown) {
            this.engine.setAntialiasing(oldAntialias);
            return;
        }
        let loadingX = logoX;
        let loadingY = logoY;
        if (this.loadingBarPosition) {
            loadingX = this.loadingBarPosition.x;
            loadingY = this.loadingBarPosition.y;
        }
        ctx.lineWidth = 2;
        DrawUtil.roundRect(ctx, loadingX, loadingY, width, 20, 10, this.loadingBarColor);
        const progress = width * this.progress;
        const margin = 5;
        const progressWidth = progress - margin * 2;
        const height = 20 - margin * 2;
        DrawUtil.roundRect(ctx, loadingX + margin, loadingY + margin, progressWidth > 10 ? progressWidth : 10, height, 5, null, this.loadingBarColor);
        this.engine.setAntialiasing(oldAntialias);
    }
}
Loader._DEFAULT_LOADER_OPTIONS = {
    loadables: [],
    fullscreenAfterLoad: false,
    fullscreenContainer: undefined
};
//# sourceMappingURL=Loader.js.map