import * as React from 'react';

import { InputOption } from './InputOption';
import { levenshteinDistance } from '../../mmui-util';

export type OnSelected = (item: any) => void;

export interface Props {
    reset?: boolean;
    header?: string;
    options: Array<InputOption>;
    defaults?: Array<string>;
    onSelected: OnSelected;
}
export interface State {
    id: string;
    open: boolean;
    query: string;
    selected: Array<string>;
}

/**
 * Dropdown Multi-select React Component
 * @deprecated Use MmuiDropChoiceComponent
 */
export class MmuiDropSelectComponent extends React.Component<Props, State> {
    dropDownBodyRef: React.RefObject<HTMLDivElement>;

    protected id;
    static id_counter = 0;

    constructor(props) {
        super(props);
        this.dropDownBodyRef = React.createRef();
        this.state = {
            id: 'mmui_drop_select_component_' + this.getID(),
            selected: 'defaults' in props ? props.defaults : [],
            open: false,
            query: '',
        };
    }

    protected getID(): string {
        if (this.id === undefined) {
            this.id = MmuiDropSelectComponent.id_counter;
            MmuiDropSelectComponent.id_counter++;
        }
        return this.id;
    }

    componentDidUpdate(prevProps) {
        if ('reset' in this.props && this.props.reset && !prevProps.reset) {
            if ('defaults' in this.props && this.props.defaults) {
                this.setState({ selected: this.props.defaults });
            } else {
                if (this.state.selected.length > 0) {
                    this.setState({ selected: [] });
                }
            }
        }
    }

    onBlur = (e) => {
        if (this.state.open) {
            const mX = e.clientX;
            const mY = e.clientY;

            const rect = this.dropDownBodyRef.current.getBoundingClientRect();
            const inRect =
                rect.left <= mX &&
                mX <= rect.right &&
                rect.top <= mY &&
                mY <= rect.bottom;

            if (!inRect) {
                this.setState({ open: false });
                window.removeEventListener('click', this.onBlur);
            }
        }
    };

    onHeaderClick = () => {
        const isOpen = !this.state.open;
        this.setState({ open: isOpen });

        if (isOpen) {
            setTimeout(() => {
                window.addEventListener('click', this.onBlur);
            }, 100);
        } else {
            window.removeEventListener('click', this.onBlur);
        }
    };

    onSearchChange = (e: React.SyntheticEvent) => {
        this.setState({ query: (e.target as HTMLInputElement).value });
    };

    onItemSelect = (e: React.SyntheticEvent) => {
        const target: HTMLInputElement = e.target as HTMLInputElement;
        const selectedValue: string = target.value;
        const isChecked: boolean = target.checked;

        const n: number = this.state.selected.indexOf(selectedValue);
        let selectedValues: Array<string>;
        if (isChecked) {
            if (n == -1) {
                selectedValues = this.state.selected.concat(selectedValue);
            }
        } else {
            if (n > -1) {
                this.state.selected.splice(n, 1);
                selectedValues = this.state.selected;
            }
        }

        this.setState({ selected: selectedValues });

        if ('onSelected' in this.props) {
            this.props.onSelected(selectedValues.slice());
        }
    };

    isSearchable() {
        return 'searchable' in this.props;
    }

    render() {
        const isSearchable: boolean = this.isSearchable();
        const selected: Array<string> = this.state.selected;

        let header = 'Select';
        if (selected.length == 0) {
            if ('header' in this.props) {
                header = this.props.header;
            }
        } else {
            header = selected[0];
            if (selected.length > 1) {
                header += '+' + (selected.length - 1).toString();
            }
        }

        let searchField = null;
        if (isSearchable) {
            // todo: make the id configurable
            searchField = (
                <input
                    className="form-control mmui-form-control mmui-drop-select-choice-filter mmui-mb-1"
                    id="search-filter"
                    value={this.state.query}
                    onChange={this.onSearchChange}
                    placeholder="Search..."
                />
            );
        }

        let optionElements = null;
        if ('options' in this.props) {
            let options: Array<InputOption> = this.props.options;

            if (isSearchable) {
                if (this.state.query.length > 0) {
                    if ('fuzzy' in this.props) {
                        const scored: Array<any> = options.map(
                            (option: InputOption): any => {
                                return {
                                    w: option.label,
                                    d: levenshteinDistance(
                                        option.label,
                                        this.state.query
                                    ),
                                };
                            }
                        );
                        scored.sort(function (a, b) {
                            return a['d'] - b['d'];
                        });
                        options = scored.map(function (d) {
                            return d['w'];
                        });
                    } else {
                        options = options.filter((option: InputOption) => {
                            return option.label.indexOf(this.state.query) > -1;
                        });
                    }
                }
            }

            optionElements = options.map((option: InputOption) => {
                const label: string = option.label;
                const value: string = option.value;
                const checked: boolean =
                    this.state.selected.indexOf(value) > -1;
                const id: string = 'input_checkbox_' + this.getID();

                return (
                    <div className="form-check mmui-form-check" key={id}>
                        <input
                            className="form-check-input"
                            style={{ marginTop: '0' }}
                            type="checkbox"
                            name={this.state.id + '_input'}
                            id={id}
                            onChange={this.onItemSelect}
                            checked={checked}
                            value={value}
                        />
                        <label
                            className="form-check-label mmui-form-check-label"
                            htmlFor={id}
                        >
                            {label}
                        </label>
                    </div>
                );
            });
        }

        const id: string = 'drop_select_' + this.getID();
        return (
            <div id={id} className="mmui-drop-select">
                <button
                    className="bg-transparent border-0 pl-0 pr-0 pt-0 pb-0 w-100"
                    onClick={this.onHeaderClick}
                >
                    <div className="btn mmui-btn-dropdown">
                        <div className="mmui-dropdown-toggle"></div>
                        <div
                            className="mmui-drop-select-title"
                            style={{ pointerEvents: 'none' }}
                        >
                            {header}
                        </div>
                    </div>
                </button>

                <div
                    className={
                        this.state.open
                            ? 'mmui-drop-select-body'
                            : 'mmui-drop-select-body d-none'
                    }
                    ref={this.dropDownBodyRef}
                >
                    {searchField}

                    <div className="form-group mmui-form-group-scroll">
                        {optionElements}
                    </div>
                </div>
            </div>
        );
    }
}
