import React, {useState, useRef} from 'react';
import PropTypes from 'prop-types';
import {Term} from '../data/model.js';
import * as fuzzy from '../data/fuzzy.js';
import * as util from '../util.js';

/**
 * Codes des touches du clavier par nom.
 */
const keys = Object.assign(Object.create(null), {
    'backspace': 8,
    'enter': 13,
    'left': 37,
    'up': 38,
    'right': 39,
    'down': 40,
});

/**
 * Zone de saisie des termes de recherche.
 *
 * @prop query Ensemble de termes déjà dans la requête.
 * @prop addQueryTerm Fonction de rappel pour ajouter un terme à la requête.
 * @prop removeQueryTerm Fonction de rappel pour ôter un terme de la requête.
 * @prop availableTerms Termes pouvant être ajoutés par l’utilisateur.
 */
const TermInput = ({
    query,
    addQueryTerm,
    removeQueryTerm,
    availableTerms,
}) =>
{
    // Valeur actuellement saisie dans le champ de recherche de termes
    const [value, setValue] = useState('');

    // Liste des termes suggérés par autocomplétion
    const {
        list: suggestions,
        setList: setSuggestions,
        focus: focusedSuggestion,
        setFocus: setFocusedSuggestion,
    } = util.useFocusableList();

    // Référence au champ de saisie
    const inputRef = useRef(null);

    /**
     * Valide l’entrée courante en l’ajoutant comme terme de la requête.
     *
     * @param term Terme à ajouter dans la requête.
     */
    const finalizeInput = term =>
    {
        addQueryTerm(term);
        setValue('');
        setSuggestions([]);

        if (inputRef.current !== null)
        {
            inputRef.current.focus();
        }
    };

    /**
     * Gère l’appui sur une touche dans le champ de saisie.
     *
     * @param ev Informations sur l’événement d’appui.
     */
    const handleInputKeyDown = ev =>
    {
        if (ev.keyCode === keys.enter && value)
        {
            // Touche Entrée : ajout de la suggestion ayant le focus
            ev.preventDefault();

            if (focusedSuggestion < suggestions.length)
            {
                finalizeInput(suggestions[focusedSuggestion]);
            }
        }
        else if (ev.keyCode === keys.backspace && !value)
        {
            // Touche Retour alors que le champ de saisie est vide :
            // retrait du dernier terme de la requête
            ev.preventDefault();
            removeQueryTerm(query.length - 1);
        }
        else if (ev.keyCode === keys.up)
        {
            // Touche Haut : focus de la suggestion précédente
            ev.preventDefault();
            setFocusedSuggestion(focusedSuggestion - 1);
        }
        else if (ev.keyCode === keys.down)
        {
            // Touche Bas : focus de la suggestion suivante
            ev.preventDefault();
            setFocusedSuggestion(focusedSuggestion + 1);
        }
    };

    /**
     * Gère la modification du contenu du champ de saisie.
     *
     * @param ev Informations sur l’événement de modification.
     */
    const handleInputChange = ev =>
    {
        const nextValue = ev.target.value;
        setValue(nextValue);

        if (nextValue.length === 0)
        {
            // On ne fait pas de suggestion si la valeur saisie est trop courte
            setSuggestions([]);
        }
        else
        {
            // Filtre les termes correspondant à la saisie
            setSuggestions(fuzzy.filterTerms(nextValue, availableTerms));
        }
    };

    return (
        <div className="TermInput">
            {query.map((term, index) => 
                <span
                    key={term.id}
                    className="TermInput_queryTerm"
                    title="Cliquez pour retirer ce terme de la recherche"
                    onClick={removeQueryTerm.bind(null, index)}
                >
                    {term.name}
                </span>
            )}

            <input
                autoFocus={true}
                type="text" className="TermInput_input"
                placeholder="Rechercher un symptôme, un signe ou une maladie…"
                value={value} ref={inputRef}
                onChange={handleInputChange}
                onKeyDown={handleInputKeyDown} />

            <ul className="TermInput_suggestions">
                {suggestions.map((suggestion, index) =>
                    <li
                        key={suggestion.id}
                        className={[
                            'TermInput_suggestion',
                            index === focusedSuggestion
                                ? 'TermInput_suggestion-focus'
                                : '',
                        ].join(' ')}
                        onMouseEnter={setFocusedSuggestion.bind(null, index)}
                        onClick={finalizeInput.bind(null, suggestion)}
                        title="Cliquez pour ajouter ce terme à la recherche"
                    >
                        {suggestion.name}
                        {suggestion.alias.length >= 1 ? (
                            <span
                                className="TermInput_suggestionAlias"
                                title={
                                    (suggestion.alias.length >= 2
                                        ? 'Autres noms : '
                                        : 'Autre nom : ')
                                    + suggestion.alias.join(', ')
                                }
                            >
                                {suggestion.alias.join(', ')}
                            </span>
                        ) : null}
                    </li>
                )}
            </ul>
        </div>
    );
};

TermInput.propTypes = {
    query: PropTypes.arrayOf(Term).isRequired,
    addQueryTerm: PropTypes.func.isRequired,
    removeQueryTerm: PropTypes.func.isRequired,
    availableTerms: PropTypes.arrayOf(Term).isRequired,
};

export default TermInput;
