/**
 * Copyright 2015 CANAL+ Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { shouldValidateMetadata } from "../../../compat";
import { READY_STATES } from "../../../compat/browser_compatibility_types";
import { isSafariMobile } from "../../../compat/browser_detection";
/* eslint-disable-next-line max-len */
import shouldPreventSeekingAt0Initially from "../../../compat/should_prevent_seeking_at_0_initially";
import { MediaError } from "../../../errors";
import log from "../../../log";
import SharedReference from "../../../utils/reference";
/**
 * Seek as soon as possible at the initially wanted position and play if
 * autoPlay is wanted.
 * @param {HTMLMediaElement} mediaElement
 * @param {Object} playbackObserver
 * @param {number|Function} startTime
 * @param {boolean} mustAutoPlay
 * @param {Function} onWarning
 * @param {boolean} isDirectfile
 * @param {Object} cancelSignal
 * @returns {Object}
 */
export default function performInitialSeekAndPlay(mediaElement, playbackObserver, startTime, mustAutoPlay, onWarning, isDirectfile, cancelSignal) {
    var resolveAutoPlay;
    var rejectAutoPlay;
    var autoPlayResult = new Promise(function (res, rej) {
        resolveAutoPlay = res;
        rejectAutoPlay = rej;
    });
    var initialSeekPerformed = new SharedReference(false, cancelSignal);
    var initialPlayPerformed = new SharedReference(false, cancelSignal);
    mediaElement.addEventListener("loadedmetadata", onLoadedMetadata);
    var deregisterCancellation = cancelSignal.register(function (err) {
        mediaElement.removeEventListener("loadedmetadata", onLoadedMetadata);
        rejectAutoPlay(err);
    });
    if (mediaElement.readyState >= READY_STATES.HAVE_METADATA) {
        onLoadedMetadata();
    }
    return { autoPlayResult: autoPlayResult, initialPlayPerformed: initialPlayPerformed, initialSeekPerformed: initialSeekPerformed };
    function onLoadedMetadata() {
        mediaElement.removeEventListener("loadedmetadata", onLoadedMetadata);
        /** `true` if we asked the `PlaybackObserver` to perform an initial seek. */
        var hasAskedForInitialSeek = false;
        var performInitialSeek = function (initialSeekTime) {
            log.info("Init: Set initial time", initialSeekTime);
            playbackObserver.setCurrentTime(initialSeekTime);
            hasAskedForInitialSeek = true;
            initialSeekPerformed.setValue(true);
            initialSeekPerformed.finish();
        };
        // `startTime` defined as a function might depend on metadata to make its
        // choice, such as the content duration, minimum and/or maximum position.
        //
        // The RxPlayer might already know those through the Manifest file for
        // non-Directfile contents, yet only through the `HTMLMediaElement` once a
        // a sufficient `readyState` has been reached for directfile contents.
        // So let's divide the two possibilities here.
        var initialTime = typeof startTime === "function" ? startTime() :
            startTime;
        if (shouldPreventSeekingAt0Initially() && initialTime === 0) {
            initialSeekPerformed.setValue(true);
            initialSeekPerformed.finish();
        }
        else if (isDirectfile && isSafariMobile) {
            // On safari mobile (version 17.1.2) seeking too early cause the video
            // to never buffer media data. Using setTimeout 0 defers the seek
            // to a moment at which safari should be more able to handle a seek.
            setTimeout(function () {
                performInitialSeek(initialTime);
            }, 0);
        }
        else {
            performInitialSeek(initialTime);
        }
        if (shouldValidateMetadata() && mediaElement.duration === 0) {
            var error = new MediaError("MEDIA_ERR_NOT_LOADED_METADATA", "Cannot load automatically: your browser " +
                "falsely announced having loaded the content.");
            onWarning(error);
        }
        if (cancelSignal.isCancelled()) {
            return;
        }
        /**
         * We only want to continue to `play` when a `seek` has actually been
         * performed (if it has been asked). This boolean keep track of if the
         * seek arised.
         */
        var isAwaitingSeek = hasAskedForInitialSeek;
        playbackObserver.listen(function (observation, stopListening) {
            if (hasAskedForInitialSeek && observation.seeking) {
                isAwaitingSeek = false;
                return;
            }
            if (!isAwaitingSeek &&
                !observation.seeking &&
                observation.rebuffering === null &&
                observation.readyState >= 1) {
                stopListening();
                onPlayable();
            }
        }, { includeLastObservation: true, clearSignal: cancelSignal });
    }
    function onPlayable() {
        var _a;
        log.info("Init: Can begin to play content");
        if (!mustAutoPlay) {
            if (mediaElement.autoplay) {
                log.warn("Init: autoplay is enabled on HTML media element. " +
                    "Media will play as soon as possible.");
            }
            initialPlayPerformed.setValue(true);
            initialPlayPerformed.finish();
            deregisterCancellation();
            return resolveAutoPlay({ type: "skipped" });
        }
        else if (mediaElement.ended) {
            // the video has ended state to true, executing VideoElement.play() will
            // restart the video from the start, which is not wanted in most cases.
            // returning "skipped" prevents the call to play() and fix the issue
            log.warn("Init: autoplay is enabled but the video is ended. " +
                "Skipping autoplay to prevent video to start again");
            initialPlayPerformed.setValue(true);
            initialPlayPerformed.finish();
            deregisterCancellation();
            return resolveAutoPlay({ type: "skipped" });
        }
        var playResult;
        try {
            playResult = (_a = mediaElement.play()) !== null && _a !== void 0 ? _a : Promise.resolve();
        }
        catch (playError) {
            deregisterCancellation();
            return rejectAutoPlay(playError);
        }
        playResult
            .then(function () {
            if (cancelSignal.isCancelled()) {
                return;
            }
            initialPlayPerformed.setValue(true);
            initialPlayPerformed.finish();
            deregisterCancellation();
            return resolveAutoPlay({ type: "autoplay" });
        })
            .catch(function (playError) {
            deregisterCancellation();
            if (cancelSignal.isCancelled()) {
                return;
            }
            if (playError instanceof Error && playError.name === "NotAllowedError") {
                // auto-play was probably prevented.
                log.warn("Init: Media element can't play." +
                    " It may be due to browser auto-play policies.");
                var error = new MediaError("MEDIA_ERR_BLOCKED_AUTOPLAY", "Cannot trigger auto-play automatically: " +
                    "your browser does not allow it.");
                onWarning(error);
                if (cancelSignal.isCancelled()) {
                    return;
                }
                return resolveAutoPlay({ type: "autoplay-blocked" });
            }
            else {
                rejectAutoPlay(playError);
            }
        });
    }
}
