/*
 * 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.form;

import java.util.EnumSet;
import java.util.Set;

import com.google.gwt.safehtml.shared.SafeHtml;
import elemental2.dom.HTMLElement;
import elemental2.dom.HTMLInputElement;
import org.jboss.hal.ballroom.form.TagsManager.Defaults;
import org.jboss.hal.ballroom.form.TagsManager.Options;
import org.jboss.hal.resources.CSS;
import org.jboss.hal.resources.Ids;

import static org.jboss.gwt.elemento.core.Elements.div;
import static org.jboss.gwt.elemento.core.Elements.input;
import static org.jboss.gwt.elemento.core.InputType.text;
import static org.jboss.hal.ballroom.form.Decoration.DEFAULT;
import static org.jboss.hal.ballroom.form.Decoration.DEPRECATED;
import static org.jboss.hal.ballroom.form.Decoration.RESTRICTED;
import static org.jboss.hal.ballroom.form.Decoration.SUGGESTIONS;
import static org.jboss.hal.resources.CSS.*;
import static org.jboss.hal.resources.Ids.uniqueId;
import static org.jboss.hal.resources.UIConstants.HASH;

public abstract class TagsItem<T> extends AbstractFormItem<T> {

    private final Set<Decoration> editingDecorations;
    private final TagsMapping<T> mapping;
    private final TagsEditingAppearance editingAppearance;

    protected TagsItem(final String name, final String label, final SafeHtml inputHelp,
            final Set<Decoration> editingDecorations, final TagsMapping<T> mapping) {
        super(name, label, null);

        this.editingDecorations = editingDecorations;
        this.mapping = mapping;

        // read-only appearance
        addAppearance(Form.State.READONLY, new TagsReadOnlyAppearance());

        // editing appearance
        editingAppearance = new TagsEditingAppearance(input(text).css(formControl, tags).asElement(), inputHelp,
                editingDecorations, mapping);
        addAppearance(Form.State.EDITING, editingAppearance);
    }

    @Override
    public boolean supportsExpressions() {
        return false;
    }

    public String allowedCharacters() {
        return null;
    }

    @Override
    public void onSuggest(final String suggestion) {
        if (editingDecorations.contains(SUGGESTIONS)) {
            editingAppearance.onSuggest(suggestion);
        }
    }


    private class TagsReadOnlyAppearance extends ReadOnlyAppearance<T> {

        TagsReadOnlyAppearance() {
            super(EnumSet.of(DEFAULT, DEPRECATED, RESTRICTED));
        }

        @Override
        protected String name() {
            return "TagsReadOnlyAppearance";
        }

        @Override
        public String asString(T value) {
            return mapping.asString(value);
        }
    }


    private class TagsEditingAppearance extends EditingAppearance<T> {

        private final HTMLElement tagsContainer;
        private final TagsMapping<T> mapping;

        TagsEditingAppearance(HTMLInputElement inputElement, SafeHtml inputHelp,
                Set<Decoration> supportedDecorations, TagsMapping<T> mapping) {
            super(supportedDecorations, inputElement);
            this.mapping = mapping;

            tagsContainer = div().css(tagManagerContainer)
                    .id(Ids.build("tags", "container", uniqueId()))
                    .asElement();

            helpBlock.classList.add(CSS.hint);
            helpBlock.innerHTML = inputHelp.asString();

            inputContainer.appendChild(tagsContainer);
            inputContainer.appendChild(helpBlock);
            inputGroup.classList.add(properties);
        }

        @Override
        protected String name() {
            return "TagsEditingAppearance";
        }

        @Override
        public void attach() {
            super.attach();
            Options options = Defaults.get();
            options.tagsContainer = HASH + tagsContainer.id;
            options.validator = mapping.validator();

            TagsManager.Api api = TagsManager.Api.element(inputElement);
            api.tagsManager(options);
            api.onRefresh((event, cst) -> {
                modifyValue(mapping.parse(cst));
                clearError();
            });
            api.onInvalid((event, cst) -> {
                String message = allowedCharacters() != null ? MESSAGES.invalidTagFormat(allowedCharacters())
                        :  MESSAGES.invalidFormat();
                showError(message);
            });
            api.onDuplicated((event, cst) -> showError(MESSAGES.duplicateResource(CONSTANTS.entry())));
        }

        @Override
        public void showValue(final T value) {
            if (attached) {
                TagsManager.Api.element(inputElement).setTags(mapping.tags(value));
            } else {
                inputElement.value = asString(value);
            }
        }

        @Override
        public String asString(final T value) {
            return mapping.asString(value);
        }

        @Override
        public void clearValue() {
            if (attached) {
                TagsManager.Api.element(inputElement).removeAll();
            } else {
                inputElement.value = "";
            }
        }

        void onSuggest(final String suggestion) {
            if (attached) {
                TagsManager.Api.element(inputElement).addTag(suggestion);
            }
            setModified(true);
            setUndefined(false);
        }

        @Override
        void applyInvalid(final String errorMessage) {
            root.classList.add(hasError);
            helpBlock.classList.remove(CSS.hint);
            helpBlock.textContent = errorMessage;
        }

        @Override
        void unapplyInvalid() {
            root.classList.remove(hasError);
            helpBlock.classList.add(CSS.hint);
            helpBlock.innerHTML = MESSAGES.propertiesHint().asString();
        }
    }
}

@@ -1844,22 +1844,16 @@
 agsItem(
-final 
 String n
@@ -1856,22 +1856,16 @@
 ng name,
- final
  String 
@@ -1870,22 +1870,16 @@
 g label,
- final
  SafeHtm
@@ -1903,22 +1903,16 @@
         
-final 
 Set%3CDeco
@@ -1938,22 +1938,16 @@
 rations,
- final
  TagsMap
@@ -2638,38 +2638,32 @@
  void onSuggest(
-final 
 String suggestio
@@ -4920,112 +4920,8 @@
 %7D);%0A
-            api.onDuplicated((event, cst) -%3E showError(MESSAGES.duplicateResource(CONSTANTS.entry())));%0A
     
@@ -4975,22 +4975,16 @@
 owValue(
-final 
 T value)
@@ -5247,22 +5247,16 @@
 sString(
-final 
 T value)
@@ -5571,22 +5571,16 @@
 Suggest(
-final 
 String s
@@ -5827,22 +5827,16 @@
 Invalid(
-final 
 String e

Fix wrong validation in TagsItem