// @flow

import React from 'react';

import AbstractMessageContainer, { type Props }
    from '../AbstractMessageContainer';

import ChatMessageGroup from './ChatMessageGroup';

/**
 * Displays all received chat messages, grouped by sender.
 *
 * @extends AbstractMessageContainer
 */
export default class MessageContainer extends AbstractMessageContainer {
    /**
     * Whether or not chat has been scrolled to the bottom of the screen. Used
     * to determine if chat should be scrolled automatically to the bottom when
     * the {@code ChatInput} resizes.
     */
    _isScrolledToBottom: boolean;

    /**
     * Reference to the HTML element at the end of the list of displayed chat
     * messages. Used for scrolling to the end of the chat messages.
     */
    _messagesListEndRef: Object;

    /**
     * A React ref to the HTML element containing all {@code ChatMessageGroup}
     * instances.
     */
    _messageListRef: Object;

    /**
     * Initializes a new {@code MessageContainer} instance.
     *
     * @param {Props} props - The React {@code Component} props to initialize
     * the new {@code MessageContainer} instance with.
     */
    constructor(props: Props) {
        super(props);

        this._isScrolledToBottom = true;

        this._messageListRef = React.createRef();
        this._messagesListEndRef = React.createRef();

        this._onChatScroll = this._onChatScroll.bind(this);
    }

    /**
     * Implements {@code Component#render}.
     *
     * @inheritdoc
     */
    render() {
        const groupedMessages = this._getMessagesGroupedBySender();
        const messages = groupedMessages.map((group, index) => {
            const messageType = group[0] && group[0].messageType;

            return (
                <ChatMessageGroup
                    className = { messageType || 'remote' }
                    key = { index }
                    messages = { group } />
            );
        });

        return (
            <div
                id = 'chatconversation'
                onScroll = { this._onChatScroll }
                ref = { this._messageListRef }>
                { messages }
                <div ref = { this._messagesListEndRef } />
            </div>
        );
    }

    /**
     * Scrolls to the bottom again if the instance had previously been scrolled
     * to the bottom. This method is used when a resize has occurred below the
     * instance and bottom scroll needs to be maintained.
     *
     * @returns {void}
     */
    maybeUpdateBottomScroll() {
        if (this._isScrolledToBottom) {
            this.scrollToBottom(false);
        }
    }

    /**
     * Automatically scrolls the displayed chat messages down to the latest.
     *
     * @param {boolean} withAnimation - Whether or not to show a scrolling
     * animation.
     * @returns {void}
     */
    scrollToBottom(withAnimation: boolean) {
        this._messagesListEndRef.current.scrollIntoView({
            behavior: withAnimation ? 'smooth' : 'auto'
        });
    }

    _getMessagesGroupedBySender: () => Array<Array<Object>>;

    _onChatScroll: () => void;

    /**
     * Callback invoked to listen to the current scroll location.
     *
     * @private
     * @returns {void}
     */
    _onChatScroll() {
        const element = this._messageListRef.current;

        this._isScrolledToBottom
            = element.scrollHeight - element.scrollTop === element.clientHeight;
    }
}
