/* eslint-disable lines-around-comment */
import Spinner from '@atlaskit/spinner';
// @ts-ignore
import Bourne from '@hapi/bourne';
// @ts-ignore
import { jitsiLocalStorage } from '@jitsi/js-utils/jitsi-local-storage';
import { makeStyles } from '@material-ui/styles';
import clsx from 'clsx';
import React, { useState, useEffect, useCallback } from 'react';
import { WithTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';

import { IState } from '../../app/types';
// @ts-ignore
import { getMultipleVideoSendingSupportFeatureFlag } from '../../base/config';
// @ts-ignore
import { Dialog, hideDialog, openDialog } from '../../base/dialog';
import { translate } from '../../base/i18n/functions';
import Icon from '../../base/icons/components/Icon';
import { IconCloseSmall, IconShareDesktop } from '../../base/icons/svg/index';
import { browser, JitsiTrackErrors } from '../../base/lib-jitsi-meet';
// @ts-ignore
import { createLocalTrack } from '../../base/lib-jitsi-meet/functions';
import { VIDEO_TYPE } from '../../base/media/constants';
import { connect } from '../../base/redux/functions';
// @ts-ignore
import { updateSettings } from '../../base/settings';
// @ts-ignore
import { Tooltip } from '../../base/tooltip';
// @ts-ignore
import { getLocalVideoTrack } from '../../base/tracks';
// @ts-ignore
import { NOTIFICATION_TIMEOUT_TYPE, showErrorNotification } from '../../notifications';
// @ts-ignore
import { toggleBackgroundEffect, virtualBackgroundTrackChanged } from '../actions';
import { IMAGES, BACKGROUNDS_LIMIT, VIRTUAL_BACKGROUND_TYPE, type Image } from '../constants';
// @ts-ignore
import { toDataURL } from '../functions';
// @ts-ignore
import logger from '../logger';

import UploadImageButton from './UploadImageButton';
// @ts-ignore
import VirtualBackgroundPreview from './VirtualBackgroundPreview';

interface Props extends WithTranslation {

    /**
     * The list of Images to choose from.
     */
    _images: Array<Image>,

    /**
     * Returns the jitsi track that will have backgraund effect applied.
     */
    _jitsiTrack: Object,

    /**
     * The current local flip x status.
     */
    _localFlipX: boolean,

    /**
    * Whether or not multi-stream send support is enabled.
    */
    _multiStreamModeEnabled: boolean,

    /**
     * Returns the selected thumbnail identifier.
     */
    _selectedThumbnail: string,

    /**
     * If the upload button should be displayed or not.
     */
    _showUploadButton: boolean,

    /**
     * Returns the selected virtual background object.
     */
    _virtualBackground: any,

    /**
     * The redux {@code dispatch} function.
     */
    dispatch: Function,

    /**
     * The initial options copied in the state for the {@code VirtualBackground} component.
     *
     * NOTE: currently used only for electron in order to open the dialog in the correct state after desktop sharing
     * selection.
     */
    initialOptions: Object
}

const onError = (event: any) => {
    event.target.style.display = 'none';
};


/**
 * Maps (parts of) the redux state to the associated props for the
 * {@code VirtualBackground} component.
 *
 * @param {Object} state - The Redux state.
 * @private
 * @returns {{Props}}
 */
function _mapStateToProps(state: any): Object {
    const { localFlipX } = state['features/base/settings'];
    const dynamicBrandingImages = state['features/dynamic-branding'].virtualBackgrounds;
    const hasBrandingImages = Boolean(dynamicBrandingImages.length);

    return {
        _localFlipX: Boolean(localFlipX),
        _images: (hasBrandingImages && dynamicBrandingImages) || IMAGES,
        _virtualBackground: state['features/virtual-background'],
        _selectedThumbnail: state['features/virtual-background'].selectedThumbnail,
        _showUploadButton: !(hasBrandingImages || state['features/base/config'].disableAddingBackgroundImages),
        _jitsiTrack: getLocalVideoTrack(state['features/base/tracks'])?.jitsiTrack,
        _multiStreamModeEnabled: getMultipleVideoSendingSupportFeatureFlag(state)
    };
}

const VirtualBackgroundDialog = translate(connect(_mapStateToProps)(VirtualBackground));

const useStyles = makeStyles((theme: any) => {
    return {
        container: {
            display: 'flex',
            flexDirection: 'column'
        },
        dialog: {
            alignSelf: 'flex-start',
            marginLeft: '-10px',
            position: 'relative',
            maxHeight: '300px',
            color: 'white',
            display: 'inline-grid',
            gridTemplateColumns: 'auto auto auto auto auto',
            columnGap: '9px',
            cursor: 'pointer',
            // @ts-ignore
            [[ '& .desktop-share:hover',
                '& .thumbnail:hover',
                '& .blur:hover',
                '& .slight-blur:hover',
                '& .virtual-background-none:hover' ]]: {
                opacity: 0.5,
                border: '2px solid #99bbf3'
            },
            '& .background-option': {
                marginTop: `${theme.spacing(2)}px`,
                borderRadius: `${theme.shape.borderRadius}px`,
                height: '60px',
                width: '107px',
                textAlign: 'center',
                justifyContent: 'center',
                fontWeight: 'bold',
                boxSizing: 'border-box',
                display: 'flex',
                alignItems: 'center'
            },
            '& thumbnail-container': {
                position: 'relative',
                '&:focus-within .thumbnail ~ .delete-image-icon': {
                    display: 'block'
                }
            },
            '& .thumbnail': {
                objectFit: 'cover'
            },
            '& .thumbnail:hover ~ .delete-image-icon': {
                display: 'block'
            },
            '& .thumbnail-selected': {
                objectFit: 'cover',
                border: '2px solid #246fe5'
            },
            '& .blur': {
                boxShadow: 'inset 0 0 12px #000000',
                background: '#7e8287',
                padding: '0 10px'
            },
            '& .blur-selected': {
                border: '2px solid #246fe5'
            },
            '& .slight-blur': {
                boxShadow: 'inset 0 0 12px #000000',
                background: '#a4a4a4',
                padding: '0 10px'
            },
            '& .slight-blur-selected': {
                border: '2px solid #246fe5'
            },
            '& .virtual-background-none': {
                background: '#525252',
                padding: '0 10px'
            },
            '& .none-selected': {
                border: '2px solid #246fe5'
            },
            '& .desktop-share': {
                background: '#525252'
            },
            '& .desktop-share-selected': {
                border: '2px solid #246fe5',
                padding: '0 10px'
            },
            '& delete-image-icon': {
                background: '#3d3d3d',
                position: 'absolute',
                display: 'none',
                left: '96px',
                bottom: '51px',
                '&:hover': {
                    display: 'block'
                },
                '@media (max-width: 632px)': {
                    left: '51px'
                }
            },
            '@media (max-width: 720px)': {
                gridTemplateColumns: 'auto auto auto auto'
            },
            '@media (max-width: 632px)': {
                gridTemplateColumns: 'auto auto auto auto auto',
                fontSize: '1.5vw',

                // @ts-ignore
                [[ '& .desktop-share:hover',
                    '& .thumbnail:hover',
                    '& .blur:hover',
                    '& .slight-blur:hover',
                    '& .virtual-background-none:hover' ]]: {
                    height: '60px',
                    width: '60px'
                },

                // @ts-ignore
                [[ '& .desktop-share',
                    '& .virtual-background-none,',
                    '& .thumbnail,',
                    '& .blur,',
                    '& .slight-blur' ]]: {
                    height: '60px',
                    width: '60px'
                },
                // @ts-ignore
                [[ '& .desktop-share-selected',
                    '& .thumbnail-selected',
                    '& .none-selected',
                    '& .blur-selected',
                    '& .slight-blur-selected' ]]: {
                    height: '60px',
                    width: '60px'
                }
            },
            '@media (max-width: 360px)': {
                gridTemplateColumns: 'auto auto auto auto'
            },
            '@media (max-width: 319px)': {
                gridTemplateColumns: 'auto auto'
            }
        },
        dialogMarginTop: {
            marginTop: '44px'
        },
        virtualBackgroundLoading: {
            overflow: 'hidden',
            position: 'fixed',
            left: '50%',
            marginTop: '10px',
            transform: 'translateX(-50%)'
        }
    };
});

/**
 * Renders virtual background dialog.
 *
 * @returns {ReactElement}
 */
function VirtualBackground({
    _images,
    _jitsiTrack,
    _localFlipX,
    _selectedThumbnail,
    _showUploadButton,
    _virtualBackground,
    _multiStreamModeEnabled,
    dispatch,
    initialOptions,
    t
}: Props) {
    const classes = useStyles();
    const [ previewIsLoaded, setPreviewIsLoaded ] = useState(false);
    const [ options, setOptions ] = useState<any>({ ...initialOptions });
    const localImages = jitsiLocalStorage.getItem('virtualBackgrounds');
    const [ storedImages, setStoredImages ] = useState<Array<Image>>((localImages && Bourne.parse(localImages)) || []);
    const [ loading, setLoading ] = useState(false);
    let { disableScreensharingVirtualBackground } = useSelector((state: IState) => state['features/base/config']);

    // Disable screenshare as virtual background in multi-stream mode.
    disableScreensharingVirtualBackground = disableScreensharingVirtualBackground || _multiStreamModeEnabled;

    const [ activeDesktopVideo ] = useState(_virtualBackground?.virtualSource?.videoType === VIDEO_TYPE.DESKTOP
        ? _virtualBackground.virtualSource
        : null);
    const [ initialVirtualBackground ] = useState(_virtualBackground);
    const deleteStoredImage = useCallback(e => {
        const imageId = e.currentTarget.getAttribute('data-imageid');

        setStoredImages(storedImages.filter(item => item.id !== imageId));
    }, [ storedImages ]);

    const deleteStoredImageKeyPress = useCallback(e => {
        if (e.key === ' ' || e.key === 'Enter') {
            e.preventDefault();
            deleteStoredImage(e);
        }
    }, [ deleteStoredImage ]);

    /**
     * Updates stored images on local storage.
     */
    useEffect(() => {
        try {
            jitsiLocalStorage.setItem('virtualBackgrounds', JSON.stringify(storedImages));
        } catch (err) {
            // Preventing localStorage QUOTA_EXCEEDED_ERR
            err && setStoredImages(storedImages.slice(1));
        }
        if (storedImages.length === BACKGROUNDS_LIMIT) {
            setStoredImages(storedImages.slice(1));
        }
    }, [ storedImages ]);

    const enableBlur = useCallback(async () => {
        setOptions({
            backgroundType: VIRTUAL_BACKGROUND_TYPE.BLUR,
            enabled: true,
            blurValue: 25,
            selectedThumbnail: 'blur'
        });
        logger.info('"Blur" option set for virtual background preview!');

    }, []);

    const enableBlurKeyPress = useCallback(e => {
        if (e.key === ' ' || e.key === 'Enter') {
            e.preventDefault();
            enableBlur();
        }
    }, [ enableBlur ]);

    const enableSlideBlur = useCallback(async () => {
        setOptions({
            backgroundType: VIRTUAL_BACKGROUND_TYPE.BLUR,
            enabled: true,
            blurValue: 8,
            selectedThumbnail: 'slight-blur'
        });
        logger.info('"Slight-blur" option set for virtual background preview!');

    }, []);

    const enableSlideBlurKeyPress = useCallback(e => {
        if (e.key === ' ' || e.key === 'Enter') {
            e.preventDefault();
            enableSlideBlur();
        }
    }, [ enableSlideBlur ]);


    const shareDesktop = useCallback(async () => {
        if (disableScreensharingVirtualBackground) {
            return;
        }

        let isCancelled = false, url;

        try {
            url = await createLocalTrack('desktop', '');
        } catch (e: any) {
            if (e.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {
                isCancelled = true;
            } else {
                logger.error(e);
            }
        }

        if (!url) {
            if (!isCancelled) {
                dispatch(showErrorNotification({
                    titleKey: 'virtualBackground.desktopShareError'
                }, NOTIFICATION_TIMEOUT_TYPE.LONG));
                logger.error('Could not create desktop share as a virtual background!');
            }

            /**
             * For electron createLocalTrack will open the {@code DesktopPicker} dialog and hide the
             * {@code VirtualBackgroundDialog}. That's why we need to reopen the {@code VirtualBackgroundDialog}
             * and restore the current state through {@code initialOptions} prop.
             */
            if (browser.isElectron()) {
                dispatch(openDialog(VirtualBackgroundDialog, { initialOptions: options }));
            }

            return;
        }

        const newOptions = {
            backgroundType: VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE,
            enabled: true,
            selectedThumbnail: 'desktop-share',
            url
        };

        /**
         * For electron createLocalTrack will open the {@code DesktopPicker} dialog and hide the
         * {@code VirtualBackgroundDialog}. That's why we need to reopen the {@code VirtualBackgroundDialog}
         * and force it to show desktop share virtual background through {@code initialOptions} prop.
         */
        if (browser.isElectron()) {
            dispatch(openDialog(VirtualBackgroundDialog, { initialOptions: newOptions }));
        } else {
            setOptions(newOptions);
            logger.info('"Desktop-share" option set for virtual background preview!');
        }
    }, [ dispatch, options ]);

    const shareDesktopKeyPress = useCallback(e => {
        if (e.key === ' ' || e.key === 'Enter') {
            e.preventDefault();
            shareDesktop();
        }
    }, [ shareDesktop ]);

    const removeBackground = useCallback(async () => {
        setOptions({
            enabled: false,
            selectedThumbnail: 'none'
        });
        logger.info('"None" option set for virtual background preview!');

    }, []);

    const removeBackgroundKeyPress = useCallback(e => {
        if (e.key === ' ' || e.key === 'Enter') {
            e.preventDefault();
            removeBackground();
        }
    }, [ removeBackground ]);

    const setUploadedImageBackground = useCallback(async e => {
        const imageId = e.currentTarget.getAttribute('data-imageid');
        const image = storedImages.find(img => img.id === imageId);

        if (image) {
            setOptions({
                backgroundType: 'image',
                enabled: true,
                url: image.src,
                selectedThumbnail: image.id
            });
            logger.info('Uploaded image set for virtual background preview!');
        }
    }, [ storedImages ]);

    const setImageBackground = useCallback(async e => {
        const imageId = e.currentTarget.getAttribute('data-imageid');
        const image = _images.find(img => img.id === imageId);

        if (image) {
            try {
                const url = await toDataURL(image.src);

                setOptions({
                    backgroundType: 'image',
                    enabled: true,
                    url,
                    selectedThumbnail: image.id
                });
                logger.info('Image set for virtual background preview!');
            } catch (err) {
                logger.error('Could not fetch virtual background image:', err);
            }

            setLoading(false);
        }
    }, []);

    const setImageBackgroundKeyPress = useCallback(e => {
        if (e.key === ' ' || e.key === 'Enter') {
            e.preventDefault();
            setImageBackground(e);
        }
    }, [ setImageBackground ]);

    const setUploadedImageBackgroundKeyPress = useCallback(e => {
        if (e.key === ' ' || e.key === 'Enter') {
            e.preventDefault();
            setUploadedImageBackground(e);
        }
    }, [ setUploadedImageBackground ]);

    const applyVirtualBackground = useCallback(async () => {
        if (activeDesktopVideo) {
            await activeDesktopVideo.dispose();
        }
        setLoading(true);
        await dispatch(toggleBackgroundEffect(options, _jitsiTrack));
        await setLoading(false);
        if (_localFlipX && options.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE) {
            dispatch(updateSettings({
                localFlipX: !_localFlipX
            }));
        } else {

            // Set x scale to default value.
            dispatch(updateSettings({
                localFlipX: true
            }));
        }
        dispatch(hideDialog());
        logger.info(`Virtual background type: '${typeof options.backgroundType === 'undefined'
            ? 'none' : options.backgroundType}' applied!`);
        dispatch(virtualBackgroundTrackChanged());
    }, [ dispatch, options, _localFlipX ]);

    // Prevent the selection of a new virtual background if it has not been applied by default
    const cancelVirtualBackground = useCallback(async () => {
        await setOptions({
            backgroundType: initialVirtualBackground.backgroundType,
            enabled: initialVirtualBackground.backgroundEffectEnabled,
            url: initialVirtualBackground.virtualSource,
            selectedThumbnail: initialVirtualBackground.selectedThumbnail,
            blurValue: initialVirtualBackground.blurValue
        });
        dispatch(hideDialog());
    }, []);

    const loadedPreviewState = useCallback(async loaded => {
        await setPreviewIsLoaded(loaded);
    }, []);

    return (
        <Dialog
            hideCancelButton = { false }
            okKey = { 'virtualBackground.apply' }
            onCancel = { cancelVirtualBackground }
            onSubmit = { applyVirtualBackground }
            submitDisabled = { !options || loading || !previewIsLoaded }
            titleKey = { 'virtualBackground.title' } >
            <VirtualBackgroundPreview
                loadedPreview = { loadedPreviewState }
                options = { options } />
            {loading ? (
                <div className = { classes.virtualBackgroundLoading }>
                    <Spinner
                        // @ts-ignore
                        isCompleting = { false }
                        size = 'medium' />
                </div>
            ) : (
                <div className = { classes.container }>
                    {_showUploadButton
                    && <UploadImageButton
                        setLoading = { setLoading }
                        setOptions = { setOptions }
                        setStoredImages = { setStoredImages }
                        showLabel = { previewIsLoaded }
                        storedImages = { storedImages } />}
                    <div
                        className = { clsx(classes.dialog, { [classes.dialogMarginTop]: previewIsLoaded }) }
                        role = 'radiogroup'
                        tabIndex = { -1 }>
                        <Tooltip
                            content = { t('virtualBackground.removeBackground') }
                            position = { 'top' }>
                            <div
                                aria-checked = { _selectedThumbnail === 'none' }
                                aria-label = { t('virtualBackground.removeBackground') }
                                className = { clsx('background-option', 'virtual-background-none', {
                                    'none-selected': _selectedThumbnail === 'none'
                                }) }
                                onClick = { removeBackground }
                                onKeyPress = { removeBackgroundKeyPress }
                                role = 'radio'
                                tabIndex = { 0 } >
                                {t('virtualBackground.none')}
                            </div>
                        </Tooltip>
                        <Tooltip
                            content = { t('virtualBackground.slightBlur') }
                            position = { 'top' }>
                            <div
                                aria-checked = { _selectedThumbnail === 'slight-blur' }
                                aria-label = { t('virtualBackground.slightBlur') }
                                className = { clsx('background-option', 'slight-blur', {
                                    'slight-blur-selected': _selectedThumbnail === 'slight-blur'
                                }) }
                                onClick = { enableSlideBlur }
                                onKeyPress = { enableSlideBlurKeyPress }
                                role = 'radio'
                                tabIndex = { 0 }>
                                {t('virtualBackground.slightBlur')}
                            </div>
                        </Tooltip>
                        <Tooltip
                            content = { t('virtualBackground.blur') }
                            position = { 'top' }>
                            <div
                                aria-checked = { _selectedThumbnail === 'blur' }
                                aria-label = { t('virtualBackground.blur') }
                                className = { clsx('background-option', 'blur', {
                                    'blur-selected': _selectedThumbnail === 'blur'
                                }) }
                                onClick = { enableBlur }
                                onKeyPress = { enableBlurKeyPress }
                                role = 'radio'
                                tabIndex = { 0 }>
                                {t('virtualBackground.blur')}
                            </div>
                        </Tooltip>
                        {!disableScreensharingVirtualBackground && (
                            <Tooltip
                                content = { t('virtualBackground.desktopShare') }
                                position = { 'top' }>
                                <div
                                    aria-checked = { _selectedThumbnail === 'desktop-share' }
                                    aria-label = { t('virtualBackground.desktopShare') }
                                    className = { clsx('background-option', 'desktop-share', {
                                        'desktop-share-selected': _selectedThumbnail === 'desktop-share'
                                    }) }
                                    onClick = { shareDesktop }
                                    onKeyPress = { shareDesktopKeyPress }
                                    role = 'radio'
                                    tabIndex = { 0 }>
                                    <Icon
                                        className = 'share-desktop-icon'
                                        size = { 30 }
                                        src = { IconShareDesktop } />
                                </div>
                            </Tooltip>
                        )}
                        {_images.map(image => (
                            <Tooltip
                                content = { image.tooltip && t(`virtualBackground.${image.tooltip}`) }
                                key = { image.id }
                                position = { 'top' }>
                                <img
                                    alt = { image.tooltip && t(`virtualBackground.${image.tooltip}`) }
                                    aria-checked = { options.selectedThumbnail === image.id
                                        || _selectedThumbnail === image.id }
                                    className = {
                                        options.selectedThumbnail === image.id || _selectedThumbnail === image.id
                                            ? 'background-option thumbnail-selected' : 'background-option thumbnail' }
                                    data-imageid = { image.id }
                                    onClick = { setImageBackground }
                                    onError = { onError }
                                    onKeyPress = { setImageBackgroundKeyPress }
                                    role = 'radio'
                                    src = { image.src }
                                    tabIndex = { 0 } />
                            </Tooltip>
                        ))}
                        {storedImages.map((image, index) => (
                            <div
                                className = { 'thumbnail-container' }
                                key = { image.id }>
                                <img
                                    alt = { t('virtualBackground.uploadedImage', { index: index + 1 }) }
                                    aria-checked = { _selectedThumbnail === image.id }
                                    className = { clsx('background-option', {
                                        'thumbnail-selected': _selectedThumbnail === image.id,
                                        'thumbnail': _selectedThumbnail !== image.id
                                    }) }
                                    data-imageid = { image.id }
                                    onClick = { setUploadedImageBackground }
                                    onError = { onError }
                                    onKeyPress = { setUploadedImageBackgroundKeyPress }
                                    role = 'radio'
                                    src = { image.src }
                                    tabIndex = { 0 } />

                                <Icon
                                    ariaLabel = { t('virtualBackground.deleteImage') }
                                    className = { 'delete-image-icon' }
                                    data-imageid = { image.id }
                                    onClick = { deleteStoredImage }
                                    onKeyPress = { deleteStoredImageKeyPress }
                                    role = 'button'
                                    size = { 15 }
                                    src = { IconCloseSmall }
                                    tabIndex = { 0 } />
                            </div>
                        ))}
                    </div>
                </div>
            )}
        </Dialog>
    );
}

export default VirtualBackgroundDialog;
