/*
 * Copyright 2015-2016 Red Hat, Inc, and individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.hal.ballroom.autocomplete;

import elemental.events.Event;
import elemental2.dom.Element;
import jsinterop.annotations.JsConstructor;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsType;
import org.jboss.gwt.elemento.core.Elements;
import org.jboss.hal.ballroom.Attachable;
import org.jboss.hal.ballroom.form.AbstractFormItem;
import org.jboss.hal.ballroom.form.FormItem;
import org.jboss.hal.ballroom.form.SuggestHandler;
import org.jetbrains.annotations.NonNls;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static elemental2.dom.DomGlobal.document;
import static elemental2.dom.DomGlobal.setTimeout;
import static jsinterop.annotations.JsPackage.GLOBAL;
import static org.jboss.gwt.elemento.core.Elements.asHtmlElement;
import static org.jboss.gwt.elemento.core.Elements.htmlElements;
import static org.jboss.hal.ballroom.form.Form.State.EDITING;
import static org.jboss.hal.resources.CSS.autocompleteSuggestions;

/**
 * Java wrapper for <a href="https://github.com/Pixabay/JavaScript-autoComplete">javascript-auto-complete</a>
 *
 * @author Harald Pehl
 * @see <a href="https://github.com/Pixabay/JavaScript-autoComplete">https://github.com/Pixabay/JavaScript-autoComplete</a>
 */
public class AutoComplete implements SuggestHandler, Attachable {

    @JsType(isNative = true, namespace = GLOBAL, name = "autoComplete")
    static class Api {

        @JsConstructor
        @SuppressWarnings("UnusedParameters")
        Api(Options options) {}

        @JsMethod
        private native void destroy();
    }


    @NonNls static final Logger logger = LoggerFactory.getLogger(AutoComplete.class);

    private FormItem formItem;
    private Api api;
    private Options options;

    protected void init(Options options) {
        this.options = options;
    }

    @Override
    public void attach() {
        if (api == null) {
            options.selector = formItemSelector();
            options.onSelect = (event, item, renderedItem) -> {
                if (formItem() instanceof AbstractFormItem) {
                    ((AbstractFormItem) formItem()).onSuggest(item);
                }
            };
            api = new Api(options);
        }
    }

    @Override
    public void detach() {
        if (api != null) {
            api.destroy();
            api = null;
        }
    }

    @Override
    public void showAll() {
        Element element = document.getElementById(formItem().getId(EDITING));
        setTimeout((o) -> {
            element.blur();
            triggerEvent(element, Event.KEYUP, "", 0); // to reset 'last_val' in autoComplete.js
            triggerEvent(element, Event.KEYUP, SHOW_ALL_VALUE, SHOW_ALL_VALUE.charAt(0));
            element.focus();
        }, 351); // timeout must be > 350, which is used in autoComplete.js
    }

    private native void triggerEvent(Element element, String type, String key, int keyCode) /*-{
        element.value = key;
        if ($doc.createEvent) {
            event = new Event(type);
            event.keyCode = keyCode;
            event.which = keyCode;
            element.dispatchEvent(event);
        } else {
            event = $doc.createEventObject();
            event.keyCode = keyCode;
            event.which = keyCode;
            element.fireEvent("on" + type, event);
        }
    }-*/;

    @Override
    public void close() {
        Elements.stream(document.querySelectorAll(autocompleteSuggestions))
                .filter(htmlElements())
                .map(asHtmlElement())
                .filter(Elements::isVisible)
                .forEach(element -> Elements.setVisible(element, false));
    }

    @Override
    public void setFormItem(FormItem formItem) {
        this.formItem = formItem;
    }

    private FormItem formItem() {
        if (formItem == null) {
            throw new IllegalStateException(
                    "No form item assigned. Please call AutoComplete.setFormItem(FormItem) before using this as a SuggestHandler.");
        }
        return formItem;
    }

    private String formItemSelector() {
        return "#" + formItem().getId(EDITING);
    }
}

@@ -686,18 +686,27 @@
 ntal
-.events.Ev
+2.dom.HTMLInputElem
 ent;
@@ -728,20 +728,26 @@
 al2.dom.
-Elem
+KeyboardEv
 ent;%0Aimp
@@ -3004,32 +3004,80 @@
 %0A%0A    @Override%0A
+    @SuppressWarnings(%22HardCodedStringLiteral%22)%0A
     public void 
@@ -3092,24 +3092,33 @@
 ) %7B%0A        
+HTMLInput
 Element elem
@@ -3122,16 +3122,35 @@
 lement =
+ (HTMLInputElement)
  documen
@@ -3246,24 +3246,86 @@
 ent.blur();%0A
+            KeyboardEvent event = new KeyboardEvent(%22keyup%22);%0A
             
@@ -3346,27 +3346,21 @@
 lement, 
-E
+e
 vent
-.KEYUP
 , %22%22, 0)
@@ -3441,19 +3441,13 @@
 nt, 
-E
+e
 vent
-.KEYUP
 , SH
@@ -3636,16 +3636,25 @@
 erEvent(
+HTMLInput
 Element 
@@ -3666,19 +3666,27 @@
 nt, 
-String type
+KeyboardEvent event
 , St
@@ -3746,81 +3746,8 @@
 ey;%0A
-        if ($doc.createEvent) %7B%0A            event = new Event(type);%0A    
     
@@ -3775,36 +3775,32 @@
 eyCode;%0A        
-  
-  
 event.which = ke
@@ -3798,36 +3798,32 @@
 hich = keyCode;%0A
-    
         element.
@@ -3848,204 +3848,8 @@
 t);%0A
-        %7D else %7B%0A            event = $doc.createEventObject();%0A            event.keyCode = keyCode;%0A            event.which = keyCode;%0A            element.fireEvent(%22on%22 + type, event);%0A        %7D%0A
     

Fix key-up event dispatching in auto-complete