import Event from '../../event/Event.js';
import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
import HTMLElement from '../html-element/HTMLElement.js';
import TimeRanges from './TimeRanges.js';
import DOMTokenList from '../../dom/DOMTokenList.js';
import MediaStream from './MediaStream.js';
import TextTrackKindEnum from './TextTrackKindEnum.js';
import * as PropertySymbol from '../../PropertySymbol.js';
import ElementEventAttributeUtility from '../element/ElementEventAttributeUtility.js';
/**
 * HTML Media Element.
 *
 * Reference:
 * https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
 *
 */
export default class HTMLMediaElement extends HTMLElement {
    // Internal Properties
    [PropertySymbol.volume] = 1;
    [PropertySymbol.paused] = true;
    [PropertySymbol.currentTime] = 0;
    [PropertySymbol.playbackRate] = 1;
    [PropertySymbol.defaultPlaybackRate] = 1;
    [PropertySymbol.muted] = false;
    [PropertySymbol.defaultMuted] = false;
    [PropertySymbol.preservesPitch] = true;
    [PropertySymbol.buffered] = new TimeRanges(PropertySymbol.illegalConstructor);
    [PropertySymbol.duration] = NaN;
    [PropertySymbol.error] = null;
    [PropertySymbol.ended] = false;
    [PropertySymbol.networkState] = 0;
    [PropertySymbol.readyState] = 0;
    [PropertySymbol.seeking] = false;
    [PropertySymbol.seekable] = new TimeRanges(PropertySymbol.illegalConstructor);
    [PropertySymbol.sinkId] = '';
    [PropertySymbol.played] = new TimeRanges(PropertySymbol.illegalConstructor);
    [PropertySymbol.remote] = new this[PropertySymbol.window].RemotePlayback();
    [PropertySymbol.controlsList] = null;
    [PropertySymbol.mediaKeys] = null;
    [PropertySymbol.srcObject] = null;
    [PropertySymbol.textTracks] = [];
    // Events
    /* eslint-disable jsdoc/require-jsdoc */
    get onabort() {
        return ElementEventAttributeUtility.getEventListener(this, 'onabort');
    }
    set onabort(value) {
        this[PropertySymbol.propertyEventListeners].set('onabort', value);
    }
    get oncanplay() {
        return ElementEventAttributeUtility.getEventListener(this, 'oncanplay');
    }
    set oncanplay(value) {
        this[PropertySymbol.propertyEventListeners].set('oncanplay', value);
    }
    get oncanplaythrough() {
        return ElementEventAttributeUtility.getEventListener(this, 'oncanplaythrough');
    }
    set oncanplaythrough(value) {
        this[PropertySymbol.propertyEventListeners].set('oncanplaythrough', value);
    }
    get ondurationchange() {
        return ElementEventAttributeUtility.getEventListener(this, 'ondurationchange');
    }
    set ondurationchange(value) {
        this[PropertySymbol.propertyEventListeners].set('ondurationchange', value);
    }
    get onemptied() {
        return ElementEventAttributeUtility.getEventListener(this, 'onemptied');
    }
    set onemptied(value) {
        this[PropertySymbol.propertyEventListeners].set('onemptied', value);
    }
    get onended() {
        return ElementEventAttributeUtility.getEventListener(this, 'onended');
    }
    set onended(value) {
        this[PropertySymbol.propertyEventListeners].set('onended', value);
    }
    get onloadeddata() {
        return ElementEventAttributeUtility.getEventListener(this, 'onloadeddata');
    }
    set onloadeddata(value) {
        this[PropertySymbol.propertyEventListeners].set('onloadeddata', value);
    }
    get onloadedmetadata() {
        return ElementEventAttributeUtility.getEventListener(this, 'onloadedmetadata');
    }
    set onloadedmetadata(value) {
        this[PropertySymbol.propertyEventListeners].set('onloadedmetadata', value);
    }
    get onloadstart() {
        return ElementEventAttributeUtility.getEventListener(this, 'onloadstart');
    }
    set onloadstart(value) {
        this[PropertySymbol.propertyEventListeners].set('onloadstart', value);
    }
    get onpause() {
        return ElementEventAttributeUtility.getEventListener(this, 'onpause');
    }
    set onpause(value) {
        this[PropertySymbol.propertyEventListeners].set('onpause', value);
    }
    get onplay() {
        return ElementEventAttributeUtility.getEventListener(this, 'onplay');
    }
    set onplay(value) {
        this[PropertySymbol.propertyEventListeners].set('onplay', value);
    }
    get onplaying() {
        return ElementEventAttributeUtility.getEventListener(this, 'onplaying');
    }
    set onplaying(value) {
        this[PropertySymbol.propertyEventListeners].set('onplaying', value);
    }
    get onprogress() {
        return ElementEventAttributeUtility.getEventListener(this, 'onprogress');
    }
    set onprogress(value) {
        this[PropertySymbol.propertyEventListeners].set('onprogress', value);
    }
    get onratechange() {
        return ElementEventAttributeUtility.getEventListener(this, 'onratechange');
    }
    set onratechange(value) {
        this[PropertySymbol.propertyEventListeners].set('onratechange', value);
    }
    get onresize() {
        return ElementEventAttributeUtility.getEventListener(this, 'onresize');
    }
    set onresize(value) {
        this[PropertySymbol.propertyEventListeners].set('onresize', value);
    }
    get onseeked() {
        return ElementEventAttributeUtility.getEventListener(this, 'onseeked');
    }
    set onseeked(value) {
        this[PropertySymbol.propertyEventListeners].set('onseeked', value);
    }
    get onseeking() {
        return ElementEventAttributeUtility.getEventListener(this, 'onseeking');
    }
    set onseeking(value) {
        this[PropertySymbol.propertyEventListeners].set('onseeking', value);
    }
    get onstalled() {
        return ElementEventAttributeUtility.getEventListener(this, 'onstalled');
    }
    set onstalled(value) {
        this[PropertySymbol.propertyEventListeners].set('onstalled', value);
    }
    get onsuspend() {
        return ElementEventAttributeUtility.getEventListener(this, 'onsuspend');
    }
    set onsuspend(value) {
        this[PropertySymbol.propertyEventListeners].set('onsuspend', value);
    }
    get ontimeupdate() {
        return ElementEventAttributeUtility.getEventListener(this, 'ontimeupdate');
    }
    set ontimeupdate(value) {
        this[PropertySymbol.propertyEventListeners].set('ontimeupdate', value);
    }
    get onvolumechange() {
        return ElementEventAttributeUtility.getEventListener(this, 'onvolumechange');
    }
    set onvolumechange(value) {
        this[PropertySymbol.propertyEventListeners].set('onvolumechange', value);
    }
    get onwaiting() {
        return ElementEventAttributeUtility.getEventListener(this, 'onwaiting');
    }
    set onwaiting(value) {
        this[PropertySymbol.propertyEventListeners].set('onwaiting', value);
    }
    /* eslint-enable jsdoc/require-jsdoc */
    /**
     * Returns buffered.
     *
     * @returns Buffered.
     */
    get buffered() {
        return this[PropertySymbol.buffered];
    }
    /**
     * Returns duration.
     *
     * @returns Duration.
     */
    get duration() {
        return this[PropertySymbol.duration];
    }
    /**
     * Returns error.
     *
     * @returns Error.
     */
    get error() {
        return this[PropertySymbol.error];
    }
    /**
     * Returns ended.
     *
     * @returns Ended.
     */
    get ended() {
        return this[PropertySymbol.ended];
    }
    /**
     * Returns networkState.
     *
     * @returns NetworkState.
     */
    get networkState() {
        return this[PropertySymbol.networkState];
    }
    /**
     * Returns readyState.
     *
     * @returns ReadyState.
     */
    get readyState() {
        return this[PropertySymbol.readyState];
    }
    /**
     * Return a RemotePlayback object instance associated with the media element.
     *
     * @returns RemotePlayback.
     */
    get remote() {
        return this[PropertySymbol.remote];
    }
    /**
     * Returns seeking.
     *
     * @returns Seeking.
     */
    get seeking() {
        return this[PropertySymbol.seeking];
    }
    /**
     * Returns seekable.
     *
     * @returns Seekable.
     */
    get seekable() {
        return this[PropertySymbol.seekable];
    }
    /**
     * Returns sinkId.
     *
     * @returns SinkId.
     */
    get sinkId() {
        return this[PropertySymbol.sinkId];
    }
    /**
     * Returns played.
     *
     * @returns Played.
     */
    get played() {
        return this[PropertySymbol.played];
    }
    /**
     * Returns autoplay.
     *
     * @returns Autoplay.
     */
    get autoplay() {
        return this.getAttribute('autoplay') !== null;
    }
    /**
     * Sets autoplay.
     *
     * @param autoplay Autoplay.
     */
    set autoplay(autoplay) {
        if (!autoplay) {
            this.removeAttribute('autoplay');
        }
        else {
            this.setAttribute('autoplay', '');
        }
    }
    /**
     * Returns controls.
     *
     * @returns Controls.
     */
    get controls() {
        return this.getAttribute('controls') !== null;
    }
    /**
     * Sets controls.
     *
     * @param controls Controls.
     */
    set controls(controls) {
        if (!controls) {
            this.removeAttribute('controls');
        }
        else {
            this.setAttribute('controls', '');
        }
    }
    /**
     * Returns loop.
     *
     * @returns Loop.
     */
    get loop() {
        return this.getAttribute('loop') !== null;
    }
    /**
     * Sets loop.
     *
     * @param loop Loop.
     */
    set loop(loop) {
        if (!loop) {
            this.removeAttribute('loop');
        }
        else {
            this.setAttribute('loop', '');
        }
    }
    /**
     * Returns preload.
     *
     * @returns preload.
     */
    get preload() {
        return this.getAttribute('preload') || 'auto';
    }
    /**
     * Sets preload.
     *
     * @param preload preload.
     */
    set preload(preload) {
        this.setAttribute('preload', preload);
    }
    /**
     * Returns src.
     *
     * @returns Src.
     */
    get src() {
        if (!this.hasAttribute('src')) {
            return '';
        }
        try {
            return new URL(this.getAttribute('src'), this[PropertySymbol.ownerDocument].location.href)
                .href;
        }
        catch (e) {
            return this.getAttribute('src');
        }
    }
    /**
     * Sets src.
     *
     * @param src Src.
     */
    set src(src) {
        this.setAttribute('src', src);
        if (Boolean(src)) {
            this.dispatchEvent(new Event('canplay', { bubbles: false, cancelable: false }));
            this.dispatchEvent(new Event('durationchange', { bubbles: false, cancelable: false }));
        }
    }
    /**
     * Returns controlsList.
     *
     * @returns ControlsList.
     */
    get controlsList() {
        if (this[PropertySymbol.controlsList] === null) {
            this[PropertySymbol.controlsList] = new DOMTokenList(PropertySymbol.illegalConstructor, this, 'controlslist');
        }
        return this[PropertySymbol.controlsList];
    }
    /**
     * Sets controlsList.
     *
     * @param value Value.
     */
    set controlsList(value) {
        this.setAttribute('controlslist', value);
    }
    /**
     * Returns mediaKeys.
     *
     * @returns MediaKeys.
     */
    get mediaKeys() {
        return this[PropertySymbol.mediaKeys];
    }
    /**
     * Returns muted.
     *
     * @returns Muted.
     */
    get muted() {
        if (this[PropertySymbol.muted]) {
            return this[PropertySymbol.muted];
        }
        if (!this[PropertySymbol.defaultMuted]) {
            return this.getAttribute('muted') !== null;
        }
        return false;
    }
    /**
     * Sets muted.
     *
     * @param muted Muted.
     */
    set muted(muted) {
        this[PropertySymbol.muted] = !!muted;
        if (!muted && !this[PropertySymbol.defaultMuted]) {
            this.removeAttribute('muted');
        }
        else {
            this.setAttribute('muted', '');
        }
    }
    /**
     * Returns defaultMuted.
     *
     * @returns DefaultMuted.
     */
    get defaultMuted() {
        return this[PropertySymbol.defaultMuted];
    }
    /**
     * Sets defaultMuted.
     *
     * @param defaultMuted DefaultMuted.
     */
    set defaultMuted(defaultMuted) {
        this[PropertySymbol.defaultMuted] = !!defaultMuted;
        if (!this[PropertySymbol.defaultMuted] && !this[PropertySymbol.muted]) {
            this.removeAttribute('muted');
        }
        else {
            this.setAttribute('muted', '');
        }
    }
    /**
     * Returns disableRemotePlayback.
     *
     * @returns DisableRemotePlayback.
     */
    get disableRemotePlayback() {
        return this.getAttribute('disableremoteplayback') !== null;
    }
    /**
     * Sets disableRemotePlayback.
     *
     * @param disableRemotePlayback DisableRemotePlayback.
     */
    set disableRemotePlayback(disableRemotePlayback) {
        if (!disableRemotePlayback) {
            this.removeAttribute('disableremoteplayback');
        }
        else {
            this.setAttribute('disableremoteplayback', '');
        }
    }
    /**
     * A MediaStream representing the media to play or that has played in the current HTMLMediaElement, or null if not assigned.
     *
     * @returns MediaStream.
     */
    get srcObject() {
        return this[PropertySymbol.srcObject];
    }
    /**
     * Sets src object.
     *
     * @param srcObject SrcObject.
     */
    set srcObject(srcObject) {
        if (srcObject !== null && !(srcObject instanceof MediaStream)) {
            throw new this[PropertySymbol.window].TypeError(`Failed to set the 'srcObject' property on 'HTMLMediaElement': The provided value is not of type 'MediaStream'.`);
        }
        this[PropertySymbol.srcObject] = srcObject;
    }
    /**
     * Returns text track list.
     *
     * @returns Text track list.
     */
    get textTracks() {
        const items = [];
        for (const track of this[PropertySymbol.textTracks]) {
            items.push(track);
        }
        for (const track of this.querySelectorAll('track')[PropertySymbol.items]) {
            items.push(track.track);
        }
        return new this[PropertySymbol.window].TextTrackList(PropertySymbol.illegalConstructor, items);
    }
    /**
     * Returns currentSrc.
     *
     * @returns CurrentrSrc.
     */
    get currentSrc() {
        const src = this.src;
        if (src) {
            return src;
        }
        const sourceElement = this.querySelector('source');
        return sourceElement ? sourceElement.src : '';
    }
    /**
     * Returns volume.
     *
     * @returns Volume.
     */
    get volume() {
        return this[PropertySymbol.volume];
    }
    /**
     * Sets volume.
     *
     * @param volume Volume.
     */
    set volume(volume) {
        const parsedVolume = Number(volume);
        if (isNaN(parsedVolume)) {
            throw new this[PropertySymbol.window].TypeError(`Failed to set the 'volume' property on 'HTMLMediaElement': The provided double value is non-finite.`);
        }
        if (parsedVolume < 0 || parsedVolume > 1) {
            throw new this[PropertySymbol.window].DOMException(`Failed to set the 'volume' property on 'HTMLMediaElement': The volume provided (${parsedVolume}) is outside the range [0, 1].`, DOMExceptionNameEnum.indexSizeError);
        }
        // TODO: volumechange event https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volumechange_event
        this[PropertySymbol.volume] = parsedVolume;
    }
    /**
     * Returns crossOrigin.
     *
     * @returns CrossOrigin.
     */
    get crossOrigin() {
        const crossOrigin = this.getAttribute('crossorigin');
        if (crossOrigin === 'use-credentials') {
            return 'use-credentials';
        }
        if (crossOrigin !== null) {
            return 'anonymous';
        }
        return null;
    }
    /**
     * Sets crossOrigin.
     *
     * @param crossOrigin CrossOrigin.
     */
    set crossOrigin(crossOrigin) {
        this.setAttribute('crossorigin', crossOrigin);
    }
    /**
     * Returns currentTime.
     *
     * @returns CurrentTime.
     */
    get currentTime() {
        return this[PropertySymbol.currentTime];
    }
    /**
     * Sets currentTime.
     *
     * @param currentTime CurrentTime.
     */
    set currentTime(currentTime) {
        const parsedCurrentTime = Number(currentTime);
        if (isNaN(parsedCurrentTime)) {
            throw new this[PropertySymbol.window].TypeError(`Failed to set the 'currentTime' property on 'HTMLMediaElement': The provided double value is non-finite.`);
        }
        this[PropertySymbol.currentTime] = parsedCurrentTime;
    }
    /**
     * Returns playbackRate.
     *
     * @returns PlaybackRate.
     */
    get playbackRate() {
        return this[PropertySymbol.playbackRate];
    }
    /**
     * Sets playbackRate.
     *
     * @param playbackRate PlaybackRate.
     */
    set playbackRate(playbackRate) {
        const parsedPlaybackRate = Number(playbackRate);
        if (isNaN(parsedPlaybackRate)) {
            throw new this[PropertySymbol.window].TypeError(`Failed to set the 'playbackRate' property on 'HTMLMediaElement': The provided double value is non-finite.`);
        }
        this[PropertySymbol.playbackRate] = parsedPlaybackRate;
    }
    /**
     * Returns defaultPlaybackRate.
     *
     * @returns DefaultPlaybackRate.
     */
    get defaultPlaybackRate() {
        return this[PropertySymbol.defaultPlaybackRate];
    }
    /**
     * Sets defaultPlaybackRate.
     *
     * @param defaultPlaybackRate DefaultPlaybackRate.
     */
    set defaultPlaybackRate(defaultPlaybackRate) {
        const parsedDefaultPlaybackRate = Number(defaultPlaybackRate);
        if (isNaN(parsedDefaultPlaybackRate)) {
            throw new this[PropertySymbol.window].TypeError(`Failed to set the 'defaultPlaybackRate' property on 'HTMLMediaElement': The provided double value is non-finite.`);
        }
        this[PropertySymbol.defaultPlaybackRate] = parsedDefaultPlaybackRate;
    }
    /**
     * Returns preservesPitch.
     *
     * @returns PlaybackRate.
     */
    get preservesPitch() {
        return this[PropertySymbol.preservesPitch];
    }
    /**
     * Sets preservesPitch.
     *
     * @param preservesPitch PreservesPitch.
     */
    set preservesPitch(preservesPitch) {
        this[PropertySymbol.preservesPitch] = Boolean(preservesPitch);
    }
    /**
     * Returns paused.
     *
     * @returns Paused.
     */
    get paused() {
        return this[PropertySymbol.paused];
    }
    /**
     * @override
     */
    get tabIndex() {
        const tabIndex = this.getAttribute('tabindex');
        if (tabIndex !== null) {
            const parsed = Number(tabIndex);
            return isNaN(parsed) ? 0 : parsed;
        }
        return 0;
    }
    /**
     * @override
     */
    set tabIndex(tabIndex) {
        super.tabIndex = tabIndex;
    }
    /**
     * Adds a new text track to the media element.
     *
     * @param kind The kind of text track.
     * @param label The label of the text track.
     * @param language The language of the text track data.
     */
    addTextTrack(kind, label, language) {
        const window = this[PropertySymbol.window];
        if (arguments.length === 0) {
            throw new window.TypeError(`Failed to execute 'addTextTrack' on 'HTMLMediaElement': 1 argument required, but only 0 present.`);
        }
        if (!TextTrackKindEnum[kind]) {
            throw new window.TypeError(`Failed to execute 'addTextTrack' on 'HTMLMediaElement': The provided value '${kind}' is not a valid enum value of type TextTrackKind.`);
        }
        const track = new window.TextTrack(PropertySymbol.illegalConstructor);
        track[PropertySymbol.kind] = kind;
        track[PropertySymbol.label] = label || '';
        track[PropertySymbol.language] = language || '';
        this[PropertySymbol.textTracks].push(track);
        return track;
    }
    /**
     * Pause played media.
     */
    pause() {
        if (this[PropertySymbol.paused]) {
            return;
        }
        this[PropertySymbol.paused] = true;
        this.dispatchEvent(new Event('pause', { bubbles: false, cancelable: false }));
    }
    /**
     * Start playing media.
     */
    async play() {
        if (!this[PropertySymbol.paused]) {
            return;
        }
        this[PropertySymbol.paused] = false;
        this.dispatchEvent(new Event('play', { bubbles: false, cancelable: false }));
        this.dispatchEvent(new Event('playing', { bubbles: false, cancelable: false }));
    }
    /**
     * Reports how likely it is that the current browser will be able to play media of a given MIME type.
     *
     * @param _type MIME type.
     * @returns Can play type.
     */
    canPlayType(_type) {
        // TODO: Implement this method
        return '';
    }
    /**
     * Quickly seeks the media to the new time with precision tradeoff.
     *
     * @param _time Time.
     */
    fastSeek(_time) {
        // TODO: Implement this method
    }
    /**
     * Load media.
     */
    load() {
        this.dispatchEvent(new Event('emptied', { bubbles: false, cancelable: false }));
    }
    /**
     * Sets media keys.
     *
     * @param mediaKeys MediaKeys.
     * @returns Promise.
     */
    async setMediaKeys(mediaKeys) {
        this[PropertySymbol.mediaKeys] = mediaKeys;
    }
    /**
     * Sets sink id.
     *
     * @param sinkId SinkId.
     * @returns Promise.
     */
    async setSinkId(sinkId) {
        this[PropertySymbol.sinkId] = sinkId;
    }
    /**
     * Returns MediaStream, captures a stream of the media content.
     *
     * @returns MediaStream.
     */
    captureStream() {
        return new this[PropertySymbol.window].MediaStream();
    }
    /**
     * @override
     */
    [PropertySymbol.cloneNode](deep = false) {
        return super[PropertySymbol.cloneNode](deep);
    }
}
//# sourceMappingURL=HTMLMediaElement.js.map