// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
    Alert,
    AppState,
    BackHandler,
    findNodeHandle,
    Keyboard,
    NativeModules,
    Platform,
    Text,
    TouchableOpacity,
    ScrollView,
    View,
    Image,
} from 'react-native';
import {intlShape} from 'react-intl';
import RNFetchBlob from 'rn-fetch-blob';
import Button from 'react-native-button';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import slashForwardBoxIcon from 'assets/images/icons/slash-forward-box.png';

import {General, RequestStatus} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils';

import FileUploadButton from './components/file_upload_button';
import ImageUploadButton from './components/image_upload_button';
import CameraButton from './components/camera_button';
import FormattedMarkdownText from 'app/components/formatted_markdown_text';
import FormattedText from 'app/components/formatted_text';
import PasteableTextInput from 'app/components/pasteable_text_input';
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
import SendButton from 'app/components/send_button';
import {INSERT_TO_COMMENT, INSERT_TO_DRAFT, IS_REACTION_REGEX, MAX_FILE_COUNT, ICON_SIZE} from 'app/constants/post_textbox';
import {NOTIFY_ALL_MEMBERS} from 'app/constants/view';
import FileUploadPreview from 'app/components/file_upload_preview';

import EphemeralStore from 'app/store/ephemeral_store';
import {t} from 'app/utils/i18n';
import {confirmOutOfOfficeDisabled} from 'app/utils/status';
import {
    changeOpacity,
    makeStyleSheetFromTheme,
    getKeyboardAppearanceFromTheme,
} from 'app/utils/theme';

const {RNTextInputReset} = NativeModules;
const INPUT_LINE_HEIGHT = 20;
const EXTRA_INPUT_PADDING = 3;

export default class PostTextBoxBase extends PureComponent {
    static propTypes = {
        actions: PropTypes.shape({
            addReactionToLatestPost: PropTypes.func.isRequired,
            createPost: PropTypes.func.isRequired,
            executeCommand: PropTypes.func.isRequired,
            handleCommentDraftChanged: PropTypes.func.isRequired,
            handlePostDraftChanged: PropTypes.func.isRequired,
            handleClearFiles: PropTypes.func.isRequired,
            handleClearFailedFiles: PropTypes.func.isRequired,
            handleRemoveLastFile: PropTypes.func.isRequired,
            initUploadFiles: PropTypes.func.isRequired,
            userTyping: PropTypes.func.isRequired,
            handleCommentDraftSelectionChanged: PropTypes.func.isRequired,
            setStatus: PropTypes.func.isRequired,
            selectPenultimateChannel: PropTypes.func.isRequired,
            getChannelTimezones: PropTypes.func.isRequired,
        }).isRequired,
        canUploadFiles: PropTypes.bool.isRequired,
        channelId: PropTypes.string.isRequired,
        channelDisplayName: PropTypes.string,
        channelTeamId: PropTypes.string.isRequired,
        channelIsLoading: PropTypes.bool,
        channelIsReadOnly: PropTypes.bool.isRequired,
        currentUserId: PropTypes.string.isRequired,
        deactivatedChannel: PropTypes.bool.isRequired,
        files: PropTypes.array,
        maxFileSize: PropTypes.number.isRequired,
        maxMessageLength: PropTypes.number.isRequired,
        rootId: PropTypes.string,
        theme: PropTypes.object.isRequired,
        uploadFileRequestStatus: PropTypes.string.isRequired,
        value: PropTypes.string.isRequired,
        userIsOutOfOffice: PropTypes.bool.isRequired,
        channelIsArchived: PropTypes.bool,
        onCloseChannel: PropTypes.func,
        cursorPositionEvent: PropTypes.string,
        valueEvent: PropTypes.string,
        currentChannelMembersCount: PropTypes.number,
        enableConfirmNotificationsToChannel: PropTypes.bool,
        isTimezoneEnabled: PropTypes.bool,
        currentChannel: PropTypes.object,
        isLandscape: PropTypes.bool.isRequired,
        screenId: PropTypes.string.isRequired,
    };

    static defaultProps = {
        files: [],
        rootId: '',
        value: '',
    };

    static contextTypes = {
        intl: intlShape,
    };

    constructor(props) {
        super(props);

        this.input = React.createRef();

        this.state = {
            cursorPosition: 0,
            keyboardType: 'default',
            top: 0,
            value: props.value,
            rootId: props.rootId,
            channelId: props.channelId,
            channelTimezoneCount: 0,
            longMessageAlertShown: false,
            extraInputPadding: 0,
        };
    }

    componentDidMount(prevProps) {
        const event = this.props.rootId ? INSERT_TO_COMMENT : INSERT_TO_DRAFT;

        EventEmitter.on(event, this.handleInsertTextToDraft);
        AppState.addEventListener('change', this.handleAppStateChange);

        if (Platform.OS === 'android') {
            Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
            BackHandler.addEventListener('hardwareBackPress', this.handleAndroidBack);
        }

        if (this.props.isTimezoneEnabled !== prevProps?.isTimezoneEnabled || prevProps?.channelId !== this.props.channelId) {
            this.numberOfTimezones().then((channelTimezoneCount) => this.setState({channelTimezoneCount}));
        }
    }

    static getDerivedStateFromProps(nextProps, state) {
        if (nextProps.channelId !== state.channelId || nextProps.rootId !== state.rootId) {
            return {
                value: nextProps.value,
                channelId: nextProps.channelId,
                rootId: nextProps.rootId,
            };
        }
        return null;
    }

    componentWillUnmount() {
        const event = this.props.rootId ? INSERT_TO_COMMENT : INSERT_TO_DRAFT;

        EventEmitter.off(event, this.handleInsertTextToDraft);
        AppState.removeEventListener('change', this.handleAppStateChange);

        if (Platform.OS === 'android') {
            Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
            BackHandler.removeEventListener('hardwareBackPress', this.handleAndroidBack);
        }
    }

    blur = () => {
        if (this.input.current) {
            this.input.current.blur();
        }
    };

    focus = () => {
        if (this.input.current) {
            this.input.current.focus();
        }
    }

    numberOfTimezones = async () => {
        const {data} = await this.props.actions.getChannelTimezones(this.props.channelId);
        return data?.length || 0;
    };

    canSend = () => {
        const {files, maxMessageLength, uploadFileRequestStatus} = this.props;
        const {value} = this.state;
        const messageLength = value.trim().length;

        if (messageLength > maxMessageLength) {
            return false;
        }

        if (files.length) {
            const loadingComplete = !this.isFileLoading();
            const alreadySendingFiles = uploadFileRequestStatus === RequestStatus.STARTED;
            return loadingComplete && !alreadySendingFiles;
        }

        return messageLength > 0;
    };

    changeDraft = (text) => {
        const {
            actions,
            channelId,
            rootId,
        } = this.props;

        if (rootId) {
            actions.handleCommentDraftChanged(rootId, text);
        } else {
            actions.handlePostDraftChanged(channelId, text);
        }
    };

    checkMessageLength = (value) => {
        const {intl} = this.context;
        const {maxMessageLength} = this.props;
        const valueLength = value.trim().length;

        if (valueLength > maxMessageLength) {
            // Check if component is already aware message is too long
            if (!this.state.longMessageAlertShown) {
                Alert.alert(
                    intl.formatMessage({
                        id: 'mobile.message_length.title',
                        defaultMessage: 'Message Length',
                    }),
                    intl.formatMessage({
                        id: 'mobile.message_length.message',
                        defaultMessage: 'Your current message is too long. Current character count: {count}/{max}',
                    }, {
                        max: maxMessageLength,
                        count: valueLength,
                    }),
                );
                this.setState({longMessageAlertShown: true});
            }
        } else if (this.state.longMessageAlertShown) {
            this.setState({longMessageAlertShown: false});
        }
    };

    startAtMention = () => {
        this.handleTextChange(`${this.state.value}@`, true);
        this.focus();
    };

    startSlashCommand = () => {
        this.handleTextChange('/', true);
        this.focus();
    };

    getTextInputButton = (actionType) => {
        const {channelIsReadOnly, theme} = this.props;
        const style = getStyleSheet(theme);

        let button = null;
        const buttonStyle = [];
        let iconColor = changeOpacity(theme.centerChannelColor, 0.64);
        let isDisabled = false;

        if (!channelIsReadOnly) {
            switch (actionType) {
            case 'at':
                isDisabled = this.state.value[this.state.value.length - 1] === '@';
                if (isDisabled) {
                    iconColor = changeOpacity(theme.centerChannelColor, 0.16);
                }
                button = (
                    <TouchableOpacity
                        disabled={isDisabled}
                        onPress={this.startAtMention}
                        style={style.iconWrapper}
                    >
                        <MaterialCommunityIcons
                            color={iconColor}
                            name='at'
                            size={ICON_SIZE}
                        />
                    </TouchableOpacity>
                );
                break;
            case 'slash':
                isDisabled = this.state.value.length > 0;
                buttonStyle.push(style.slashIcon);
                if (isDisabled) {
                    buttonStyle.push(style.iconDisabled);
                }

                button = (
                    <TouchableOpacity
                        disabled={isDisabled}
                        onPress={this.startSlashCommand}
                        style={style.iconWrapper}
                    >
                        <Image
                            source={slashForwardBoxIcon}
                            style={buttonStyle}
                        />
                    </TouchableOpacity>
                );
                break;
            }
        }

        return button;
    }

    getMediaButton = (actionType) => {
        const {canUploadFiles, channelIsReadOnly, files, maxFileSize, theme} = this.props;
        const style = getStyleSheet(theme);
        let button = null;
        const props = {
            blurTextBox: this.blur,
            fileCount: files.length,
            maxFileCount: MAX_FILE_COUNT,
            onShowFileMaxWarning: this.onShowFileMaxWarning,
            onShowFileSizeWarning: this.onShowFileSizeWarning,
            uploadFiles: this.handleUploadFiles,
            maxFileSize,
            theme,
            buttonContainerStyle: style.iconWrapper,
        };

        if (canUploadFiles && !channelIsReadOnly) {
            switch (actionType) {
            case 'file':
                button = (
                    <FileUploadButton {...props}/>
                );
                break;
            case 'image':
                button = (
                    <ImageUploadButton {...props}/>
                );
                break;
            case 'camera':
                button = (
                    <CameraButton {...props}/>
                );
            }
        }

        return button;
    }

    getInputContainerStyle = () => {
        const {channelIsReadOnly, theme} = this.props;
        const style = getStyleSheet(theme);
        const inputContainerStyle = [style.inputContainer];

        if (channelIsReadOnly) {
            inputContainerStyle.push(style.readonlyContainer);
        }

        return inputContainerStyle;
    };

    getPlaceHolder = () => {
        const {channelIsReadOnly, rootId} = this.props;
        let placeholder;

        if (channelIsReadOnly) {
            placeholder = {id: t('mobile.create_post.read_only'), defaultMessage: 'This channel is read-only.'};
        } else if (rootId) {
            placeholder = {id: t('create_comment.addComment'), defaultMessage: 'Add a comment...'};
        } else {
            placeholder = {id: t('create_post.write'), defaultMessage: 'Write to {channelDisplayName}'};
        }

        return placeholder;
    };

    handleAndroidKeyboard = () => {
        this.blur();
    };

    handleAndroidBack = () => {
        const {channelId, files, rootId} = this.props;
        if (files.length) {
            this.props.actions.handleRemoveLastFile(channelId, rootId);
            return true;
        }
        return false;
    };

    handleAppStateChange = (nextAppState) => {
        if (nextAppState !== 'active') {
            this.changeDraft(this.state.value);
        }
    };

    handleEndEditing = (e) => {
        if (e && e.nativeEvent) {
            this.changeDraft(e.nativeEvent.text || '');
        }
    };

    handlePostDraftSelectionChanged = (event) => {
        const cursorPosition = event.nativeEvent.selection.end;
        const {cursorPositionEvent} = this.props;

        if (cursorPositionEvent) {
            EventEmitter.emit(cursorPositionEvent, cursorPosition);
        }

        this.setState({
            cursorPosition,
        });
    };

    handleSendMessage = () => {
        if (!this.isSendButtonEnabled()) {
            return;
        }

        this.setState({sendingMessage: true});

        const {actions, channelId, files, rootId} = this.props;
        const {value} = this.state;

        const isReactionMatch = value.match(IS_REACTION_REGEX);
        if (isReactionMatch) {
            const emoji = isReactionMatch[2];
            this.sendReaction(emoji);
            return;
        }

        const hasFailedAttachments = files.some((f) => f.failed);
        if (hasFailedAttachments) {
            const {intl} = this.context;

            Alert.alert(
                intl.formatMessage({
                    id: 'mobile.post_textbox.uploadFailedTitle',
                    defaultMessage: 'Attachment failure',
                }),
                intl.formatMessage({
                    id: 'mobile.post_textbox.uploadFailedDesc',
                    defaultMessage: 'Some attachments failed to upload to the server. Are you sure you want to post the message?',
                }),
                [{
                    text: intl.formatMessage({id: 'mobile.channel_info.alertNo', defaultMessage: 'No'}),
                    onPress: () => {
                        this.setState({sendingMessage: false});
                    },
                }, {
                    text: intl.formatMessage({id: 'mobile.channel_info.alertYes', defaultMessage: 'Yes'}),
                    onPress: () => {
                        // Remove only failed files
                        actions.handleClearFailedFiles(channelId, rootId);
                        this.sendMessage();
                    },
                }],
            );
        } else {
            this.sendMessage();
        }
    };

    handleInsertTextToDraft = (text) => {
        const {cursorPosition, value} = this.state;

        let completed;
        if (value.length === 0) {
            completed = text;
        } else {
            const firstPart = value.substring(0, cursorPosition);
            const secondPart = value.substring(cursorPosition);
            completed = `${firstPart}${text}${secondPart}`;
        }

        this.setState({
            value: completed,
        });
    };

    handleTextChange = (value, autocomplete = false) => {
        const {
            actions,
            channelId,
            rootId,
            cursorPositionEvent,
            valueEvent,
        } = this.props;

        if (valueEvent) {
            EventEmitter.emit(valueEvent, value);
        }

        const nextState = {value};

        // Workaround for some Android keyboards that don't play well with cursors (e.g. Samsung keyboards)
        if (autocomplete && this.input?.current) {
            if (Platform.OS === 'android') {
                RNTextInputReset.resetKeyboardInput(findNodeHandle(this.input.current));
            } else {
                nextState.cursorPosition = value.length;
                if (cursorPositionEvent) {
                    EventEmitter.emit(cursorPositionEvent, nextState.cursorPosition);
                }
            }
        }

        this.checkMessageLength(value);

        this.setState(nextState);

        if (value) {
            actions.userTyping(channelId, rootId);
        }
    };

    handleUploadFiles = async (files) => {
        const file = files[0];
        if (!file.fileSize | !file.fileName) {
            const path = (file.path || file.uri).replace('file://', '');
            const fileInfo = await RNFetchBlob.fs.stat(path);
            file.fileSize = fileInfo.size;
            file.fileName = fileInfo.filename;
        }

        if (file.fileSize > this.props.maxFileSize) {
            this.onShowFileSizeWarning(file.fileName);
        } else {
            this.props.actions.initUploadFiles(files, this.props.rootId);
        }
    };

    isFileLoading = () => {
        const {files} = this.props;

        return files.some((file) => file.loading);
    };

    isSendButtonVisible = () => {
        return this.canSend() || this.isFileLoading();
    };

    isSendButtonEnabled = () => {
        return this.canSend() && !this.isFileLoading() && !this.state.sendingMessage;
    };

    sendMessage = () => {
        const {value} = this.state;

        const currentMembersCount = this.props.currentChannelMembersCount;
        const notificationsToChannel = this.props.enableConfirmNotificationsToChannel;
        const toAllOrChannel = this.textContainsAtAllAtChannel(value);

        if (value.indexOf('/') === 0) {
            this.sendCommand(value);
        } else if (notificationsToChannel && currentMembersCount > NOTIFY_ALL_MEMBERS && toAllOrChannel) {
            this.showSendToAllOrChannelAlert(currentMembersCount);
        } else {
            this.doSubmitMessage();
        }
    };

    textContainsAtAllAtChannel = (text) => {
        const textWithoutCode = text.replace(/(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)| *(`{3,}|~{3,})[ .]*(\S+)? *\n([\s\S]*?\s*)\3 *(?:\n+|$)/g, '');
        return (/\B@(all|channel)\b/i).test(textWithoutCode);
    };

    showSendToAllOrChannelAlert = (currentMembersCount) => {
        const {intl} = this.context;
        const {channelTimezoneCount} = this.state;
        const {isTimezoneEnabled} = this.props;

        let notifyAllMessage = '';
        if (isTimezoneEnabled && channelTimezoneCount) {
            notifyAllMessage = (
                intl.formatMessage(
                    {
                        id: 'mobile.post_textbox.entire_channel.message.with_timezones',
                        defaultMessage: 'By using @all or @channel you are about to send notifications to {totalMembers, number} {totalMembers, plural, one {person} other {people}} in {timezones, number} {timezones, plural, one {timezone} other {timezones}}. Are you sure you want to do this?',
                    },
                    {
                        totalMembers: currentMembersCount - 1,
                        timezones: channelTimezoneCount,
                    },
                )
            );
        } else {
            notifyAllMessage = (
                intl.formatMessage(
                    {
                        id: 'mobile.post_textbox.entire_channel.message',
                        defaultMessage: 'By using @all or @channel you are about to send notifications to {totalMembers, number} {totalMembers, plural, one {person} other {people}}. Are you sure you want to do this?',
                    },
                    {
                        totalMembers: currentMembersCount - 1,
                    },
                )
            );
        }

        Alert.alert(
            intl.formatMessage({
                id: 'mobile.post_textbox.entire_channel.title',
                defaultMessage: 'Confirm sending notifications to entire channel',
            }),
            notifyAllMessage,
            [
                {
                    text: intl.formatMessage({
                        id: 'mobile.post_textbox.entire_channel.cancel',
                        defaultMessage: 'Cancel',
                    }),
                    onPress: () => {
                        this.setState({sendingMessage: false});
                    },
                },
                {
                    text: intl.formatMessage({
                        id: 'mobile.post_textbox.entire_channel.confirm',
                        defaultMessage: 'Confirm',
                    }),
                    onPress: () => this.doSubmitMessage(),
                },
            ],
        );
    };

    doSubmitMessage = () => {
        const {actions, currentUserId, channelId, files, rootId} = this.props;
        const {value} = this.state;
        const postFiles = files.filter((f) => !f.failed);
        const post = {
            user_id: currentUserId,
            channel_id: channelId,
            root_id: rootId,
            parent_id: rootId,
            message: value,
        };

        actions.createPost(post, postFiles);

        if (postFiles.length) {
            actions.handleClearFiles(channelId, rootId);
        }

        if (Platform.OS === 'ios') {
            // On iOS, if the PostTextbox height increases from its
            // initial height (due to a multiline post or a post whose
            // message wraps, for example), then when the text is cleared
            // the PostTextbox height decrease will be animated. This
            // animation in conjunction with the PostList animation as it
            // receives the newly created post is causing issues in the iOS
            // PostList component as it fails to properly react to its content
            // size changes. While a proper fix is determined for the PostList
            // component, a small delay in triggering the height decrease
            // animation gives the PostList enough time to first handle content
            // size changes from the new post.
            setTimeout(() => {
                this.handleTextChange('');
                this.setState({sendingMessage: false});
            }, 250);
        } else {
            this.handleTextChange('');
            this.setState({sendingMessage: false});
        }

        this.changeDraft('');

        let callback;
        if (Platform.OS === 'android') {
            // Fixes the issue where Android predictive text would prepend suggestions to the post draft when messages
            // are typed successively without blurring the input
            const nextState = {
                keyboardType: 'email-address',
            };

            callback = () => this.setState({keyboardType: 'default'});

            this.setState(nextState, callback);
        }

        EventEmitter.emit('scroll-to-bottom');
    };

    getStatusFromSlashCommand = (message) => {
        const tokens = message.split(' ');

        if (tokens.length > 0) {
            return tokens[0].substring(1);
        }
        return '';
    };

    isStatusSlashCommand = (command) => {
        return command === General.ONLINE || command === General.AWAY ||
            command === General.DND || command === General.OFFLINE;
    };

    updateStatus = (status) => {
        const {actions, currentUserId} = this.props;
        actions.setStatus({
            user_id: currentUserId,
            status,
        });
    };

    sendCommand = async (msg) => {
        const {intl} = this.context;
        const {userIsOutOfOffice, actions, channelId, rootId} = this.props;

        const status = this.getStatusFromSlashCommand(msg);
        if (userIsOutOfOffice && this.isStatusSlashCommand(status)) {
            confirmOutOfOfficeDisabled(intl, status, this.updateStatus);
            this.setState({sendingMessage: false});
            return;
        }

        const {error} = await actions.executeCommand(msg, channelId, rootId);
        this.setState({sendingMessage: false});

        if (error) {
            Alert.alert(
                intl.formatMessage({
                    id: 'mobile.commands.error_title',
                    defaultMessage: 'Error Executing Command',
                }),
                error.message,
            );
            return;
        }

        this.handleTextChange('');
        this.changeDraft('');
    };

    sendReaction = (emoji) => {
        const {actions, rootId} = this.props;
        actions.addReactionToLatestPost(emoji, rootId);

        this.handleTextChange('');
        this.changeDraft('');

        this.setState({sendingMessage: false});
    };

    onShowFileMaxWarning = () => {
        EventEmitter.emit('fileMaxWarning');
    };

    onShowFileSizeWarning = (filename) => {
        const {formatMessage} = this.context.intl;
        const fileSizeWarning = formatMessage({
            id: 'file_upload.fileAbove',
            defaultMessage: 'File above {max} cannot be uploaded: {filename}',
        }, {
            max: getFormattedFileSize({size: this.props.maxFileSize}),
            filename,
        });

        EventEmitter.emit('fileSizeWarning', fileSizeWarning);
        setTimeout(() => {
            EventEmitter.emit('fileSizeWarning', null);
        }, 5000);
    };

    onCloseChannelPress = () => {
        const {onCloseChannel, channelTeamId} = this.props;
        this.props.actions.selectPenultimateChannel(channelTeamId);
        if (onCloseChannel) {
            onCloseChannel();
        }
    };

    archivedView = (theme, style) => {
        return (
            <View style={style.archivedWrapper}>
                <FormattedMarkdownText
                    id='archivedChannelMessage'
                    defaultMessage='You are viewing an **archived channel**. New messages cannot be posted.'
                    theme={theme}
                    style={style.archivedText}
                />
                <Button
                    containerStyle={style.closeButton}
                    onPress={this.onCloseChannelPress}
                >
                    <FormattedText
                        id='center_panel.archived.closeChannel'
                        defaultMessage='Close Channel'
                        style={style.closeButtonText}
                    />
                </Button>
            </View>
        );
    };

    handleLayout = (e) => {
        this.setState({
            top: e.nativeEvent.layout.y,
        });
    };

    showPasteFilesErrorDialog = () => {
        const {formatMessage} = this.context.intl;
        Alert.alert(
            formatMessage({
                id: 'mobile.files_paste.error_title',
                defaultMessage: 'Paste failed',
            }),
            formatMessage({
                id: 'mobile.files_paste.error_description',
                defaultMessage: 'An error occurred while pasting the file(s). Please try again.',
            }),
            [
                {
                    text: formatMessage({
                        id: 'mobile.files_paste.error_dismiss',
                        defaultMessage: 'Dismiss',
                    }),
                },
            ],
        );
    };

    handlePasteFiles = (error, files) => {
        if (this.props.screenId === EphemeralStore.getNavigationTopComponentId()) {
            if (error) {
                this.showPasteFilesErrorDialog();
                return;
            }

            const {maxFileSize} = this.props;
            const availableCount = MAX_FILE_COUNT - this.props.files.length;
            if (files.length > availableCount) {
                this.onShowFileMaxWarning();
                return;
            }

            const largeFile = files.find((image) => image.fileSize > maxFileSize);
            if (largeFile) {
                this.onShowFileSizeWarning(largeFile.fileName);
                return;
            }

            this.handleUploadFiles(files);
        }
    };

    handleInputSizeChange = ({nativeEvent: {contentSize}}) => {
        const {extraInputPadding} = this.state;
        const numLines = contentSize.height / INPUT_LINE_HEIGHT;
        if (numLines >= 2 && extraInputPadding !== EXTRA_INPUT_PADDING) {
            this.setState({extraInputPadding: EXTRA_INPUT_PADDING});
        } else if (numLines < 2 && extraInputPadding !== 0) {
            this.setState({extraInputPadding: 0});
        }
    }

    renderDeactivatedChannel = () => {
        const {intl} = this.context;
        const style = getStyleSheet(this.props.theme);

        return (
            <Text style={style.deactivatedMessage}>
                {intl.formatMessage({
                    id: 'create_post.deactivated',
                    defaultMessage: 'You are viewing an archived channel with a deactivated user.',
                })}
            </Text>
        );
    };

    renderTextBox = () => {
        const {intl} = this.context;
        const {channelDisplayName, channelIsArchived, channelIsLoading, channelIsReadOnly, theme, isLandscape, files, rootId} = this.props;
        const style = getStyleSheet(theme);

        if (channelIsArchived) {
            return this.archivedView(theme, style);
        }

        const {value, extraInputPadding} = this.state;
        const textValue = channelIsLoading ? '' : value;
        const placeholder = this.getPlaceHolder();

        let maxHeight = 150;

        if (isLandscape) {
            maxHeight = 88;
        }

        const inputStyle = {};
        if (extraInputPadding) {
            inputStyle.paddingBottom = style.input.paddingBottom + extraInputPadding;
            inputStyle.paddingTop = style.input.paddingTop + extraInputPadding;
        }

        return (
            <View
                style={[style.inputWrapper, padding(isLandscape)]}
                onLayout={this.handleLayout}
            >
                <ScrollView
                    style={this.getInputContainerStyle()}
                    contentContainerStyle={style.inputContentContainer}
                    keyboardShouldPersistTaps={'always'}
                    scrollEnabled={false}
                    showsVerticalScrollIndicator={false}
                    showsHorizontalScrollIndicator={false}
                    pinchGestureEnabled={false}
                    overScrollMode={'never'}
                    disableScrollViewPanResponder={true}
                >
                    <PasteableTextInput
                        ref={this.input}
                        value={textValue}
                        style={{...style.input, ...inputStyle, maxHeight}}
                        onChangeText={this.handleTextChange}
                        onSelectionChange={this.handlePostDraftSelectionChanged}
                        placeholder={intl.formatMessage(placeholder, {channelDisplayName})}
                        placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.5)}
                        multiline={true}
                        blurOnSubmit={false}
                        underlineColorAndroid='transparent'
                        keyboardType={this.state.keyboardType}
                        onEndEditing={this.handleEndEditing}
                        disableFullscreenUI={true}
                        editable={!channelIsReadOnly}
                        onPaste={this.handlePasteFiles}
                        keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}
                        onContentSizeChange={Platform.OS === 'android' ? this.handleInputSizeChange : null}
                    />
                    {!channelIsReadOnly &&
                    <React.Fragment>
                        <FileUploadPreview
                            files={files}
                            rootId={rootId}
                        />

                        <View style={style.buttonsContainer}>
                            <View style={style.quickActionsContainer}>

                                {this.getTextInputButton('at')}

                                {this.getTextInputButton('slash')}

                                {this.getMediaButton('file')}

                                {this.getMediaButton('image')}

                                {this.getMediaButton('camera')}

                            </View>
                            <SendButton
                                disabled={!this.isSendButtonEnabled()}
                                handleSendMessage={this.handleSendMessage}
                                theme={theme}
                            />
                        </View>
                    </React.Fragment>
                    }
                </ScrollView>
            </View>
        );
    };
}

const getStyleSheet = makeStyleSheetFromTheme((theme) => {
    return {
        buttonsContainer: {
            display: 'flex',
            flexDirection: 'row',
            justifyContent: 'space-between',
            alignItems: 'center',
            paddingBottom: Platform.select({
                ios: 1,
                android: 2,
            }),
        },
        slashIcon: {
            width: ICON_SIZE,
            height: ICON_SIZE,
            opacity: 1,
            tintColor: changeOpacity(theme.centerChannelColor, 0.64),
        },
        iconDisabled: {
            tintColor: changeOpacity(theme.centerChannelColor, 0.16),
        },
        iconWrapper: {
            alignItems: 'center',
            justifyContent: 'center',
            padding: 10,
        },
        quickActionsContainer: {
            display: 'flex',
            flexDirection: 'row',
            height: 44,
        },
        input: {
            color: theme.centerChannelColor,
            fontSize: 15,
            lineHeight: INPUT_LINE_HEIGHT,
            paddingHorizontal: 12,
            paddingTop: Platform.select({
                ios: 6,
                android: 8,
            }),
            paddingBottom: Platform.select({
                ios: 6,
                android: 2,
            }),
            minHeight: 30,
        },
        inputContainer: {
            flex: 1,
            flexDirection: 'column',
        },
        inputContentContainer: {
            alignItems: 'stretch',
            paddingTop: Platform.select({
                ios: 7,
                android: 0,
            }),
        },
        inputWrapper: {
            alignItems: 'flex-end',
            flexDirection: 'row',
            justifyContent: 'center',
            paddingBottom: 2,
            backgroundColor: theme.centerChannelBg,
            borderTopWidth: 1,
            borderTopColor: changeOpacity(theme.centerChannelColor, 0.20),
        },
        deactivatedMessage: {
            color: changeOpacity(theme.centerChannelColor, 0.8),
            fontSize: 15,
            lineHeight: 22,
            alignItems: 'flex-end',
            flexDirection: 'row',
            paddingVertical: 4,
            backgroundColor: theme.centerChannelBg,
            borderTopWidth: 1,
            borderTopColor: changeOpacity(theme.centerChannelColor, 0.20),
            marginLeft: 10,
            marginRight: 10,
        },
        archivedWrapper: {
            paddingLeft: 20,
            paddingRight: 20,
            paddingTop: 10,
            paddingBottom: 10,
            borderTopWidth: 1,
            backgroundColor: theme.centerChannelBg,
            borderTopColor: changeOpacity(theme.centerChannelColor, 0.20),
        },
        archivedText: {
            textAlign: 'center',
            color: theme.centerChannelColor,
        },
        closeButton: {
            backgroundColor: theme.buttonBg,
            alignItems: 'center',
            paddingTop: 5,
            paddingBottom: 5,
            borderRadius: 4,
            marginTop: 10,
            height: 40,
        },
        closeButtonText: {
            marginTop: 7,
            color: 'white',
            fontWeight: 'bold',
        },
        readonlyContainer: {
            marginLeft: 10,
        },
    };
});
