import * as React from 'react';
import * as _ from 'lodash';
import * as DefaultStyles from './style';
import { DropDownDrawer } from './DropDownDrawer';
import { KEY_CODE } from '../../constants/keyCodes';


export namespace InputDropDownDrawer {
    export interface Props {
        /**
         * Custom input component
         */
        customInput?: any;

        customInputProps?: any,
        /**
         * Custom component to show when no options
         */
        customEmptyStateMessage?: JSX.Element;
        /**
         * Message to show when no options
         */
        emptyStateMessage?: string;
        /**
         * Chunk to be highlighted inside the options
         */
        highlightedChunk?: string;
        /**
         * Value to display inside the input
         */
        inputValue: string;
        /**
         * Function called when an event happens inside the drop down (Arrow Up, Arrow Down, Enter, etc)
         * It passes the event and a boolean indicating if the dropdown should be showed or not
         */
        onDropDownEvent: (event, show: boolean) => void;
        /**
         * Function called when the user hits Enter, and the dropdown is closed
         * This function is not executed when the Enter is used to select an option
         */
        onEnterKeyPressed?: () => void;
        /**
         * Function called when an option is selected, passing it
         */
        onSelectOption: (event, option: { toString: () => string }) => void;
        /**
         * Function called when the value of the input changes
         */
        onValueChange?: (event) => void;
        /**
         * Placeholder of the input
         */
        placeholder?: string;
        /**
         * Indicates if the input is read only
         */
        readOnly?: boolean;
        /**
         * Indicates if the dropdown needs to be displayed
         */
        showDropDown: boolean;
        /**
         * Indicates if the input will have an icon at the right, as a combo box
         */
        showSelectIcon?: boolean;
        /**
         * List of options to be displayed
         */
        suggestOptions: { toString: () => string }[];
        /**
         * Prop to customize styles
         */
        styles?: Styles;

        id?: string;

        name?: string;


    }

    export interface State {
        /**
         * Indicates the current selected option
         */
        selectedOption: number;
    }

    export interface Styles {
        /**
         * Styles for the container of the entire component
         */
        Container?: any;
        /**
         * Styles for the component that renders the dropdown
         */
        DropDownDrawer?: DropDownDrawer.Styles;
        /**
         * Styles for the input
         */
        Input?: any;
        /**
         * Styles for the input icon on collapse
         */
        InputSelectIconOnCollapse?: any;
        /**
         * Styles for the input icon on expand
         */
        InputSelectIconOnExpand?: any;
        /**
         * Styles for the wrapper that contains both input and icon
         */
        InputWrapper?: any;
    }
}

/**
 * Component that displays an input with a dropdown, depending on the passed props
 * The only logic it has is to handle clicks and key presses
 * It changes the selected option or notifies when to open/close the dropdown
 * 
 * @version 1.0.0
 * 
 */
export class InputDropDownDrawer extends React.Component<InputDropDownDrawer.Props, InputDropDownDrawer.State> {
    /**
     * This div is injected in the component to know if a click is done inside it or not
     */
    private containerDiv: any;

    constructor(props: InputDropDownDrawer.Props) {
        super(props);

        this.state = {
            selectedOption: 0
        };
    }

    componentDidMount(): void {
        document.addEventListener('mousedown', this.handleClickOutside);
    }

    componentWillUnmount(): void {
        document.removeEventListener('mousedown', this.handleClickOutside);
    }

    /**
     * This function catches any click done outside of the component
     * It notifies that the dropdown should be not displayed anymore
     */
    handleClickOutside = (event): void => {
        if (this.containerDiv && !this.containerDiv.contains(event.target)) {
            this.props.onDropDownEvent(event, false);
        }
    };

    getNextOption = (currentOption: number, maxOptions: number): number => (
        (currentOption + 1) % maxOptions
    );

    getPreviousOption = (currentOption: number, maxOptions: number): number => (
        (currentOption + maxOptions - 1) % maxOptions
    );

    /**
     * This function handles important key press events
     */
    keyPressHandle = (event): void => {
        if (event.key === KEY_CODE.ARROW_DOWN) {
            const newSelectedOption = this.getNextOption(this.state.selectedOption, this.props.suggestOptions.length);
            this.props.onDropDownEvent(event, true);
            this.setState({
                selectedOption: newSelectedOption
            });
        }
        if (event.key === KEY_CODE.ARROW_UP) {
            const newSelectedOption = this.getPreviousOption(this.state.selectedOption, this.props.suggestOptions.length);
            this.props.onDropDownEvent(event, true);
            this.setState({
                selectedOption: newSelectedOption
            });
        }
        if (event.key === KEY_CODE.ENTER) {
            if (this.props.showDropDown && this.props.suggestOptions.length > 0) {
                this.props.onSelectOption(event, this.props.suggestOptions[this.state.selectedOption]);
            } else {
                if (this.props.onEnterKeyPressed) {
                    this.props.onEnterKeyPressed();
                }
            }

        }
        if ((event.key === KEY_CODE.ESCAPE) || (event.key === KEY_CODE.TAB)) {
            if (this.props.showDropDown) {
                this.props.onDropDownEvent(event, false);
            }
        }
    }

    handleInputClick = (): void => {
        if (this.props.showSelectIcon) {
            this.props.onDropDownEvent('CLICK', !this.props.showDropDown);
        }
    };

    handleOnInputChange = (event): void => {
        if (this.props.onValueChange) {
            this.props.onValueChange(event);
        }
        this.setState({ selectedOption: 0 });
    };

    selectInput = (Custom, Styles): JSX.Element => {
        if (Custom) {
            return <Custom
                {...this.props.customInputProps}
                onValueChange={this.handleOnInputChange}
                value={this.props.inputValue}
                readOnly={this.props.readOnly}
                pointerCursor={this.props.showSelectIcon}
                id={this.props.id && this.props.id + '-input-drop-down-drawer-input'}
                name={this.props.name && this.props.name + '-input-drop-down-drawer-input'}
            />;
        }
        else {
            return <Styles.Input
                className='input-drop-down-drawer-input'
                onChange={this.handleOnInputChange}
                placeholder={this.props.placeholder}
                readOnly={this.props.readOnly}
                pointerCursor={this.props.showSelectIcon}
                value={this.props.inputValue}
                id={this.props.id && this.props.id + '-input-drop-down-drawer-input'}
                name={this.props.name && this.props.name + '-input-drop-down-drawer-input'}
            />
        }
    }

    handleOnHoverOption = (event, value: { toString: () => string }, position: number) => {
        this.setState({
            selectedOption: position
        });
    }

    render(): JSX.Element {
        const Styles: InputDropDownDrawer.Styles = _.merge(DefaultStyles, this.props.styles);

        return (
            <Styles.Container
                className='input-drop-down-drawer-container'
                innerRef={(div) => this.containerDiv = div}
                onKeyDown={this.keyPressHandle}
                id={this.props.id && this.props.id + '-input-drop-down-drawer-container-div'}
                name={this.props.name && this.props.name + '-input-drop-down-drawer-container-div'}
            >
                <Styles.InputWrapper
                    className='input-drop-down-drawer-input-wrapper'
                    onClick={this.handleInputClick}
                    id={this.props.id && this.props.id + '-input-drop-down-drawer-input-wrapper-div'}
                    name={this.props.name && this.props.name + '-input-drop-down-drawer-input-wrapper-div'}
                >

                    {this.selectInput(this.props.customInput, Styles)}

                    {
                        this.props.showSelectIcon && (
                            this.props.showDropDown ?
                                <Styles.InputSelectIconOnExpand className='input-drop-down-drawer-input-select-icon' />
                                :
                                <Styles.InputSelectIconOnCollapse className='input-drop-down-drawer-input-select-icon' />
                        )
                    }
                </Styles.InputWrapper>
                {
                    this.props.showDropDown &&
                    <DropDownDrawer
                        customEmptyStateMessage={this.props.customEmptyStateMessage}
                        emptyStateMessage={this.props.emptyStateMessage}
                        highlightedChunk={this.props.highlightedChunk}
                        selectedOption={this.state.selectedOption}
                        onHoverOption={this.handleOnHoverOption}
                        onSelectOption={this.props.onSelectOption}
                        options={this.props.suggestOptions}
                        styles={Styles.DropDownDrawer}
                        id={this.props.id && this.props.id}
                        name={this.props.name && this.props.name}
                    />
                }
            </Styles.Container>
        );
    }
}
