import PropTypes from 'prop-types';
import React from 'react';
import { Platform, StyleSheet, View, SafeAreaView, KeyboardAvoidingView, } from 'react-native';
import { ActionSheetProvider, } from '@expo/react-native-action-sheet';
import uuid from 'uuid';
import { getBottomSpace } from 'react-native-iphone-x-helper';
import dayjs from 'dayjs';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import * as utils from './utils';
import Actions from './Actions';
import Avatar from './Avatar';
import Bubble from './Bubble';
import SystemMessage from './SystemMessage';
import MessageImage from './MessageImage';
import MessageText from './MessageText';
import Composer from './Composer';
import Day from './Day';
import InputToolbar from './InputToolbar';
import LoadEarlier from './LoadEarlier';
import Message from './Message';
import MessageContainer from './MessageContainer';
import Send from './Send';
import Time from './Time';
import GiftedAvatar from './GiftedAvatar';
import { MIN_COMPOSER_HEIGHT, MAX_COMPOSER_HEIGHT, DEFAULT_PLACEHOLDER, TIME_FORMAT, DATE_FORMAT, } from './Constant';
dayjs.extend(localizedFormat);
class GiftedChat extends React.Component {
    constructor(props) {
        super(props);
        this._isMounted = false;
        this._keyboardHeight = 0;
        this._bottomOffset = 0;
        this._maxHeight = undefined;
        this._isFirstLayout = true;
        this._locale = 'en';
        this.invertibleScrollViewProps = undefined;
        this._actionSheetRef = undefined;
        this._messageContainerRef = React.createRef();
        this._isTextInputWasFocused = false;
        this.state = {
            isInitialized: false,
            composerHeight: this.props.minComposerHeight,
            messagesContainerHeight: undefined,
            typingDisabled: false,
            text: undefined,
            messages: undefined,
        };
        this.getLocale = () => this._locale;
        this.safeAreaSupport = (bottomOffset) => (bottomOffset != null ? bottomOffset : getBottomSpace());
        this.onKeyboardWillShow = (e) => {
            this.handleTextInputFocusWhenKeyboardShow();
            if (this.props.isKeyboardInternallyHandled) {
                this.setIsTypingDisabled(true);
                this.setKeyboardHeight(e.endCoordinates ? e.endCoordinates.height : e.end.height);
                this.setBottomOffset(this.safeAreaSupport(this.props.bottomOffset));
                const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard();
                this.setState({
                    messagesContainerHeight: newMessagesContainerHeight,
                });
            }
        };
        this.onKeyboardWillHide = (_e) => {
            this.handleTextInputFocusWhenKeyboardHide();
            if (this.props.isKeyboardInternallyHandled) {
                this.setIsTypingDisabled(true);
                this.setKeyboardHeight(0);
                this.setBottomOffset(0);
                const newMessagesContainerHeight = this.getBasicMessagesContainerHeight();
                this.setState({
                    messagesContainerHeight: newMessagesContainerHeight,
                });
            }
        };
        this.onKeyboardDidShow = (e) => {
            if (Platform.OS === 'android') {
                this.onKeyboardWillShow(e);
            }
            this.setIsTypingDisabled(false);
        };
        this.onKeyboardDidHide = (e) => {
            if (Platform.OS === 'android') {
                this.onKeyboardWillHide(e);
            }
            this.setIsTypingDisabled(false);
        };
        this.onSend = (messages = [], shouldResetInputToolbar = false) => {
            if (!Array.isArray(messages)) {
                messages = [messages];
            }
            const newMessages = messages.map((message) => ({
                ...message,
                user: this.props.user,
                createdAt: new Date(),
                _id: this.props.messageIdGenerator && this.props.messageIdGenerator(),
            }));
            if (shouldResetInputToolbar === true) {
                this.setIsTypingDisabled(true);
                this.resetInputToolbar();
            }
            if (this.props.onSend) {
                this.props.onSend(newMessages);
            }
            if (shouldResetInputToolbar === true) {
                setTimeout(() => {
                    if (this.getIsMounted() === true) {
                        this.setIsTypingDisabled(false);
                    }
                }, 100);
            }
        };
        this.onInputSizeChanged = (size) => {
            const newComposerHeight = Math.max(this.props.minComposerHeight, Math.min(this.props.maxComposerHeight, size.height));
            const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard(newComposerHeight);
            this.setState({
                composerHeight: newComposerHeight,
                messagesContainerHeight: newMessagesContainerHeight,
            });
        };
        this.onInputTextChanged = (text) => {
            if (this.getIsTypingDisabled()) {
                return;
            }
            if (this.props.onInputTextChanged) {
                this.props.onInputTextChanged(text);
            }
            // Only set state if it's not being overridden by a prop.
            if (this.props.text === undefined) {
                this.setState({ text });
            }
        };
        this.onInitialLayoutViewLayout = (e) => {
            const { layout } = e.nativeEvent;
            if (layout.height <= 0) {
                return;
            }
            this.notifyInputTextReset();
            this.setMaxHeight(layout.height);
            const newComposerHeight = this.props.minComposerHeight;
            const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard(newComposerHeight);
            const initialText = this.props.initialText || '';
            this.setState({
                isInitialized: true,
                text: this.getTextFromProp(initialText),
                composerHeight: newComposerHeight,
                messagesContainerHeight: newMessagesContainerHeight,
            });
        };
        this.onMainViewLayout = (e) => {
            // fix an issue when keyboard is dismissing during the initialization
            const { layout } = e.nativeEvent;
            if (this.getMaxHeight() !== layout.height
                || this.getIsFirstLayout() === true) {
                this.setMaxHeight(layout.height);
                this.setState({
                    messagesContainerHeight: this._keyboardHeight > 0
                        ? this.getMessagesContainerHeightWithKeyboard()
                        : this.getBasicMessagesContainerHeight(),
                });
            }
            if (this.getIsFirstLayout() === true) {
                this.setIsFirstLayout(false);
            }
        };
        this.invertibleScrollViewProps = {
            inverted: this.props.inverted,
            keyboardShouldPersistTaps: this.props.keyboardShouldPersistTaps,
            onKeyboardWillShow: this.onKeyboardWillShow,
            onKeyboardWillHide: this.onKeyboardWillHide,
            onKeyboardDidShow: this.onKeyboardDidShow,
            onKeyboardDidHide: this.onKeyboardDidHide,
        };
    }
    static append(currentMessages = [], messages, inverted = true) {
        if (!Array.isArray(messages)) {
            messages = [messages];
        }
        return inverted
            ? messages.concat(currentMessages)
            : currentMessages.concat(messages);
    }
    static prepend(currentMessages = [], messages, inverted = true) {
        if (!Array.isArray(messages)) {
            messages = [messages];
        }
        return inverted
            ? currentMessages.concat(messages)
            : messages.concat(currentMessages);
    }
    getChildContext() {
        return {
            actionSheet: this.props.actionSheet || (() => this._actionSheetRef.getContext()),
            getLocale: this.getLocale,
        };
    }
    componentDidMount() {
        const { messages, text } = this.props;
        this.setIsMounted(true);
        this.initLocale();
        this.setMessages(messages || []);
        this.setTextFromProp(text);
    }
    componentWillUnmount() {
        this.setIsMounted(false);
    }
    componentDidUpdate(prevProps = {}) {
        const { messages, text, inverted } = this.props;
        if (this.props !== prevProps) {
            this.setMessages(messages || []);
        }
        if (inverted === false
            && messages
            && prevProps.messages
            && messages.length !== prevProps.messages.length) {
            setTimeout(() => this.scrollToBottom(false), 200);
        }
        if (text !== prevProps.text) {
            this.setTextFromProp(text);
        }
    }
    initLocale() {
        if (this.props.locale === null) {
            this.setLocale('en');
        }
        else {
            this.setLocale(this.props.locale || 'en');
        }
    }
    setLocale(locale) {
        this._locale = locale;
    }
    setTextFromProp(textProp) {
        // Text prop takes precedence over state.
        if (textProp !== undefined && textProp !== this.state.text) {
            this.setState({ text: textProp });
        }
    }
    getTextFromProp(fallback) {
        if (this.props.text === undefined) {
            return fallback;
        }
        return this.props.text;
    }
    setMessages(messages) {
        this.setState({ messages });
    }
    getMessages() {
        return this.state.messages;
    }
    setMaxHeight(height) {
        this._maxHeight = height;
    }
    getMaxHeight() {
        return this._maxHeight;
    }
    setKeyboardHeight(height) {
        this._keyboardHeight = height;
    }
    getKeyboardHeight() {
        if (Platform.OS === 'android' && !this.props.forceGetKeyboardHeight) {
            // For android: on-screen keyboard resized main container and has own height.
            // @see https://developer.android.com/training/keyboard-input/visibility.html
            // So for calculate the messages container height ignore keyboard height.
            return 0;
        }
        return this._keyboardHeight;
    }
    setBottomOffset(value) {
        this._bottomOffset = value;
    }
    getBottomOffset() {
        return this._bottomOffset;
    }
    setIsFirstLayout(value) {
        this._isFirstLayout = value;
    }
    getIsFirstLayout() {
        return this._isFirstLayout;
    }
    setIsTypingDisabled(value) {
        this.setState({
            typingDisabled: value,
        });
    }
    getIsTypingDisabled() {
        return this.state.typingDisabled;
    }
    setIsMounted(value) {
        this._isMounted = value;
    }
    getIsMounted() {
        return this._isMounted;
    }
    getMinInputToolbarHeight() {
        return this.props.renderAccessory
            ? this.props.minInputToolbarHeight * 2
            : this.props.minInputToolbarHeight;
    }
    calculateInputToolbarHeight(composerHeight) {
        return (composerHeight
            + (this.getMinInputToolbarHeight() - this.props.minComposerHeight));
    }
    /**
     * Returns the height, based on current window size, without taking the keyboard into account.
     */
    getBasicMessagesContainerHeight(composerHeight = this.state.composerHeight) {
        return (this.getMaxHeight() - this.calculateInputToolbarHeight(composerHeight));
    }
    /**
     * Returns the height, based on current window size, taking the keyboard into account.
     */
    getMessagesContainerHeightWithKeyboard(composerHeight = this.state.composerHeight) {
        return (this.getBasicMessagesContainerHeight(composerHeight)
            - this.getKeyboardHeight()
            + this.getBottomOffset());
    }
    /**
     * Store text input focus status when keyboard hide to retrieve
     * it after wards if needed.
     * `onKeyboardWillHide` may be called twice in sequence so we
     * make a guard condition (eg. showing image picker)
     */
    handleTextInputFocusWhenKeyboardHide() {
        if (!this._isTextInputWasFocused) {
            this._isTextInputWasFocused = this.textInput?.isFocused() || false;
        }
    }
    /**
     * Refocus the text input only if it was focused before showing keyboard.
     * This is needed in some cases (eg. showing image picker).
     */
    handleTextInputFocusWhenKeyboardShow() {
        if (this.textInput
            && this._isTextInputWasFocused
            && !this.textInput.isFocused()) {
            this.textInput.focus();
        }
        // Reset the indicator since the keyboard is shown
        this._isTextInputWasFocused = false;
    }
    scrollToBottom(animated = true) {
        if (this._messageContainerRef && this._messageContainerRef.current) {
            const { inverted } = this.props;
            if (!inverted) {
                this._messageContainerRef.current.scrollToEnd({ animated });
            }
            else {
                this._messageContainerRef.current.scrollToOffset({
                    offset: 0,
                    animated,
                });
            }
        }
    }
    renderMessages() {
        const { messagesContainerStyle, ...messagesContainerProps } = this.props;
        const fragment = (<View style={[
            {
                height: this.state.messagesContainerHeight,
            },
            messagesContainerStyle,
        ]}>
	<MessageContainer {...messagesContainerProps} invertibleScrollViewProps={this.invertibleScrollViewProps} messages={this.getMessages()} forwardRef={this._messageContainerRef} isTyping={this.props.isTyping}/>
	{this.renderChatFooter()}
  		</View>);
        return this.props.isKeyboardInternallyHandled ? (<KeyboardAvoidingView enabled>{fragment}</KeyboardAvoidingView>) : (fragment);
    }
    resetInputToolbar() {
        if (this.textInput) {
            this.textInput.clear();
        }
        this.notifyInputTextReset();
        const newComposerHeight = this.props.minComposerHeight;
        const newMessagesContainerHeight = this.getMessagesContainerHeightWithKeyboard(newComposerHeight);
        this.setState({
            text: this.getTextFromProp(''),
            composerHeight: newComposerHeight,
            messagesContainerHeight: newMessagesContainerHeight,
        });
    }
    focusTextInput() {
        if (this.textInput) {
            this.textInput.focus();
        }
    }
    notifyInputTextReset() {
        if (this.props.onInputTextChanged) {
            this.props.onInputTextChanged('');
        }
    }
    renderInputToolbar() {
        const inputToolbarProps = {
            ...this.props,
            text: this.getTextFromProp(this.state.text),
            composerHeight: Math.max(this.props.minComposerHeight, this.state.composerHeight),
            onSend: this.onSend,
            onInputSizeChanged: this.onInputSizeChanged,
            onTextChanged: this.onInputTextChanged,
            textInputProps: {
                ...this.props.textInputProps,
                ref: (textInput) => (this.textInput = textInput),
                maxLength: this.getIsTypingDisabled() ? 0 : this.props.maxInputLength,
            },
        };
        if (this.props.renderInputToolbar) {
            return this.props.renderInputToolbar(inputToolbarProps);
        }
        return <InputToolbar {...inputToolbarProps}/>;
    }
    renderChatFooter() {
        if (this.props.renderChatFooter) {
            return this.props.renderChatFooter();
        }
        return null;
    }
    renderLoading() {
        if (this.props.renderLoading) {
            return this.props.renderLoading();
        }
        return null;
    }
    render() {
        if (this.state.isInitialized === true) {
            const { wrapInSafeArea } = this.props;
            const Wrapper = wrapInSafeArea ? SafeAreaView : View;
            return (<Wrapper style={styles.safeArea}>
	<ActionSheetProvider ref={(component) => (this._actionSheetRef = component)}>
	<View style={styles.container} onLayout={this.onMainViewLayout}>
	{this.renderMessages()}
	{this.renderInputToolbar()}
  					</View>
  				</ActionSheetProvider>
  			</Wrapper>);
        }
        return (<View style={styles.container} onLayout={this.onInitialLayoutViewLayout}>
	{this.renderLoading()}
  		</View>);
    }
}
GiftedChat.childContextTypes = {
    actionSheet: PropTypes.func,
    getLocale: PropTypes.func,
};
GiftedChat.defaultProps = {
    messages: [],
    messagesContainerStyle: undefined,
    text: undefined,
    placeholder: DEFAULT_PLACEHOLDER,
    disableComposer: false,
    messageIdGenerator: () => uuid.v4(),
    user: {},
    onSend: () => { },
    locale: null,
    timeFormat: TIME_FORMAT,
    dateFormat: DATE_FORMAT,
    loadEarlier: false,
    onLoadEarlier: () => { },
    isLoadingEarlier: false,
    renderLoading: null,
    renderLoadEarlier: null,
    renderAvatar: undefined,
    showUserAvatar: false,
    actionSheet: null,
    onPressAvatar: null,
    onLongPressAvatar: null,
    renderUsernameOnMessage: false,
    renderAvatarOnTop: false,
    renderBubble: null,
    renderSystemMessage: null,
    onLongPress: null,
    renderMessage: null,
    renderMessageText: null,
    renderMessageImage: null,
    renderMessageVideo: null,
    renderMessageAudio: null,
    imageProps: {},
    videoProps: {},
    audioProps: {},
    lightboxProps: {},
    textInputProps: {},
    listViewProps: {},
    renderCustomView: null,
    isCustomViewBottom: false,
    renderDay: null,
    renderTime: null,
    renderFooter: null,
    renderChatEmpty: null,
    renderChatFooter: null,
    renderInputToolbar: null,
    renderComposer: null,
    renderActions: null,
    renderSend: null,
    renderAccessory: null,
    isKeyboardInternallyHandled: true,
    onPressActionButton: null,
    bottomOffset: null,
    minInputToolbarHeight: 44,
    keyboardShouldPersistTaps: Platform.select({
        ios: 'never',
        android: 'always',
        default: 'never',
    }),
    onInputTextChanged: null,
    maxInputLength: null,
    forceGetKeyboardHeight: false,
    inverted: true,
    extraData: null,
    minComposerHeight: MIN_COMPOSER_HEIGHT,
    maxComposerHeight: MAX_COMPOSER_HEIGHT,
    wrapInSafeArea: true,
};
GiftedChat.propTypes = {
    messages: PropTypes.arrayOf(PropTypes.object),
    messagesContainerStyle: utils.StylePropType,
    text: PropTypes.string,
    initialText: PropTypes.string,
    placeholder: PropTypes.string,
    disableComposer: PropTypes.bool,
    messageIdGenerator: PropTypes.func,
    user: PropTypes.object,
    onSend: PropTypes.func,
    locale: PropTypes.string,
    timeFormat: PropTypes.string,
    dateFormat: PropTypes.string,
    isKeyboardInternallyHandled: PropTypes.bool,
    loadEarlier: PropTypes.bool,
    onLoadEarlier: PropTypes.func,
    isLoadingEarlier: PropTypes.bool,
    renderLoading: PropTypes.func,
    renderLoadEarlier: PropTypes.func,
    renderAvatar: PropTypes.func,
    showUserAvatar: PropTypes.bool,
    actionSheet: PropTypes.func,
    onPressAvatar: PropTypes.func,
    onLongPressAvatar: PropTypes.func,
    renderUsernameOnMessage: PropTypes.bool,
    renderAvatarOnTop: PropTypes.bool,
    isCustomViewBottom: PropTypes.bool,
    renderBubble: PropTypes.func,
    renderSystemMessage: PropTypes.func,
    onLongPress: PropTypes.func,
    renderMessage: PropTypes.func,
    renderMessageText: PropTypes.func,
    renderMessageImage: PropTypes.func,
    imageProps: PropTypes.object,
    videoProps: PropTypes.object,
    audioProps: PropTypes.object,
    lightboxProps: PropTypes.object,
    renderCustomView: PropTypes.func,
    renderDay: PropTypes.func,
    renderTime: PropTypes.func,
    renderFooter: PropTypes.func,
    renderChatEmpty: PropTypes.func,
    renderChatFooter: PropTypes.func,
    renderInputToolbar: PropTypes.func,
    renderComposer: PropTypes.func,
    renderActions: PropTypes.func,
    renderSend: PropTypes.func,
    renderAccessory: PropTypes.func,
    onPressActionButton: PropTypes.func,
    bottomOffset: PropTypes.number,
    minInputToolbarHeight: PropTypes.number,
    listViewProps: PropTypes.object,
    keyboardShouldPersistTaps: PropTypes.oneOf(['always', 'never', 'handled']),
    onInputTextChanged: PropTypes.func,
    maxInputLength: PropTypes.number,
    forceGetKeyboardHeight: PropTypes.bool,
    inverted: PropTypes.bool,
    textInputProps: PropTypes.object,
    extraData: PropTypes.object,
    minComposerHeight: PropTypes.number,
    maxComposerHeight: PropTypes.number,
    alignTop: PropTypes.bool,
    wrapInSafeArea: PropTypes.bool,
};
const styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    safeArea: {
        flex: 1,
    },
});
export * from './Models';
export { GiftedChat, Actions, Avatar, Bubble, SystemMessage, MessageImage, MessageText, Composer, Day, InputToolbar, LoadEarlier, Message, MessageContainer, Send, Time, GiftedAvatar, utils, };
//# sourceMappingURL=GiftedChat.js.map