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

import classNames from "classnames";
import IDUtil from "../../util/IDUtil";
import TranscriptUtil from "../../util/TranscriptUtil";
import FlexPlayerUtil from "../../util/FlexPlayerUtil";

import AVPlayer from "./mediaColumn/AVPlayer";
import ImageViewer from "./mediaColumn/ImageViewer";
import PlaylistDropdown from "./mediaColumn/PlaylistDropdown";

import ColumnHeader from "./ColumnHeader";
import BaseColumn from "./BaseColumn";
import Info from "../shared/Info";

import TimelineView from "./mediaColumn/TimelineView";
import MediaEvents from "./_MediaEvents";
import Strings from "./_Strings";

import { AnnotationEvents } from "./AnnotationClient";

import { ResourceViewerContext } from "./ResourceViewerContext";

/*
    This component takes on 3 possible playlists (for video, audio & images). From that it generates"
    - a playlist for given media objects (segments will be added to the timeline; not part of the playlist)
	- viewer for the active media object
    - viewer
        - video: player + timeline
        - audio: player + timeline
        - image: image viewer?

	Playlists for testing

	http://0.0.0.0:5304/tool/resource-viewer?id=FLM77223&cid=eye-desmet-films&st=werk
	http://0.0.0.0:5304/tool/resource-viewer?id=482379@program&cid=nisv-catalogue-aggr&st=%22openbaar%20vervoer%22
	http://0.0.0.0:5304/tool/resource-viewer?id=50263@program&cid=nisv-catalogue-aggr
	http://0.0.0.0:5304/tool/resource-viewer?id=5313446@program&cid=nisv-catalogue-aggr&st=aardkloot
	http://0.0.0.0:5304/tool/resource-viewer?id=49689@program&cid=nisv-catalogue-aggr-18-158
	https://mediasuite.xlab.nl/tool/resource-viewer?id=5176026@program&cid=nisv-catalogue-aggr
	https://mediasuite-test.rdlabs.beeldengeluid.nl/tool/resource-viewer?id=5230684@program&cid=nisv-catalogue-aggr&st=wereld
	http://mediasuite.clariah.nl/tool/resource-viewer?id=498233@program&cid=nisv-catalogue-aggr&st=smaaknu

*/

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

    constructor(props) {
        super(props);
        this.state = {
            playerAPI: null, // called after an AVPlayer has loaded a new mediaObject (via onPlayerReady)
            duration: -1,
            showPlaylist: false,
        };
        //this.imageLabelRef = React.createRef();
    }

    componentDidMount = () => {
        this.context.annotationClient.events.bind(
            AnnotationEvents.ON_SET_SELECTION,
            this.onSetSelection
        );
    };

    componentWillUnmount = () => {
        this.context.mediaEvents.unbind(
            MediaEvents.SET_PLAYER_POS,
            this.onPlayerSeek
        );
        this.context.annotationClient.events.unbind(
            AnnotationEvents.ON_SET_SELECTION,
            this.onSetSelection
        );
    };

    /* ----------------------------- COMPONENT INTERACTION FUNCTIONS ------------------------ */

    setActiveMediaObject = async (mediaObject) => {
        //tell the context to update the active media object
        await this.context.setActiveMediaObject(mediaObject);
        //now notify whoever is interested that a new media object is active!
        this.context.mediaEvents.trigger(
            MediaEvents.ACTIVE_MEDIA_OBJECT,
            mediaObject
        );

        //hide the playlist drop-down
        this.setState({ showPlaylist: false });
    };

    // Receives a playerAPI after the AVPlayer was loaded
    onPlayerReady = (playerAPI) => {
        const resourceStart =
            this.context.activeMediaObject &&
            this.context.activeMediaObject.resourceStart
                ? this.context.activeMediaObject.resourceStart
                : 0;

        //TODO get the active transcript type? (a new context property for the resourceViewer.jsx?)
        const transcript = this.context.getActiveTranscript();
        const transcriptFirstHit = transcript ? this.context.getFirstHitInTranscript(
            this.context.query?.term,
            transcript
        ) : -1;

        if (this.context.urlParams && this.context.urlParams.startTime) {
            playerAPI.playerAPI.currentTime = resourceStart + parseInt(this.context.urlParams.startTime);
        } else if(transcriptFirstHit !== -1) {
            playerAPI.playerAPI.currentTime = resourceStart + transcriptFirstHit;
        } else {
            playerAPI.playerAPI.currentTime = resourceStart;
        }

        this.setState({ playerAPI: playerAPI }, () => {
            // reset, and register the SET_PLAYER_POS event in the mediaEvents
            // so other components can directly control the player
            this.context.mediaEvents
                .reset(MediaEvents.SET_PLAYER_POS)
                .bind(MediaEvents.SET_PLAYER_POS, this.onPlayerSeek);
            this.context.setPlayerAPI(playerAPI);
        });
    };

    // Seek on player and timeline
    onPlayerSeek = (pos) => {
        if (!this.context.activeMediaObject || !this.state.playerAPI) {
            return;
        }
        this.onAirSeek(pos);
    };

    onAirSeek = (pos) => {
        FlexPlayerUtil.seekRelativeToOnAir(
            this.state.playerAPI,
            pos,
            this.context.activeMediaObject
        );
    };

    //Here you can delegate time to other components via a ref (faster than using state)
    onGetPosition = (realPos) => {
        if (this.context.activeMediaObject) {
            // the relative position is used to shield off off-air content
            const relativePosition = FlexPlayerUtil.timeRelativeToOnAir(
                realPos,
                this.context.activeMediaObject
            );

            // Trigger event
            this.context.mediaEvents.trigger(
                MediaEvents.PLAYER_POS,
                relativePosition
            );

            // Trigger RAW event
            this.context.mediaEvents.trigger(
                MediaEvents.PLAYER_POS_RAW,
                realPos
            );
        }
    };

    onGetDuration = (onAirDuration) =>
        this.setState({ duration: onAirDuration }); // whenever the player calculated a duration (required for drawing timeline)

    /* ----------------------------- PLAYLIST FUNCTIONS ------------------------ */

    // Toggle playlist visiblity
    togglePlaylist = () => {
        this.setState({ showPlaylist: !this.state.showPlaylist });
    };

    onPlaylistNext = () => {
        this.navPlaylist(1);
    };

    onPlaylistPrev = () => {
        this.navPlaylist(-1);
    };

    // Navigate playlist
    navPlaylist = (diff) => {
        if (!this.context.activeMediaObject) {
            return;
        }
        const activeId = this.context.activeMediaObject.assetId;
        let activeIndex = 0;
        this.props.mediaObjects.some((mediaObject, index) => {
            if (mediaObject.assetId == activeId) {
                activeIndex = index;
                return true;
            }
            return false;
        });

        // Use diff to set a new active MediaObject
        this.setActiveMediaObject(
            this.props.mediaObjects[
                (activeIndex + diff + this.props.mediaObjects.length) %
                    this.props.mediaObjects.length
            ],
            null
        );
    };

    /* ----------------------------- ANNOTATION CLIENT EVENTS ------------------------ */

    onSetSelection = () => this.forceUpdate();

    /* ----------------------------- RENDER FUNCTIONS ------------------------ */

    //renders the playlist for the list of video/audio mediaObjects (test with: ?id=FLM77223&cid=eye-desmet-films)
    renderAVPlaylist = (
        mediaObjects,
        transcripts, //TODO make sure to think about what to do with non ASR transcripts
        initialSearchTerm,
        activeId
    ) => {
        if (
            !mediaObjects ||
            mediaObjects.length === 0 ||
            (mediaObjects.length === 1 && !mediaObjects[0].segments)
        )
            return null;

        return (
            <PlaylistDropdown
                mediaObjects={mediaObjects}
                transcriptMatches={TranscriptUtil.calcTranscriptMatchesPerMediaObject(
                    mediaObjects,
                    transcripts,
                    initialSearchTerm
                )}
                activeId={activeId}
                onSelect={this.setActiveMediaObject}
            />
        );
    };

    renderImagePlaylist = (mediaObjects, initialSearchTerm) =>
        console.debug("Implement this for flipping through images");

    renderAVPlayer = (activeMediaObject, collectionConfig) => {
        return (
            <AVPlayer
                mediaObject={activeMediaObject}
                useCredentials={activeMediaObject.requiresPlayoutAccess} // whether the player needs to send a cookie or not
                hideOffAirContent={collectionConfig.hideOffAirContent()} // whether to hide start- & end offsets or not
                onPlayerReady={this.onPlayerReady}
                onGetPosition={this.onGetPosition}
                onGetDuration={this.onGetDuration}
            />
        );
    };

    renderUnknownPlayer = (activeMediaObject) => {
        if (!activeMediaObject) {
            return (
                <div className="error">
                    This resource does not have a media object
                    <br />
                </div>
            );
        } else if (activeMediaObject.mimeType === "application/javascript") {
            return (
                <div className="error">
                    Deze media kan i.v.m. beperkingen m.b.t. auteursrecht of het
                    type content niet binnen de media suite worden afgespeeld{" "}
                    <br />
                    <a href={activeMediaObject.url} target="_external_js">
                        Bekijk de media extern
                    </a>
                </div>
            );
        } else {
            return (
                <iframe src={activeMediaObject.url} width="650" height="550" />
            );
        }
    };

    //TODO this is currently being wired up
    renderImageViewer = (activeMediaObject, collectionConfig, mediaObjects) => {
        if (!activeMediaObject || !mediaObjects) return null;
        if (mediaObjects.findIndex((mo) => mo.cors === false) === -1) {
            //image viewer requires CORS
            return (
                //<div><p className="bg__imv__overlay_static" ref={this.imageLabelRef} id="html-overlay">Image ID: {activeMediaObject.assetId}</p>
                <ImageViewer
                    //imageLabelRef={this.imageLabelRef}
                    mediaObjects={mediaObjects} //image viewer uses an internal playlist (for now)
                    useCredentials={collectionConfig.requiresPlayoutAccess()}
                    annotationClient={this.context.annotationClient}
                />
                //</div>
            );
        } else {
            //just return an image tag TODO support for playlist
            //return mediaObjects.map(mo => <img src={mo.url} />);
            return <img src={activeMediaObject.url} />;
        }
    };

    // Get mediatype of mediaObject
    getMediaType(mediaObject) {
        return mediaObject && mediaObject.mimeType
            ? mediaObject.mimeType.split("/")[0]
            : "";
    }

    // Renders a player depending on the given mediaobject
    renderPlayer = (
        activeMediaObject,
        collectionConfig,
        mediaObjects = null
    ) => {
        const renderPlayerError = (msg) => <div className="error">{msg}</div>;

        if (
            collectionConfig.getAnonymousUserRestrictions().prohibitPlayout ===
                true &&
            this.context.user.id === "ANONYMOUS"
        ) {
            return renderPlayerError(
                "To view the content within this collection, you are required to login"
            );
        }
        let player = null;
        switch (this.getMediaType(activeMediaObject)) {
            case "video":
                player = this.renderAVPlayer(
                    activeMediaObject,
                    collectionConfig
                );
                break;
            case "audio":
                player = this.renderAVPlayer(
                    activeMediaObject,
                    collectionConfig
                );
                break;
            case "image":
                player = this.renderImageViewer(
                    activeMediaObject,
                    collectionConfig,
                    mediaObjects
                );
                break;
            case "application":
                player = this.renderUnknownPlayer(activeMediaObject);
                break;
            case "":
                player = renderPlayerError(
                    "No Media Object available for this resource"
                );
                break;
            case undefined:
                player = renderPlayerError(
                    "No Media Object available for this resource"
                );
                break;
            case null:
                player = renderPlayerError(
                    "No Media Object available for this resource"
                );
                break;
            default:
                player = renderPlayerError(
                    "Error: Unknown media type: " + mediaType
                );
                break;
        }
        return player;
    };

    renderTimelineView = (
        mediaObject,
        duration,
        activeAnnotationTypes,
        showAnnotationPopup
    ) => {
        if (!mediaObject || duration == -1) {
            return null;
        }
        return (
            <TimelineView
                mediaObject={mediaObject}
                duration={duration}
                activeAnnotationTypes={activeAnnotationTypes}
                showAnnotationPopup={showAnnotationPopup}
            />
        );
    };

    renderHeader(togglePlaylist, activeAssetId, down, onNext, onPrev) {
        return (
            <ColumnHeader>
                <div className="playlist">
                    {/* Playlist label */}
                    <strong>
                        <Info
                            id="playlist_info"
                            text={Strings.PLAYLIST_HELP}
                            className="black left"
                        />
                        {Strings.PLAYLIST_TITLE}
                    </strong>

                    {/* Active media object (selector) */}
                    <span
                        className={classNames("active", { down })}
                        onClick={togglePlaylist}
                    >
                        {activeAssetId}
                    </span>
                </div>

                {/* Playlist actions */}
                <div className="actions">
                    <button
                        className="btn prev"
                        title={Strings.PLAYLIST_PREV_TITLE}
                        onClick={onPrev}
                    ></button>
                    <button
                        className="btn next"
                        title={Strings.PLAYLIST_NEXT_TITLE}
                        onClick={onNext}
                    ></button>
                </div>
            </ColumnHeader>
        );
    }

    render() {
        // Render player based on the selected media type
        // optionally add a class for locked content
        const player = this.context.activeMediaObject ? (
            <div
                className={classNames("player", {
                    locked:
                        this.context.collectionConfig.requiresPlayoutAccess() &&
                        !this.context.activeMediaObject.playoutAccess,
                })}
            >
                {this.renderPlayer(
                    this.context.activeMediaObject,
                    this.context.collectionConfig,
                    this.props.mediaObjects
                )}
            </div>
        ) : null;

        // Render the timeline
        const timeline =
            this.context.resource &&
            ["video", "audio"].includes(
                this.getMediaType(this.context.activeMediaObject)
            )
                ? this.renderTimelineView(
                      this.context.activeMediaObject,
                      this.state.duration,
                      this.context.activeAnnotationTypes,
                      this.props.showAnnotationPopup
                  )
                : null;

        // Render the playlist dropdown
        const playlist = this.state.showPlaylist
            ? this.renderAVPlaylist(
                  this.props.mediaObjects,
                  this.props.transcripts,
                  this.context.query ? this.context.query.term : null,
                  this.context.activeMediaObject
                      ? this.context.activeMediaObject.assetId
                      : ""
              )
            : null;

        // Only show the header if there are multiple MediaObjects or Segments
        const header =
            this.props.mediaObjects.length > 1
                ? this.renderHeader(
                      this.togglePlaylist,
                      this.context.activeMediaObject
                          ? this.context.activeMediaObject.assetId
                          : null,
                      this.state.showPlaylist,
                      this.onPlaylistNext,
                      this.onPlaylistPrev
                  )
                : null;

        return (
            <BaseColumn className={IDUtil.cssClassName("viewer-column")}>
                {header}

                <div className="column-content">
                    {/* Playlist */}
                    {playlist}

                    {/* Player */}
                    {player}

                    {/* Timeline */}
                    {timeline}
                </div>
            </BaseColumn>
        );
    }
}

MediaColumn.propTypes = {
    mediaObjects: PropTypes.array, // TODO further specify (arrayOf(MediaObject.getProptypes(false)))
    transcripts: PropTypes.object, // All of the resource's transcripts
    showAnnotationPopup: PropTypes.func.isRequired, // Show the annotation popup; called from the timeline
};
