import * as React from 'react';
import { cloneChoice, getMmuiCheckedDropChoices } from './utils';
import { MmuiDropChoiceComponent, Props } from './MmuiDropChoiceComponent';

/**
 * Drop Choice Component
 * A multi-select dropdown that allows for choice filtering.
 *
 * token: a thing serving as a visible or tangible representation of a fact
 */

export interface MmuiDropChoiceMultipleProps extends Props {
    hasParentSelection: boolean;
}

export class MmuiDropChoiceMultipleComponent<
    P extends MmuiDropChoiceMultipleProps
> extends MmuiDropChoiceComponent<P> {
    static defaultProps = {
        hasParentSelection: true,
    };

    /**
     * Constructor
     * @param props
     *
     */
    constructor(props: P) {
        super(props);
    }

    getCheckedChoices() {
        return getMmuiCheckedDropChoices(this.state.choices);
    }

    /**
     * Update the state of the choice that matches the provided changed choice.
     * @param changedChoice
     */
    updateChoice(changedChoice) {
        let choice,
            newChoice,
            childChoice,
            newChildChoice,
            newChoices = [],
            selectedChoiceCount = 0;
        for (let p = 0; p < this.state.choices.length; p++) {
            choice = this.state.choices[p];
            newChoice = cloneChoice(choice);

            // if changed choice is a parent
            if (newChoice.id == changedChoice.id) {
                newChoice.isChecked = changedChoice.isChecked;
                newChoice.isIndeterminate = false;

                if (choice.children && choice.children.length > 0) {
                    newChoice.children = [];
                    for (let c = 0; c < choice.children.length; c++) {
                        childChoice = choice.children[c];
                        newChildChoice = cloneChoice(childChoice);

                        //set all children to the same state as parent
                        newChildChoice.isChecked = newChoice.isChecked;

                        newChoice.children.push(newChildChoice);
                    }
                }
            } else if (choice.children && choice.children.length > 0) {
                newChoice.children = [];

                let isAllChecked = true,
                    isAnyChecked = false;
                for (let c = 0; c < choice.children.length; c++) {
                    childChoice = choice.children[c];
                    newChildChoice = cloneChoice(childChoice);
                    if (newChildChoice.id === changedChoice.id) {
                        newChildChoice.isChecked = changedChoice.isChecked;
                    }

                    isAllChecked = isAllChecked && newChildChoice.isChecked;
                    isAnyChecked = isAnyChecked || newChildChoice.isChecked;

                    newChoice.children.push(newChildChoice);
                }

                newChoice.isChecked = isAllChecked;
                if (isAllChecked) {
                    newChoice.isIndeterminate = false;
                } else {
                    newChoice.isIndeterminate = isAnyChecked;
                }
            }

            if (newChoice.isChecked) {
                selectedChoiceCount = selectedChoiceCount + 1;
            }

            newChoices.push(newChoice);
        }
        this.setState({
            choices: newChoices,
            selectedChoiceCount: selectedChoiceCount,
        });
    }

    applyFilter(filterValue: string) {
        let isVisibleParent,
            hasVisibleChildren,
            choice,
            newChoice,
            childChoice,
            newChildChoice,
            newChoices = [];
        for (let p = 0; p < this.state.choices.length; p++) {
            choice = this.state.choices[p];
            newChoice = cloneChoice(choice);
            if (filterValue) {
                isVisibleParent = newChoice.display
                    .toLowerCase()
                    .includes(filterValue.toLowerCase());

                hasVisibleChildren = false;
                if (choice.children && choice.children.length > 0) {
                    newChoice.children = [];
                    for (let c = 0; c < choice.children.length; c++) {
                        childChoice = choice.children[c];
                        newChildChoice = cloneChoice(childChoice);
                        newChildChoice.isVisible = newChildChoice.display
                            .toLowerCase()
                            .includes(filterValue.toLowerCase());
                        hasVisibleChildren =
                            hasVisibleChildren || newChildChoice.isVisible;

                        newChoice.children.push(newChildChoice);
                    }
                }

                newChoice.isVisible = isVisibleParent || hasVisibleChildren;
            } else {
                newChoice.isVisible = true;

                if (choice.children && choice.children.length > 0) {
                    newChoice.children = [];
                    for (let c = 0; c < choice.children.length; c++) {
                        childChoice = choice.children[c];
                        newChildChoice = cloneChoice(childChoice);
                        newChildChoice.isVisible = true;

                        newChoice.children.push(newChildChoice);
                    }
                }
            }

            newChoices.push(newChoice);
        }

        this.setState({ choices: newChoices });
    }

    /**
     * Called when the component's props or state has been updated.
     */
    componentDidUpdate() {
        if (this.props.hasParentSelection) {
            let choice, choiceElmt;
            for (let p = 0; p < this.state.choices.length; p++) {
                choice = this.state.choices[p];
                choiceElmt = document.getElementById(choice.id);
                choiceElmt.indeterminate = choice.isIndeterminate;
            }
        }
    }

    removeToken(choiceId: string, config?: any) {
        let choice,
            newChoice,
            childChoice,
            newChildChoice,
            newChoices = [],
            selectedChoiceCount = 0;
        for (let p = 0; p < this.state.choices.length; p++) {
            choice = this.state.choices[p];
            newChoice = cloneChoice(choice);
            if (newChoice.id === choiceId) {
                newChoice.isChecked = false;

                if (choice.children && choice.children.length > 0) {
                    newChoice.children = [];
                    for (let c = 0; c < choice.children.length; c++) {
                        childChoice = choice.children[c];
                        newChildChoice = cloneChoice(childChoice);
                        newChildChoice.isChecked = false;
                        newChoice.children.push(newChildChoice);
                    }
                }
            } else if (choice.children && choice.children.length > 0) {
                newChoice.children = [];

                let isAnyChecked = false;
                for (let c = 0; c < choice.children.length; c++) {
                    childChoice = choice.children[c];
                    newChildChoice = cloneChoice(childChoice);

                    if (newChildChoice.id === choiceId) {
                        newChildChoice.isChecked = false;
                        newChoice.isChecked = false;
                    }

                    isAnyChecked = isAnyChecked || newChildChoice.isChecked;

                    newChoice.children.push(newChildChoice);
                }

                if (!newChoice.isChecked) {
                    newChoice.isIndeterminate = isAnyChecked;
                }
            }

            if (newChoice.isChecked) {
                selectedChoiceCount = selectedChoiceCount + 1;
            }

            newChoices.push(newChoice);
        }

        const setStateCallback =
            config && config.setStateCallback
                ? config.setStateCallback
                : undefined;
        this.setState(
            {
                choices: newChoices,
                selectedChoiceCount: selectedChoiceCount,
            },
            setStateCallback
        );
    }

    /**
     * Build the summary of selected choices using just text.
     */
    getTextSummaryRender() {
        let summaryDisplayRender,
            summaryCountDisplay,
            //Converts the choice option object into a display value to be used in the summary.
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            choiceDisplayMapper = (choice, index = 0, parent = undefined) => {
                let display = choice.display,
                    isCheckedParent =
                        choice.isChecked &&
                        choice.children &&
                        choice.children.length > 0;
                if (parent) {
                    display = `${parent.display} - ${display}`;
                } else if (isCheckedParent) {
                    display = `${display} - All`;
                }

                return display;
            };
        if (this.state.choices.length > 0) {
            let choice,
                choiceChild,
                choiceDisplays = [];
            for (let i = 0; i < this.state.choices.length; i++) {
                choice = this.state.choices[i];
                if (choice.isChecked) {
                    choiceDisplays.push(choiceDisplayMapper(choice, i));
                } else if (choice.children) {
                    for (let j = 0; j < choice.children.length; j++) {
                        choiceChild = choice.children[j];
                        if (choiceChild.isChecked) {
                            choiceDisplays.push(
                                choiceDisplayMapper(choiceChild, j, choice)
                            );
                        }
                    }
                }
            }

            if (choiceDisplays.length > 0) {
                if (this.state.isOpen) {
                    summaryDisplayRender = (
                        <div className="mmui-drop-select-title">
                            <span style={{ whiteSpace: 'normal' }}>
                                {choiceDisplays.join(
                                    this.state.summaryTextDisplaySep
                                )}
                            </span>
                        </div>
                    );
                } else {
                    if (choiceDisplays.length > 1) {
                        const moreCountDisplay = `+${
                            choiceDisplays.length - 1
                        } more`;
                        summaryCountDisplay = (
                            <span className="mmui-tokens-summary-display">
                                {moreCountDisplay}
                            </span>
                        );
                    }

                    summaryDisplayRender = (
                        <div className="mmui-drop-select-title">
                            <span>{choiceDisplays[0]}</span>{' '}
                            {summaryCountDisplay}
                        </div>
                    );
                }
            }
            // display title, if no choices have been selected
            else {
                summaryDisplayRender = (
                    <div className="mmui-drop-select-title">
                        {this.state.title}
                    </div>
                );
            }
        }
        // display title, if there are no choice options
        else {
            summaryDisplayRender = (
                <div className="mmui-drop-select-title">{this.state.title}</div>
            );
        }

        return summaryDisplayRender;
    }

    getTokenRender(choice, parent = undefined){

        let key = `token-${choice.id}`,
            display = choice.display,
            isCheckedParent =
                choice.isChecked &&
                choice.children &&
                choice.children.length > 0;
        if (parent) {
            display = `${parent.display} - ${display}`;
        } else if (isCheckedParent) {
            display = `${display} - All`;
        }

        return (
            <div
                key={key}
                id={key}
                className="mmui-token badge mmui-badge"
                data-token-id={choice.id}
                data-token-display={choice.display}
                onMouseOver={this.onTokenMouseOver.bind(this)}
                onMouseOut={this.onTokenMouseOut.bind(this)}
            >
                <span className="mmui-token-display">{display}</span>
                <a
                    href="#"
                    className="mmui-token-remove"
                    data-token-id={choice.id}
                    onClick={this.onTokenRemove}
                >
                    <i className="fa fa-times-circle"></i>
                </a>
            </div>
        )
    }

    /**
     * Build the summary of selected choices using tokens elements.
     */
    getTokenSummaryRender() {
        let summaryDisplay,
            summaryCountDisplay,
            choiceTokenElmts = [],
            //Converts the choice option object into a token element to be used in the summary.
            // eslint-disable-next-line @typescript-eslint/no-unused-vars
            choiceTokenElmtMapper = (choice, index = 0, parent = undefined) => {
                return this.getTokenRender(choice, parent);
            };
        if (this.state.choices.length > 0) {
            let choice, choiceChild;

            for (let i = 0; i < this.state.choices.length; i++) {
                choice = this.state.choices[i];
                if (choice.isChecked) {
                    choiceTokenElmts.push(choiceTokenElmtMapper(choice, i));
                } else if (choice.children && choice.children.length > 0) {
                    for (let j = 0; j < choice.children.length; j++) {
                        choiceChild = choice.children[j];
                        if (choiceChild.isChecked) {
                            choiceTokenElmts.push(
                                choiceTokenElmtMapper(choiceChild, j, choice)
                            );
                        }
                    }
                }
            }

            if (choiceTokenElmts.length > 0) {
                if (this.state.isOpen) {
                    summaryDisplay = (
                        <div className="mmui-tokens">{choiceTokenElmts}</div>
                    );
                } else {
                    summaryDisplay = (
                        <div className="mmui-tokens">{choiceTokenElmts[0]}</div>
                    );
                }

                if (!this.state.isOpen && choiceTokenElmts.length > 1) {
                    const moreCountDisplay = `+${
                        choiceTokenElmts.length - 1
                    } more`;
                    summaryCountDisplay = (
                        <div className="mmui-tokens-summary-display">
                            {moreCountDisplay}
                        </div>
                    );
                }
            }
            // display title, if no choices have been selected
            else {
                summaryDisplay = (
                    <div className="mmui-drop-select-title">
                        {this.state.title}
                    </div>
                );
            }
        }
        // display title, if there are no choice options
        else {
            summaryDisplay = (
                <div className="mmui-drop-select-title">{this.state.title}</div>
            );
        }

        return (
            <React.Fragment>
                {summaryDisplay}
                {summaryCountDisplay}
            </React.Fragment>
        );
    }

    /**
     * Select all or Unselect all based on target checked state.
     * @param e
     */
    onChangeAllSelection = (e: React.SyntheticEvent) => {
        let choice,
            newChoice,
            childChoice,
            newChildChoice,
            newChoices = [],
            selectedChoiceCount = 0,
            isChecked = (e.target as HTMLInputElement).checked;

        for (let p = 0; p < this.state.choices.length; p++) {
            choice = this.state.choices[p];
            newChoice = cloneChoice(choice);
            newChoice.isChecked = isChecked;
            newChoice.isIndeterminate = false;

            if (choice.children && choice.children.length > 0) {
                newChoice.children = [];
                for (let c = 0; c < choice.children.length; c++) {
                    childChoice = choice.children[c];
                    newChildChoice = cloneChoice(childChoice);
                    newChildChoice.isChecked = isChecked;

                    newChoice.children.push(newChildChoice);
                }
            }

            if (newChoice.isChecked) {
                selectedChoiceCount = selectedChoiceCount + 1;
            }

            newChoices.push(newChoice);
        }

        this.setState({
            choices: newChoices,
            selectedChoiceCount: selectedChoiceCount,
        });
    };

    /**
     * Return the number of selected choice options.
     * @return
     */
    static getSelectedChoicesCount(choices): number {
        let selectedCount = 0,
            choice,
            childChoice;

        for (let p = 0; p < choices.length; p++) {
            choice = choices[p];
            if (choice.isChecked) {
                selectedCount++;
            }

            if (choice.children && choice.children.length > 0) {
                for (let c = 0; c < choice.children.length; c++) {
                    childChoice = choice.children[c];
                    if (childChoice.isChecked) {
                        selectedCount++;
                    }
                }
            }
        }

        return selectedCount;
    }

    /**
     * Return the number of selected choice options from the current state.
     */
    getSelectedCount(): number {
        return MmuiDropChoiceMultipleComponent.getSelectedChoicesCount(
            this.state.choices
        );
    }

    getSelectAllCheckRender() {
        let selectAllCheck;
        if (this.props.hasSelectAll) {
            const selectAllCheckId = `select-all-${this.state.id}`,
                isAllSelected =
                    this.state.selectedChoiceCount ===
                    this.state.choices.length;

            selectAllCheck = (
                <div className="form-check mmui-form-check">
                    <input
                        id={selectAllCheckId}
                        className="form-check-input"
                        type="checkbox"
                        name="select-all"
                        value="select-all"
                        onChange={this.onChangeAllSelection}
                        checked={isAllSelected}
                    />
                    <label
                        className="form-check-label mmui-form-check-label"
                        htmlFor={selectAllCheckId}
                    >
                        Select All
                    </label>
                </div>
            );
        }

        return selectAllCheck;
    }

    renderChoiceInput(choice) {
        const choiceInputProps: any = {
                id: choice.id,
                name: choice.name,
                value: choice.value,
                checked: choice.isChecked,
            },
            choiceStyle: any = { display: 'block' };
        if (choice.isParent) {
            choiceInputProps['data-isparent'] = 'true';
        } else {
            choiceInputProps['data-parentid'] = choice.parentValue;
        }

        if (!choice.isVisible) {
            choiceStyle.display = 'none';
        }

        if (!this.props.hasParentSelection && choice.children) {
            return <span>{choice.display}</span>;
        }

        return (
            <div key={choice.id} style={choiceStyle} >
                <div className="form-check mmui-form-check d-flex">
                    <input
                        className="form-check-input"
                        type="checkbox"
                        {...choiceInputProps}
                        data-display={choice.display}
                        onChange={this.onChoiceChange}
                    />
                    <label
                        className="form-check-label mmui-form-check-label"
                        htmlFor={choice.id}
                    >
                        {choice.display}
                    </label>
                </div>
            </div>
        );
    }

    renderChoice(choice) {
        let key = `choice-input-${choice.id}`,
            choiceInputRender = this.renderChoiceInput(choice),
            childChoiceInputRenders;

        if (choice.children) {
            childChoiceInputRenders = choice.children.map((childChoice) => {
                return this.renderChoiceInput(childChoice);
            });
        }

        return (
            <div key={key}>
                {choiceInputRender}
                <div className="mmui-pl-3">{childChoiceInputRenders}</div>
            </div>
        );
    }
}
