import React from 'react';
import PropTypes from 'prop-types';

import MediaObject from '../../../model/MediaObject';

import { SESSION_PLAYER_VOLUME } from '../../player/PlayerAPI';

import HTML5AudioPlayer from '../../player/audio/HTML5AudioPlayer';
import HTML5VideoPlayer from '../../player/video/HTML5VideoPlayer';
import VimeoPlayer from '../../player/video/VimeoPlayer';
import YouTubePlayer from '../../player/video/YouTubePlayer';

import IDUtil from '../../../util/IDUtil';
import ComponentUtil from '../../../util/ComponentUtil';
import FlexPlayerUtil from '../../../util/FlexPlayerUtil';
import RegexUtil from '../../../util/RegexUtil';
import SessionStorageHandler from "../../../util/SessionStorageHandler";

import MediaEvents from '../_MediaEvents';
import { ResourceViewerContext } from '../ResourceViewerContext';

/*
This class receives a (generic) playerAPI from the implementing player component.
Currently VimeoPlayer, JWPlayer, HTML5VideoPlayer, HTML5AudioPlayer and YouTubePlayer have implemented this API.

TODO: check out this new React player: https://github.com/CookPete/react-player

TODO: use the annotationClient to listen to AnnotationEvents.PLAY_ANNOTATION events.
*/

export default class AVPlayer extends React.Component {
    static contextType = ResourceViewerContext;

    constructor(props) {
        super(props);

        this.state = {
            currentMediaObject: this.props.mediaObject,
            currentMediaSegment:
                this.props.mediaObject && this.props.mediaObject.segments
                    ? this.props.mediaObject.segments
                    : null,

            playerAPI: null,

            relativePosition: 0, //player pos relative to on-air start time (e.g. on-air starts at 6:01, real player = 8:01, so relative pos = 2:00)
            realPosition: 0, //real player position
            duration: 0,

            paused: true //FIXME call the player API instead (isPaused)?
        };
    }

    componentDidMount() {
        document.addEventListener('keydown', this.onKeyPressed);
        this.context.mediaEvents.bind(MediaEvents.ACTIVE_MEDIA_OBJECT, this.onMediaObjectSet);
    }

    componentWillUnmount() {
        document.removeEventListener('keydown', this.onKeyPressed);
        this.context.mediaEvents.unbind(MediaEvents.ACTIVE_MEDIA_OBJECT, this.onMediaObjectSet);
    }

    /************************************** Keyboard controls ***************************************/

    onKeyPressed = e => {
        if(e.keyCode >= 49 && e.keyCode <= 57) { //1-9
            e.shiftKey ?
                ComponentUtil.checkFocusAndExec(this.rw, e.keyCode - 48) :
                ComponentUtil.checkFocusAndExec(this.ff, e.keyCode - 48)
            ;
        } else if(e.keyCode === 80 || e.keyCode === 32) {
            ComponentUtil.checkFocusAndExec(this.togglePlay); //p or space
        } else if(e.keyCode === 37) {
            ComponentUtil.checkFocusAndExec(this.rw, 60); //left
        } else if(e.keyCode === 39) {
            ComponentUtil.checkFocusAndExec(this.ff, 60); //right
        }
    };

    onMediaObjectSet = mediaObject => {
        this.setState({
            currentMediaObject: mediaObject,
            currentMediaSegment: this.props.mediaObject && this.props.mediaObject.segments
                    ? this.props.mediaObject.segments
                    : null,
            playerAPI: null
        });
    };

    /*************************************** Player event callbacks ***************************************/

    //called after the underlying player implemtation has loaded a video
    onPlayerReady = playerAPI => {
        //remove this instance as an observer from the old playerAPI
        if (this.state.playerAPI) {
            this.state.playerAPI.removeObserver(this);
        }

        //add this instance as an observer of the new playerAPI
        playerAPI.addObserver(this);

        this.setState({ playerAPI: playerAPI }, () => {
            //get the new duration
            this.state.playerAPI.getDuration(this.onGetDuration);
            this.state.playerAPI.isPaused(paused => {
                this.state.playerAPI.play();
            });

            //propagate to the owner (if any)
            if (this.props.onPlayerReady) {
                this.props.onPlayerReady(playerAPI);
            }
        });

        // restore volume
        const volume = SessionStorageHandler.getInt(SESSION_PLAYER_VOLUME, null);            
        if (volume !== null) {
            playerAPI.setVolume(volume);
        }
        
    };

    playProgress = event => {
        if (this.state.playerAPI) {
            this.state.playerAPI.getPosition(this.onGetPosition);
            if (this.props.onPlayProgress) {
                this.props.onPlayProgress(event);
            }
        }
    };

    onPlay = data => {
        this.setState({ paused: false });
        if (this.props.onPlay) {
            this.props.onPlay(data);
        }
    };

    onPause = paused => {
        this.setState({ paused: true });
        if (this.props.onPause) {
            this.props.onPause(data);
        }
    };

    //TODO test this well! (relative duration)
    onGetDuration = value => {
        if (!isNaN(value)) {
            const onAirDuration = FlexPlayerUtil.onAirDuration(
                value,
                this.state.currentMediaObject
            );
            this.setState(
                {
                    duration: onAirDuration
                },
                () => {
                    if (this.props.onGetDuration) {
                        this.props.onGetDuration(onAirDuration);
                    }
                }
            );
        }
    };

    onGetPosition = value => {
        this.setState({
            relativePosition: FlexPlayerUtil.timeRelativeToOnAir(
                value,
                this.state.currentMediaObject
            ),
            realPosition: value
        });
        if (this.props.onGetPosition) {
            this.props.onGetPosition(value);
        }
    };

    loadProgress = data => {
        if (this.props.onLoadProgress) {
            this.props.onLoadProgress(data);
        }
    };

    onFinish = data => {
        if (this.props.onFinish) {
            this.props.onFinish(data);
        }
    };

    onSeek = data => {
        if (this.props.onSeek) {
            this.props.onSeek(data);
        }
    };

    /************************************** Seek controls ***************************************/

    rw = t => this.__doOnAirSeek(this.state.relativePosition - t);
    ff = t => this.__doOnAirSeek(this.state.relativePosition + t);

    togglePlay = () => {
        if(this.state.paused === false) {//FIXME, this does not work yet!
            this.state.playerAPI.pause();
        } else {
            this.state.playerAPI.play();
        }
    };

    //this is the central seek function of the FlexPlayer and makes sure all seeks take on air content into account
    __doOnAirSeek = time => {
        FlexPlayerUtil.seekRelativeToOnAir(
            this.state.playerAPI,
            time,
            this.state.currentMediaObject
        );
    };

    /* ----------------- MAIN RENDER --------------------------------------------------------- */

    render() {
        const playerEventCallbacks = {
            playProgress: this.playProgress,
            onPlay: this.onPlay,
            onPause: this.onPause,
            onFinish: this.onFinish,
            loadProgress: this.loadProgress,
            onSeek: this.onSeek
        };

        let player = null;

        if (this.state.currentMediaObject) {
            if (
                this.state.currentMediaObject.mimeType.indexOf('video') !== -1
            ) {
                if (
                    this.state.currentMediaObject.url.indexOf(
                        'player.vimeo.com'
                    ) !== -1
                ) {
                    player = ( //TODO adapt for playlist and test!
                        <VimeoPlayer
                            key={
                                'vimeo_player__' +
                                this.state.currentMediaObject.assetId
                            }
                            mediaObject={this.state.currentMediaObject}
                            eventCallbacks={playerEventCallbacks}
                            onPlayerReady={this.onPlayerReady}
                        />
                    );
                } else if (
                    this.state.currentMediaObject.url.indexOf('youtube.com') !==
                        -1 ||
                    this.state.currentMediaObject.url.indexOf('youtu.be') !== -1
                ) {
                    player = ( //TODO adapt for playlists and test!
                        <YouTubePlayer
                            key={
                                'yt_player__' +
                                this.state.currentMediaObject.assetId
                            }
                            mediaObject={this.state.currentMediaObject}
                            eventCallbacks={playerEventCallbacks}
                            onPlayerReady={this.onPlayerReady}
                        />
                    );
                } else {
                    player = (
                        <HTML5VideoPlayer
                            key={
                                'html_v_player__' +
                                this.state.currentMediaObject.assetId
                            }
                            mediaObject={this.state.currentMediaObject}
                            segment={this.state.currentMediaSegment}
                            useCredentials={this.props.useCredentials}
                            hideOffAirContent={this.props.hideOffAirContent}
                            eventCallbacks={playerEventCallbacks}
                            onPlayerReady={this.onPlayerReady}
                        />
                    );
                }
            } else if (
                this.state.currentMediaObject.mimeType.indexOf('audio') !== -1
            ) {
                player = (
                    <HTML5AudioPlayer
                        key={
                            'html_a_player__' +
                            this.state.currentMediaObject.assetId
                        }
                        mediaObject={this.state.currentMediaObject}
                        segment={this.state.currentMediaSegment}
                        useCredentials={this.props.useCredentials}
                        eventCallbacks={playerEventCallbacks}
                        onPlayerReady={this.onPlayerReady}
                    />
                );
            }
        }

        return (
            <div className={IDUtil.cssClassName('flex-player')}>
                <div className="flex-container">
                    <div
                        className="player-container"
                        style={{ overflowX: 'auto' }}
                    >
                        {player}
                    </div>
                </div>
            </div>
        );
    }
}

AVPlayer.propTypes = {
    //only used to set the initial mediaobject
    mediaObject : MediaObject.getPropTypes(),

    //(optional) player callback functions the owner can register to
    onLoadProgress: PropTypes.func,
    onPlay: PropTypes.func,
    onPlayProgress: PropTypes.func,
    onPause: PropTypes.func,
    onFinish: PropTypes.func,
    onSeek: PropTypes.func,
    onPlayerReady: PropTypes.func, //returns the PlayerAPI, so the owner can control the player
    onGetDuration: PropTypes.func,

    useCredentials: PropTypes.bool, //so the player sends all the required cookie information for the playout proxy
    hideOffAirContent: PropTypes.bool //in case the content has to be cut off at a certain start and/or end time
    //TODO add annotationClient
};
