import * as React from 'react';
import { MmuiTooltipComponent } from '../MmuiTooltipComponent';
import { parseChoice } from './utils';

export interface Props {
    id?: any;
    title?: string;
    summaryRenderMode?: string;
    summaryTextDisplaySep?: string;
    hasFilterInput?: boolean; //whether there is a  filter input field to visually filter option choices
    hasSelectAll?: boolean; /// whether there is a select all checkbox to toggle between selecting or un-selecting all choices.
    filterPlaceholder?: string;
    data?: any; //data used to construct the list of choices
}

export interface State {
    id: string; // unique id for the Component
    title: string; //string that is displayed if there are no option choices selected.
    summaryRenderMode: string; //"text" or "token", determines if the summary is displayed as text or as tokens
    summaryTextDisplaySep: string; //if summary render mode is "text",
    filterPlaceholder: string; //placeholder text for the filter input box
    filterValue: string; //default value for the filter input field
    choices: any[]; //the options in the multi-select, each can have a children list
    selectedChoiceCount: number; // number of parents that have been selected
    isOpen: boolean; //tracks whether the Component is in the expanded state or not.
    tooltipX: number;
    tooltipY: number;
    tooltipBody: string;
    hasTooltip: boolean;
}

export abstract class MmuiDropChoiceComponent<
    P extends Props
> extends React.Component<P, State> {
    protected id;
    protected static id_counter = 0;
    protected readonly expandedCharacterCount = 32;
    protected readonly closedCharacterCount = 13;

    /**
     * Generate a unique id for this component instance
     */
    protected getId(): number {
        if (this.id === undefined) {
            this.id = MmuiDropChoiceComponent.id_counter;
            MmuiDropChoiceComponent.id_counter++;
        }
        return this.id;
    }

    /**
     * Constructor
     * @param props
     *  props.summaryRenderMode: Options "text" or "token".
     *      Specifying "text" the summary of selected choices is display as text.
     *      Specifying "token" the summary of selected choices is display as tokens.
     */
    constructor(props: P) {
        super(props);

        const state = {
            id: 'mmui_drop_choice_component_' + this.getId(),
            title: 'Select an option',
            summaryRenderMode: 'text',
            summaryTextDisplaySep: ', ',
            filterPlaceholder: 'Filter options',
            filterValue: '',
            choices: [],
            selectedChoiceCount: 0,
            isOpen: false,
            tooltipX: 0,
            tooltipY: 0,
            tooltipBody: '',
            hasTooltip: false,
        };

        if (props.id) {
            state.id = props.id;
        }

        if (props.title) {
            state.title = props.title;
        }

        if (props.summaryRenderMode) {
            state.summaryRenderMode = props.summaryRenderMode;
        }

        if (props.summaryTextDisplaySep) {
            state.summaryTextDisplaySep = props.summaryTextDisplaySep;
        }

        if (props.filterPlaceholder) {
            state.filterPlaceholder = props.filterPlaceholder;
        }

        if (props.data) {
            state.choices = props.data.choices;

            for (const choice of state.choices) {
                if (choice.isChecked) {
                    state.selectedChoiceCount = state.selectedChoiceCount + 1;
                }
            }
        }

        this.state = state;
    }

    /**
     * Toggles the isOpen state
     * @param e
     */
    headerClick(e, config?: any) {
        e.preventDefault();

        document.addEventListener('click', this.onBlur);

        const setStateCallback =
            config && config.setStateCallback
                ? config.setStateCallback
                : undefined;
        this.setState({ isOpen: !this.state.isOpen }, setStateCallback);
    }

    /**
     * Listens for clicks on the Drop Choice header.
     * @param e
     */
    onHeaderClick = (e: React.SyntheticEvent) => {
        this.headerClick(e);
    };

    /**
     * Detects user clicks outside of the visual area of the Component.
     * @param e
     */
    onBlur = (e) => {
        this.blur(e);
    };

    /**
     * Sets state to collapsed (isOpen == false) and cleans up the event listeners.
     * @param e
     */
    blur(e, config?: any) {
        if (this.state.isOpen) {
            let elmt = event.target as HTMLElement,
                isFieldClick = false;
            while (elmt) {
                isFieldClick = isFieldClick || elmt.id === this.state.id;
                if (isFieldClick) {
                    break;
                }
                elmt = elmt.parentElement;
            }

            if (!isFieldClick) {
                const setStateCallback =
                    config && config.setStateCallback
                        ? config.setStateCallback
                        : undefined;
                this.setState({ isOpen: false }, setStateCallback);
                document.removeEventListener('click', this.onBlur);
            }
        }
    }

    onTokenMouseOut = () => {
        this.setState({
            tooltipBody: '',
            tooltipX: 0,
            tooltipY: 0,
            hasTooltip: false,
        });
    };

    onTokenMouseOver = (e: React.SyntheticEvent) => {
        const tokenElmt = e.target as HTMLElement;
        if (tokenElmt.classList.contains('mmui-token-remove')) {
            return;
        }
        const componentElmt = document.getElementById(this.state.id);
        const tokenRect = tokenElmt.getBoundingClientRect();
        const componentRect = componentElmt.getBoundingClientRect();

        const x = tokenRect.x - componentRect.x;
        // also, subtract half the height of the tooltip + arrow
        const y = tokenRect.y - componentRect.y - 70;

        const tooltipBody = tokenElmt.dataset.tokenDisplay;
        if(tooltipBody){
            this.setState({
                tooltipBody: tooltipBody,
                tooltipX: x,
                tooltipY: y,
                hasTooltip: true,
            });
        }
    };

    abstract removeToken(choiceId);

    /**
     * Remove the target token from state
     * @param e: onClick event
     */
    onTokenRemove = (e: React.SyntheticEvent) => {
        e.preventDefault();
        e.stopPropagation();
        e.nativeEvent.stopImmediatePropagation();

        const choiceTokenElmt = e.target as HTMLElement,
            choiceId = choiceTokenElmt.dataset.tokenId;
        this.removeToken(choiceId);
    };

    abstract updateChoice(newChoice);

    /**
     * Listens for choice state changes
     * @param e
     */
    onChoiceChange = (e: React.SyntheticEvent) => {
        const newChoice = parseChoice(e.target as HTMLInputElement);
        this.updateChoice(newChoice);
    };

    abstract applyFilter(filterValue: string);

    /**
     * Listens for filter value changes and hides any choices with a display that doesn't contain the filter value.
     * @param e
     */
    onFilterChange = (e: React.SyntheticEvent) => {
        const filterValue = (e.target as HTMLInputElement).value;
        this.applyFilter(filterValue);
        this.setState({ filterValue: filterValue });
    };

    getSelectAllCheckRender() {
        return;
    }

    abstract getTextSummaryRender();

    abstract getTokenSummaryRender();

    abstract renderChoice(choice);

    /**
     * Render the Component
     */
    render() {

        const choiceInputElmts = this.state.choices.map((choice) => {
                return this.renderChoice(choice);
            }
        );

        let summaryDisplayRender;
        if (this.state.summaryRenderMode === 'token') {
            summaryDisplayRender = this.getTokenSummaryRender();
        } else {
            summaryDisplayRender = this.getTextSummaryRender();
        }

        let filterInput;
        if (this.props.hasFilterInput) {
            filterInput = (
                <input
                    className="form-control mmui-form-control mmui-drop-select-filter mmui-drop-select-choice-filter mmui-mb-1"
                    placeholder={this.state.filterPlaceholder}
                    onChange={this.onFilterChange}
                    value={this.state.filterValue}
                />
            );
        }

        const selectAllCheck = this.getSelectAllCheckRender();

        let tooltip;
        const isOpenTooltipCheck =
            this.state.isOpen &&
            this.state.tooltipBody.length > this.expandedCharacterCount;
        const isClosedTooltipCheck =
            !this.state.isOpen &&
            this.state.tooltipBody.length > this.closedCharacterCount;

        if (
            this.state.hasTooltip &&
            (isOpenTooltipCheck || isClosedTooltipCheck)
        ) {
            tooltip = (
                <MmuiTooltipComponent
                    header=""
                    body={this.state.tooltipBody}
                    x={this.state.tooltipX}
                    y={this.state.tooltipY}
                    location="top"
                />
            );
        }

        return this.renderDropdown(summaryDisplayRender, filterInput, selectAllCheck, choiceInputElmts, tooltip);
    }

    renderDropdown(summaryDisplayRender, filterInput, selectAllCheck, choiceInputElmts, tooltip){

        const classNameArray = ['mmui-drop-choice'];

        if (this.state.isOpen) {
            classNameArray.push('mmui-drop-choice-expanded');
        } else {
            classNameArray.push('mmui-drop-choice-collapsed');
        }

        const dropdownButtonClasses = ['btn', 'mmui-btn-dropdown'];
        const empty = this.state.choices.length < 1;
        return (
            <>
                {empty ? (
                    <div className="mmui-custom-select">
                        <div className="mmui-dropdown-toggle disabled"></div>
                        <select disabled={true}>
                            <option>No Data Available</option>
                        </select>
                    </div>
                ) : (
                    <div
                        id={this.state.id}
                        className={classNameArray.join(' ')}
                    >
                        <div className="mmui-drop-choice-body">
                            <div className="mmui-drop-select-summary">
                                <div className="mmui-dropdown-toggle"></div>
                                <div
                                    className={dropdownButtonClasses.join(' ')}
                                    onClick={this.onHeaderClick}
                                    role={'button'}
                                    tabIndex={0}
                                >
                                    {summaryDisplayRender}
                                </div>
                            </div>
                            <div className="mmui-drop-select-details">
                                <div className="mmui-drop-select-body">
                                    {filterInput}
                                    <div className="form-group mmui-form-group-scroll">
                                        {selectAllCheck}
                                        {choiceInputElmts}
                                    </div>
                                </div>
                            </div>
                        </div>
                        {tooltip}
                    </div>
                )}
            </>
        );
    }
}
