import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import isArray from 'lodash/isEqual';
import isFunction from 'lodash/isFunction';
import Typography from '@material-ui/core/Typography';
import Dialog from '../Dialog';

import './Styles.scss';

export class DialogSelectOption extends React.Component {
    static propTypes = {
        onSelect: PropTypes.func,
        option: PropTypes.object,
        selected: PropTypes.bool,
        renderOption: PropTypes.func,
    };

    onSelect = () => {
        this.props.onSelect(this.props.option);
    };

    render() {
        const { option, selected, renderOption } = this.props;
        return (
            <Typography
                className={'dialogselect-option' + (selected ? ' selected' : '')}
                data-option={option.identifier}
                onClick={this.onSelect}
            >
                {renderOption ? renderOption(option) : option.title}
            </Typography>
        );
    }
}

export default class DialogSelect extends React.Component {
    static propTypes = {
        isSelected: PropTypes.func,
        disabled: PropTypes.bool,
        valueProperty: PropTypes.string,
        value: PropTypes.object,
        onFocus: PropTypes.func,
        onToggle: PropTypes.func,
        onChange: PropTypes.func,
        renderOption: PropTypes.func,
        label: PropTypes.node,
        options: PropTypes.array,
        renderValue: PropTypes.func,
        showSelected: PropTypes.bool,
        renderArrow: PropTypes.func,
        dialogPositioning: PropTypes.array,
        adjustWidth: PropTypes.bool,
        relativeTo: PropTypes.object,
        overlay: PropTypes.bool,
        className: PropTypes.string,
        Dialog: PropTypes.elementType,
    };

    static dialogPositioning = [
        ['left', 'right'],
        ['top', 'top'],
    ];

    constructor(props) {
        super(props);
        this.state = {
            isOpen: false,
        };
    }

    isSelected(option) {
        if (isFunction(this.props.isSelected)) {
            return this.props.isSelected(option);
        }

        const { valueProperty } = this.props;

        if (!this.props.value) {
            return false;
        }

        // if there are more values in the selection
        if (isArray(this.props.value)) {
            for (let v of this.props.value) {
                if (valueProperty) {
                    if (v[valueProperty] === option[valueProperty]) {
                        return true;
                    }
                } else {
                    if (v === option) {
                        return true;
                    }
                }
            }
        } else if (valueProperty) {
            return this.props.value[valueProperty] === option[valueProperty];
        } else {
            return this.props.value === option;
        }
    }

    onClickAway = e => {
        let element = e.target;
        do {
            if (element === this.control) {
                return;
            }
            element = element.parentElement;
        } while (element);
        this.closeDialog();
    };

    showDialog = () => {
        window.addEventListener('click', this.onClickAway, true);
        this.setState({ isOpen: true });
        this.props.onFocus && this.props.onFocus();
    };

    closeDialog = () => {
        window.removeEventListener('click', this.onClickAway, true);
        this.setState({ isOpen: false });
    };

    toggleDialog = () => {
        if (this.props.disabled) {
            return;
        }
        this.state.isOpen ? this.closeDialog() : this.showDialog();

        if (this.props.onToggle !== undefined) {
            this.props.onToggle();
        }
    };

    onChange = option => {
        this.closeDialog();
        if (!this.isSelected(option)) {
            this.props.onChange(option);
        }
    };

    renderOption(option, key) {
        const { renderOption } = this.props;
        return (
            <DialogSelectOption
                option={option}
                key={key}
                onSelect={this.onChange}
                selected={this.isSelected(option)}
                renderOption={renderOption}
            />
        );
    }

    render() {
        const {
            label,
            options,
            value,
            renderValue,
            renderOption,
            showSelected,
            renderArrow,
            dialogPositioning,
            adjustWidth,
            relativeTo,
            overlay,
            className,
            Dialog,
        } = this.props;

        let topElement = null;

        if (showSelected) {
            if (value) {
                if (renderValue) {
                    topElement = renderValue(value);
                } else {
                    topElement = renderOption(value, true);
                }
            } else {
                topElement = <div className="dialogselect-label">{label}</div>;
            }
        } else {
            topElement = <div className="dialogselect-label">{label}</div>;
        }

        const relativeElement = relativeTo || this.control;

        const show = isArray(options) && options.length > 0 && this.state.isOpen;

        const disabled = this.props.disabled || false;

        return (
            <div
                className={clsx('dialogselect', className, disabled && 'disabled')}
                ref={el => (this.control = el)}
            >
                <div className="button" onClick={this.toggleDialog}>
                    {topElement}
                    {renderArrow()}
                </div>

                <Dialog
                    className={'dialogselect-dialog ' + (adjustWidth ? '' : 'auto-width')}
                    show={show}
                    relativeTo={relativeElement}
                    onClose={this.closeDialog}
                    animation="grow"
                    relativePosition={dialogPositioning || DialogSelect.dialogPositioning}
                    parent={document.getElementById('configuratorscreen')}
                    refValue={options}
                    width={adjustWidth ? 'adjust' : false}
                    overlay={overlay}
                    overlayClassName="dialogselect-overlay"
                >
                    {isArray(options) && options.map((option, key) => this.renderOption(option, key))}
                </Dialog>
            </div>
        );
    }
}

DialogSelect.defaultProps = {
    renderArrow: () => null,
    Dialog,
};
