import React from "react";
import PropTypes from "prop-types";

//TODO fix autosuggest https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
import Autosuggest from "react-autosuggest"; //See: https://github.com/moroshko/react-autosuggest

import IDUtil from "../../../util/IDUtil";
import ExternalAPI from "../../../api/ExternalAPI";
import debounce from "debounce";
import { AnnotationEvents } from "../AnnotationClient";
import Strings from "../_Strings";
import ClassificationSelector from "./ClassificationSelector";
import { getAnnotationsPerType } from "../AnnotationHelpers";
import { CLASSIFICATION } from "../../../util/AnnotationConstants";

export default class ClassificationForm extends React.PureComponent {
    constructor(props) {
        super(props);
        this.config = this.props.annotationClient.config.motivationConfig[
            "classification"
        ];
        this.xhrs = [];
        this.getSuggestions = debounce(this.getSuggestions.bind(this), 400);

        this.state = {
            vocabulary: this.config.vocabularies
                ? this.config.vocabularies[0]
                : null,
            value: "", //the label of the selected classification (autocomplete)
            suggestionId: null, //stores the id/uri of the selected classification (e.g. GTAA URI)
            suggestions: [], //current list of suggestions shown
            isLoading: false, //loading the suggestions from the server
            showClassificationSelector: false, // show classification selector
        };
    }

    /* --------------- ANNOTATION CLIENT EVENT HANDLING -------------- */

    componentDidMount() {
        this.props.annotationClient.events.bind(
            AnnotationEvents.ON_SAVE,
            this.onSave
        );
    }

    componentWillUnmount() {
        this.props.annotationClient.events.unbind(
            AnnotationEvents.ON_SAVE,
            this.onSave
        );

        //cancel all previous outgoing requests
        for (let x = this.xhrs.length; x > 0; x--) {
            this.xhrs[x - 1].abort();
            this.xhrs.pop();
        }
    }

    onSave = () => {
        this.forceUpdate();
    };

    /* ------------------- CRUD / loading of classifications ------------------- */

    addClassification = (e) => {
        if (this.state.value != "") {
            let suggestionId = this.state.suggestionId;
            if (this.state.vocabulary && this.state.vocabulary == "custom") {
                suggestionId = IDUtil.guid();
            }
            const classification = {
                id: suggestionId,
                label: this.state.value,
                vocabulary: this.state.vocabulary,
                annotationType: "classification",
            };
            //after setting the state save the (annotation) body
            this.setState(
                {
                    value: "",
                    suggestionId: suggestionId,
                },
                () => {
                    this.props.annotationClient.saveBodyElement(
                        classification,
                        false,
                        true,
                        this.props.annotation
                    );
                }
            );
        }
    };

    setVocabulary = (event) =>
        this.setState({ vocabulary: event.target.value });

    getSuggestions(value, callback) {
        //cancel all previous outgoing requests
        for (let x = this.xhrs.length; x > 0; x--) {
            this.xhrs[x - 1].abort();
            this.xhrs.pop();
        }
        const xhr = ExternalAPI.autocomplete(
            this.state.vocabulary,
            value,
            '',
            callback
        );
        this.xhrs.push(xhr);
    }

    /* ------------------- REACT-AUTOSUGGEST FUNCTIONS ------------------- */

    loadSuggestions = (value) => {
        this.setState({
            isLoading: true,
            suggestions: [],
        });
        if (value.value === this.state.chosenValue) {
            this.setState({
                isLoading: false,
            });
        } else {
            this.getSuggestions(value.value, (data) => {
                if (!data || data.error || !Array.isArray(data)) {
                    this.setState({
                        isLoading: false,
                        suggestions: [],
                    });
                    console.error("Autocomplete failed");
                } else {
                    this.setState({
                        isLoading: false,
                        suggestions: data,
                    });
                }
            });
        }
    };

    onSuggestionsFetchRequested = (value) => this.loadSuggestions(value);
    onSuggestionsClearRequested = () => this.setState({ suggestions: [] });

    getSuggestionValue = (suggestion) => {
        this.setState({ suggestionId: suggestion.value });
        return suggestion.label.split("|")[0];
    };

    //TODO the rendering should be adapted for different vocabularies
    renderSuggestion = (suggestion) => {
        const arr = suggestion.label.split("|");
        let label = arr[1];
        const scopeNote = arr[2] ? "(" + arr[2] + ")" : "";
        if (this.state.vocabulary == "GTAA") {
            switch (arr[1]) {
                case "Persoon":
                    label = (
                        <span className="label label-warning">Persoon</span>
                    );
                    break;
                case "Maker":
                    label = <span className="label label-warning">Maker</span>;
                    break;
                case "Geografisch":
                    label = (
                        <span className="label label-success">Locatie</span>
                    );
                    break;
                case "Naam":
                    label = <span className="label label-info">Naam</span>;
                    break;
                case "Onderwerp":
                    label = (
                        <span className="label label-primary">Onderwerp</span>
                    );
                    break;
                case "Genre":
                    label = <span className="label label-default">Genre</span>;
                    break;
                case "B&G Onderwerp":
                    label = (
                        <span className="label label-danger">
                            B&G Onderwerp
                        </span>
                    );
                    break;
                default:
                    label = (
                        <span className="label label-default">Concept</span>
                    );
                    break;
            }
        } else if (this.state.vocabulary == "DBpedia") {
            label = <span className="label label-default">Concept</span>;
        } else if (this.state.vocabulary == "UNESCO") {
            switch (arr[1]) {
                case "Education":
                    label = (
                        <span className="label label-warning">{arr[1]}</span>
                    );
                    break;
                case "Science":
                    label = (
                        <span className="label label-warning">{arr[1]}</span>
                    );
                    break;
                case "Social and human sciences":
                    label = (
                        <span className="label label-success">{arr[1]}</span>
                    );
                    break;
                case "Information and communication":
                    label = <span className="label label-info">{arr[1]}</span>;
                    break;
                case "Politics, law and economics":
                    label = (
                        <span className="label label-primary">{arr[1]}</span>
                    );
                    break;
                case "Countries and country groupings":
                    label = (
                        <span className="label label-default">{arr[1]}</span>
                    );
                    break;
                default:
                    label = (
                        <span className="label label-default">{arr[1]}</span>
                    );
                    break;
            }
        }
        return (
            <span>
                {arr[0]}&nbsp;{label}&nbsp;{scopeNote}
            </span>
        );
    };

    onChange(event, { newValue }) {
        this.setState({
            chosenValue: newValue,
            value: newValue,
        });
    }

    // after selection a suggestion, add it immediately
    onSelect = (e, { suggestionValue }) => {
        this.setState(
            {
                chosenValue: suggestionValue,
                value: suggestionValue,
            },
            () => {
                this.addClassification();
            }
        );
    };

    onSubmit = (e) => {
        e.preventDefault();
        this.addClassification();
    };

    /* ------------------- CLASSIFICATION SELECTOR ------------------- */

    showClassificationSelector = () => {
        this.setState({ showClassificationSelector: true });
    };

    hideClassificationSelector = () => {
        this.setState({ showClassificationSelector: false });
    };

    toggleClassificationSelector = () => {
        this.setState({
            showClassificationSelector: !this.state.showClassificationSelector,
        });
    };

    toggleClassificationFromSelector = (_classification) => {
        // Check if there is an existing classification with the same id
        let existingClassification = null;

        getAnnotationsPerType([CLASSIFICATION], this.props.annotation)[
            CLASSIFICATION
        ].some((classification) => {
            if (classification.id == _classification.id) {
                existingClassification = classification;
                return true;
            }
            return false;
        });
        if (existingClassification) {
            this.props.deleteAnnotation(existingClassification);
            return;
        }

        // Create a fresh classification and add it to the annotation
        const classification = Object.assign({}, _classification);
        delete classification.annotationId;

        this.props.annotationClient.saveBodyElement(
            classification,
            false,
            true,
            this.props.annotation
        );
    };

    /* ------------------- RENDER FUNCTIONS ------------------- */

    renderVocabularySelector = (
        vocabularies,
        activeVocabulary,
        setVocabFunc
    ) => {
        const options = vocabularies.map((v, index) => (
            <option key={"__v__" + index} value={v}>
                {v}
            </option>
        ));
        options.push(
            <option key="__v__custom" value="custom">
                Custom (no external lookup)
            </option>
        );
        return (
            <select value={activeVocabulary} onChange={setVocabFunc}>
                {options}
            </select>
        );
    };

    render() {
        const vocabSelector = this.renderVocabularySelector(
            this.config.vocabularies,
            this.state.vocabulary,
            this.setVocabulary
        );
        const inputProps = {
            placeholder: "Search",
            value: this.state.value,
            onChange: this.onChange.bind(this),
        };

        const currentClassificationsButton = (
            <div
                className="current-classifications-button"
                title={Strings.ANNOTATION_TYPE_CLASSIFICATION_CURRENT}
                onClick={this.showClassificationSelector}
            ></div>
        );

        const classificationSelector = this.state.showClassificationSelector ? (
            <ClassificationSelector
                activeAnnotation={this.props.annotation}
                onClose={this.hideClassificationSelector}
                onSelect={this.toggleClassificationFromSelector}
            />
        ) : null;

        return (
            <div className={IDUtil.cssClassName("classification-form")}>
                {classificationSelector}

                <form onSubmit={this.onSubmit}>
                    <div className="filter">
                        <strong>
                            {Strings.ANNOTATION_TYPE_CLASSIFICATION_VOCABULARY}
                        </strong>
                        {vocabSelector}
                        <div className="spacer" />
                        {currentClassificationsButton}
                    </div>
                    <Autosuggest
                        ref={(input) => (this.classifications = input)}
                        suggestions={this.state.suggestions}
                        onSuggestionsFetchRequested={
                            this.onSuggestionsFetchRequested
                        }
                        onSuggestionsClearRequested={
                            this.onSuggestionsClearRequested
                        }
                        getSuggestionValue={this.getSuggestionValue}
                        renderSuggestion={this.renderSuggestion}
                        inputProps={inputProps}
                        onSuggestionSelected={this.onSelect}
                    />
                </form>
            </div>
        );
    }
}

ClassificationForm.propTypes = {
    annotationClient: PropTypes.object.isRequired,
    annotation: PropTypes.object.isRequired,
    deleteAnnotation: PropTypes.func.isRequired,
};
