// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Stack, mergeStyles } from '@fluentui/react';
import { videoContainerStyles } from '../styles/VideoTile.styles';
import { getEmojiResource } from './utils/videoGalleryLayoutUtils';
import { generateStartPositionWave, getReactionMovementStyle, getReactionStyleBucket, moveAnimationStyles, opacityAnimationStyles, reactionOverlayStyle, spriteAnimationStyles } from '../styles/ReactionOverlay.style';
import { REACTION_NUMBER_OF_ANIMATION_FRAMES, REACTION_SCREEN_SHARE_ANIMATION_TIME_MS, REACTION_START_DISPLAY_SIZE, getCombinedKey, getReceivedUnixTime } from './utils/reactionUtils';
const MAX_NUMBER_OF_EMOJIS = 50;
const NUMBER_OF_EMOJI_TYPES = 5;
const REACTION_POSITION_ARRAY_SIZE = 55;
/**
 * The overlay responsible for rendering multiple reactions all at once in presentation mode
 * @internal
 */
export const RemoteContentShareReactionOverlay = React.memo((props) => {
    const { reactionResources, localParticipant, remoteParticipants, hostDivHeight, hostDivWidth } = props;
    // Reactions that are currently being animated
    const [visibleReactions, setVisibleReactions] = useState([]);
    // Dictionary of userId to a reaction status. This is used to track the latest received reaction
    // per user to avoid animating the same reaction multiple times and to limit the number of
    // active reactions of a certain type.
    const latestReceivedReaction = useRef({});
    // Track the number of active reactions of each type to limit the number of active reactions
    // of a certain type.
    const activeTypeCount = useRef({
        like: 0,
        heart: 0,
        laugh: 0,
        applause: 0,
        surprised: 0
    });
    // Used to track the total number of reactions ever played. This is a helper variable
    // to calculate the reaction movement index (i.e. the .left position of the reaction)
    const visibleReactionPosition = useRef(new Array(REACTION_POSITION_ARRAY_SIZE).fill(false));
    const remoteParticipantReactions = useMemo(() => { var _a; return (_a = remoteParticipants === null || remoteParticipants === void 0 ? void 0 : remoteParticipants.map(remoteParticipant => remoteParticipant.reaction).filter((reaction) => !!reaction)) !== null && _a !== void 0 ? _a : []; }, [remoteParticipants]);
    const findFirstEmptyPosition = () => {
        return visibleReactionPosition.current.findIndex(item => item === false);
    };
    const updateVisibleReactions = useCallback((reaction, userId) => {
        var _a, _b;
        const combinedKey = getCombinedKey(userId, reaction.reactionType, reaction.receivedOn);
        const alreadyHandled = ((_a = latestReceivedReaction.current[userId]) === null || _a === void 0 ? void 0 : _a.id) === combinedKey;
        if (alreadyHandled) {
            return;
        }
        const activeCount = (_b = activeTypeCount.current[reaction.reactionType]) !== null && _b !== void 0 ? _b : 0;
        if (activeCount >= MAX_NUMBER_OF_EMOJIS / NUMBER_OF_EMOJI_TYPES) {
            latestReceivedReaction.current[userId] = {
                id: combinedKey,
                status: 'ignored'
            };
            return;
        }
        activeTypeCount.current[reaction.reactionType] += 1;
        latestReceivedReaction.current[userId] = {
            id: combinedKey,
            status: 'animating'
        };
        const reactionMovementIndex = findFirstEmptyPosition();
        visibleReactionPosition.current[reactionMovementIndex] = true;
        setVisibleReactions([...visibleReactions, {
                reaction: reaction,
                id: combinedKey,
                reactionMovementIndex: reactionMovementIndex,
                styleBucket: getReactionStyleBucket()
            }]);
        return;
    }, [activeTypeCount, visibleReactions]);
    const removeVisibleReaction = (reactionType, id, index) => {
        setVisibleReactions(visibleReactions.filter(reaction => reaction.id !== id));
        visibleReactionPosition.current[index] = false;
        activeTypeCount.current[reactionType] -= 1;
        Object.entries(latestReceivedReaction.current).forEach(([userId, reaction]) => {
            const userLastReaction = latestReceivedReaction.current[userId];
            if (reaction.id === id && userLastReaction) {
                userLastReaction.status = 'completedAnimating';
            }
        });
    };
    // Update visible reactions when local participant sends a reaction
    useEffect(() => {
        if (localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.reaction) {
            updateVisibleReactions(localParticipant.reaction, localParticipant.userId);
        }
    }, [localParticipant, updateVisibleReactions]);
    // Update visible reactions when remote participants send a reaction
    useEffect(() => {
        remoteParticipants === null || remoteParticipants === void 0 ? void 0 : remoteParticipants.map(participant => {
            if (participant === null || participant === void 0 ? void 0 : participant.reaction) {
                updateVisibleReactions(participant.reaction, participant.userId);
            }
        });
    }, [remoteParticipantReactions, remoteParticipants, updateVisibleReactions]);
    // Note: canRenderReaction shouldn't be needed as we remove the animation on the onAnimationEnd event
    const canRenderReaction = (reaction, id, movementIndex) => {
        // compare current time to reaction.received at and see if more than 4 seconds has elapsed
        const canRender = Date.now() - getReceivedUnixTime(reaction.receivedOn) < REACTION_SCREEN_SHARE_ANIMATION_TIME_MS;
        // Clean up the reaction if it's not in the visible reaction list
        if (!canRender) {
            removeVisibleReaction(reaction === null || reaction === void 0 ? void 0 : reaction.reactionType, id, movementIndex);
        }
        return canRender;
    };
    const styleBucket = () => getReactionStyleBucket();
    const displaySizePx = () => REACTION_START_DISPLAY_SIZE * styleBucket().sizeScale;
    const containerHeight = hostDivHeight !== null && hostDivHeight !== void 0 ? hostDivHeight : 0;
    const containerWidth = (hostDivWidth !== null && hostDivWidth !== void 0 ? hostDivWidth : 0) - displaySizePx();
    const leftPosition = (position) => generateStartPositionWave(position, containerWidth / 2, true);
    const reactionMovementStyle = (position) => getReactionMovementStyle(leftPosition(position));
    return React.createElement(Stack, { className: mergeStyles(videoContainerStyles, {
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: 'transparent'
        }) }, visibleReactions.map(reaction => {
        var _a;
        return React.createElement("div", { key: reaction.id, style: reactionOverlayStyle },
            React.createElement("div", { className: "reaction-item" }, canRenderReaction(reaction.reaction, reaction.id, reaction.reactionMovementIndex) &&
                // First div - Section that fixes the travel height and applies the movement animation
                // Second div - Keeps track of active sprites and responsible for marking, counting
                //              and removing reactions. Responsible for opacity controls as the sprite emoji animates
                // Third div - Responsible for calculating the point of X axis where the reaction will start animation
                // Fourth div - Play Animation as the other animation applies on the base play animation for the sprite
                React.createElement("div", { style: moveAnimationStyles(containerHeight / 2, 
                    // dividing by two because reactionOverlayStyle height is set to 50%
                    containerHeight / 2 * (1 - reaction.styleBucket.heightMaxScale)) },
                    React.createElement("div", { onAnimationEnd: () => {
                            removeVisibleReaction(reaction.reaction.reactionType, reaction.id, reaction.reactionMovementIndex);
                        }, style: opacityAnimationStyles(reaction.styleBucket.opacityMax) },
                        React.createElement("div", { style: reactionMovementStyle(reaction.reactionMovementIndex) },
                            React.createElement("div", { style: spriteAnimationStyles(REACTION_NUMBER_OF_ANIMATION_FRAMES, displaySizePx(), (_a = getEmojiResource(reaction === null || reaction === void 0 ? void 0 : reaction.reaction.reactionType, reactionResources)) !== null && _a !== void 0 ? _a : '') }))))));
    }));
});
//# sourceMappingURL=RemoteContentShareReactionOverlay.js.map