import React, {Component} from 'react';
import PropTypes from 'prop-types';
import EventBus from '../utils/EventBus';
import {Loader} from './Loader';
import {constants} from '../../utils';

export class GlobalLoader extends Component<any, any> {
    static propTypes = {
        ...Loader.propTypes,
        showEvents: PropTypes.arrayOf(PropTypes.string).isRequired,
        hideEvents: PropTypes.arrayOf(PropTypes.string).isRequired
    };

    static defaultProps = Loader.defaultProps;

    eventHandlers: any;

    constructor(props) {
        super(props);
        this.state = {
            stack: [],
            visible: false,
            content: props.children
        };
        this.eventHandlers = {
            [constants.eventShowGlobalLoader]: text => this.show(text),
            [constants.eventHideGlobalLoader]: text => this.hide(text)
        };
        if (props.showEvents.length !== props.hideEvents.length) {
            throw new Error('GlobalLoader: showEvents and hideEvents has a different length');
        }
    }

    componentDidMount() {
        this.props.showEvents.forEach(event => {
            if (!this.eventHandlers[event]) {
                this.eventHandlers[event] = () => this.show();
            }
            EventBus.on(event, this.eventHandlers[event]);
        });

        this.props.hideEvents.forEach(event => {
            if (!this.eventHandlers[event]) {
                this.eventHandlers[event] = () => this.hide();
            }
            EventBus.on(event, this.eventHandlers[event]);
        });
    }

    shouldComponentUpdate(nextProps, nextState) {
        return nextState.visible !== this.state.visible || nextState.content !== this.state.content;
    }

    componentWillUnmount() {
        this.props.showEvents.forEach(event => EventBus.off(event, this.eventHandlers[event]));
        this.props.hideEvents.forEach(event => EventBus.off(event, this.eventHandlers[event]));
        this.eventHandlers = null;
    }

    show = (text = '') => {
        this.setState(({stack, visible: wasVisible}) => {
            stack = [...stack, text];

            if (!wasVisible) {
                EventBus.emit(constants.onTestAutomationAwait);
            }

            return {
                stack,
                visible: true,
                content: this.getContent(stack)
            };
        });
    };

    hide = (text = '') => {
        this.setState(
            ({stack, visible: wasVisible}): any => {
                if (!stack.length) {
                    // eslint-disable-next-line
                console.error('GlobalLoader: hide-handler called when loader is already hidden');
                    return;
                }
                let index = -1;
                if (text) {
                    index = this.getTextIndex(stack, text);
                }
                if (index === -1) {
                    index = this.getTextIndex(stack, '');
                }
                if (index === -1) {
                    index = stack.length - 1;
                }
                stack.splice(index, 1);
                stack = [...stack];

                const visible = stack.length > 0;

                if (wasVisible && !visible) {
                    setTimeout(() => EventBus.emit(constants.onTestAutomationReady));
                }

                return {
                    stack,
                    visible,
                    content: this.getContent(stack)
                };
            }
        );
    };

    getTextIndex(stack, text) {
        for (let index = stack.length - 1; index > -1; index--) {
            if (stack[index] === text) {
                return index;
            }
        }
        return -1;
    }

    getContent(stack) {
        for (let index = stack.length - 1; index > -1; index--) {
            if (stack[index]) {
                return stack[index];
            }
        }
        return this.props.children;
    }

    render() {
        let {showEvents, hideEvents, children, ...props} = this.props;
        if (this.state.visible) {
            return (
                <div className="global-loader">
                    <Loader {...props}>{this.state.content}</Loader>
                </div>
            );
        }
        return null;
    }
}
