/*
 * Copyright 2015 JBoss, by Red Hat, Inc
 *
 * 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.uberfire.ext.properties.editor.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Widget;
import org.gwtbootstrap3.client.ui.Anchor;
import org.gwtbootstrap3.client.ui.Button;
import org.gwtbootstrap3.client.ui.Column;
import org.gwtbootstrap3.client.ui.Container;
import org.gwtbootstrap3.client.ui.Form;
import org.gwtbootstrap3.client.ui.Heading;
import org.gwtbootstrap3.client.ui.HelpBlock;
import org.gwtbootstrap3.client.ui.InputGroup;
import org.gwtbootstrap3.client.ui.InputGroupButton;
import org.gwtbootstrap3.client.ui.Panel;
import org.gwtbootstrap3.client.ui.PanelBody;
import org.gwtbootstrap3.client.ui.PanelCollapse;
import org.gwtbootstrap3.client.ui.PanelGroup;
import org.gwtbootstrap3.client.ui.PanelHeader;
import org.gwtbootstrap3.client.ui.Row;
import org.gwtbootstrap3.client.ui.constants.ColumnSize;
import org.gwtbootstrap3.client.ui.constants.FormType;
import org.gwtbootstrap3.client.ui.constants.HeadingSize;
import org.gwtbootstrap3.client.ui.constants.IconType;
import org.gwtbootstrap3.client.ui.constants.Toggle;
import org.jboss.errai.ioc.client.container.IOC;
import org.jboss.errai.ioc.client.container.SyncBeanDef;
import org.jboss.errai.ioc.client.container.SyncBeanManager;
import org.uberfire.ext.properties.editor.client.fields.AbstractField;
import org.uberfire.ext.properties.editor.client.fields.PropertyEditorFieldType;
import org.uberfire.ext.properties.editor.client.options.PropertyEditorFieldOptionUtils;
import org.uberfire.ext.properties.editor.client.widgets.AbstractPropertyEditorWidget;
import org.uberfire.ext.properties.editor.client.widgets.PropertyEditorItemLabel;
import org.uberfire.ext.properties.editor.client.widgets.PropertyEditorItemWidget;
import org.uberfire.ext.properties.editor.model.CustomPropertyEditorFieldInfo;
import org.uberfire.ext.properties.editor.model.PropertyEditorCategory;
import org.uberfire.ext.properties.editor.model.PropertyEditorEvent;
import org.uberfire.ext.properties.editor.model.PropertyEditorFieldInfo;

public class PropertyEditorHelper {

    public static void extractEditorFrom(final PropertyEditorWidget propertyEditorWidget,
                                         final PanelGroup propertyMenu,
                                         final PropertyEditorEvent event,
                                         final String propertyNameFilter) {
        propertyMenu.clear();
        for (PropertyEditorCategory category : event.getSortedProperties()) {
            createCategory(propertyEditorWidget,
                           propertyMenu,
                           category,
                           propertyNameFilter);
        }
    }

    static void createCategory(final PropertyEditorWidget propertyEditorWidget,
                               final PanelGroup propertyMenu,
                               final PropertyEditorCategory category,
                               final String propertyNameFilter) {

        Panel panel = GWT.create(Panel.class);
        PanelCollapse panelCollapse = createPanelCollapse(propertyEditorWidget,
                                                          category);
        PanelHeader headerPanel = createPanelHeader(category,
                                                    propertyMenu,
                                                    panelCollapse);
        PanelBody panelBody = createPanelBody();
        Form form = createPanelContent(panelBody);

        boolean categoryHasActiveChilds = false;
        for (final PropertyEditorFieldInfo field : category.getFields()) {
            if (isAMatchOfFilter(propertyNameFilter,
                                 field)) {
                categoryHasActiveChilds = true;
                form.add(createItemsWidget(field,
                                           category,
                                           form));
            }
        }
        if (categoryHasActiveChilds) {
            panelCollapse.add(panelBody);
            panel.add(headerPanel);
            panel.add(panelCollapse);
            propertyMenu.add(panel);
        }
    }

    static PanelHeader createPanelHeader(final PropertyEditorCategory category,
                                         final PanelGroup propertyMenu,
                                         PanelCollapse panelCollapse) {

        final Heading heading = new Heading(HeadingSize.H4);
        final Anchor anchor = GWT.create(Anchor.class);
        anchor.setText(category.getName());
        anchor.setDataToggle(Toggle.COLLAPSE);
        anchor.setDataParent(propertyMenu.getId());
        anchor.setDataTargetWidget(panelCollapse);
        anchor.addStyleName("collapsed");
        heading.add(anchor);

        final PanelHeader header = GWT.create(PanelHeader.class);
        header.add(heading);
        return header;
    }

    static PanelCollapse createPanelCollapse(final PropertyEditorWidget propertyEditorWidget,
                                             final PropertyEditorCategory category) {
        final PanelCollapse collapse = GWT.create(PanelCollapse.class);
        collapse.addShowHandler(showEvent -> propertyEditorWidget.addExpandedCategory(category.getName()));

        collapse.addHiddenHandler(hiddenEvent -> {
            hiddenEvent.stopPropagation();
            propertyEditorWidget.collapseCategory(category.getName());
        });

        if (propertyEditorWidget.getExpandedCategories().contains(category.getName())) {
            collapse.setIn(true);
        }

        return collapse;
    }

    private static Form createPanelContent(final PanelBody panelBody) {
        final Container container = GWT.create(Container.class);
        container.setFluid(true);
        final Row row = GWT.create(Row.class);
        final Column column = new Column(ColumnSize.MD_12);
        final Form form = GWT.create(Form.class);
        form.setType(FormType.HORIZONTAL);
        container.add(row);
        row.add(column);
        column.add(form);
        panelBody.add(container);
        return form;
    }

    private static PanelBody createPanelBody() {
        return GWT.create(PanelBody.class);
    }

    public static void extractEditorFrom(final PropertyEditorWidget propertyEditorWidget,
                                         final PanelGroup propertyMenu,
                                         final PropertyEditorEvent event) {
        extractEditorFrom(propertyEditorWidget,
                          propertyMenu,
                          event,
                          "");
    }

    static PropertyEditorItemsWidget createItemsWidget(final PropertyEditorFieldInfo field,
                                                       final PropertyEditorCategory category,
                                                       final Form panelBody) {
        PropertyEditorItemsWidget items = GWT.create(PropertyEditorItemsWidget.class);

        items.add(createLabel(field));
        items.add(createField(field,
                              items,
                              category,
                              panelBody));

        return items;
    }

    static PropertyEditorItemLabel createLabel(final PropertyEditorFieldInfo field) {
        PropertyEditorItemLabel item = GWT.create(PropertyEditorItemLabel.class);
        item.setText(field.getLabel());
        item.setFor(String.valueOf(field.hashCode()));
        if (field.hasHelpInfo()) {
            item.setHelpTitle(field.getHelpHeading());
            item.setHelpContent(field.getHelpText());
        }
        return item;
    }

    static PropertyEditorItemWidget createField(final PropertyEditorFieldInfo field,
                                                final PropertyEditorItemsWidget parent,
                                                PropertyEditorCategory category,
                                                Form panelBody) {
        PropertyEditorItemWidget itemWidget = GWT.create(PropertyEditorItemWidget.class);
        InputGroup content = GWT.create(InputGroup.class);
        HelpBlock helpBlock = GWT.create(HelpBlock.class);

        PropertyEditorFieldType editorFieldType = PropertyEditorFieldType.getFieldTypeFrom(field);

        Widget fieldWidget;
        if (editorFieldType == PropertyEditorFieldType.CUSTOM) {
            Class<?> widgetClass = ((CustomPropertyEditorFieldInfo) field).getCustomEditorClass();
            fieldWidget = getWidget(field,
                                    widgetClass);
        } else {
            fieldWidget = editorFieldType.widget(field);
        }
        createErrorHandlingInfraStructure(parent,
                                          fieldWidget);

        content.add(fieldWidget);
        content.add(helpBlock);
        parent.setHelpInline(helpBlock);

        field.getOptions().forEach(option -> PropertyEditorFieldOptionUtils.applyFieldOption(option,
                                                                                             fieldWidget));

        if (field.isRemovalSupported()) {
            content.add(createRemoveAddOn(field,
                                          category,
                                          parent,
                                          panelBody));
        }

        itemWidget.add(content);
        return itemWidget;
    }

    private static InputGroupButton createRemoveAddOn(final PropertyEditorFieldInfo field,
                                                      final PropertyEditorCategory category,
                                                      final PropertyEditorItemsWidget parent,
                                                      final Form categoryPanel) {
        InputGroupButton groupButton = GWT.create(InputGroupButton.class);
        Button button = GWT.create(Button.class);
        button.setIcon(IconType.MINUS);
        button.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                category.getFields().remove(field);
                categoryPanel.remove(parent);
            }
        });
        groupButton.add(button);
        return groupButton;
    }

    private static Widget getWidget(final PropertyEditorFieldInfo property,
                                    final Class fieldType) {
        SyncBeanManager beanManager = IOC.getBeanManager();
        SyncBeanDef<?> iocBeanDef = beanManager.lookupBean(fieldType);
        AbstractField field = (AbstractField) iocBeanDef.getInstance();
        return field.widget(property);
    }

    static void createErrorHandlingInfraStructure(final PropertyEditorItemsWidget parent,
                                                  Widget widget) {
        AbstractPropertyEditorWidget abstractPropertyEditorWidget = (AbstractPropertyEditorWidget) widget;
        abstractPropertyEditorWidget.setParent(parent);
    }

    public static boolean validade(PropertyEditorEvent event) {
        if (event == null) {
            throw new NullEventException("Event should not be null,");
        }
        if (event.getSortedProperties().isEmpty()) {
            throw new NoPropertiesException("Event should not have empty properties.");
        }

        return event != null && !event.getSortedProperties().isEmpty();
    }

    static boolean isAMatchOfFilter(String propertyNameFilter,
                                    PropertyEditorFieldInfo field) {
        if (propertyNameFilter.isEmpty()) {
            return true;
        }
        return field.getLabel().toUpperCase().contains(propertyNameFilter.toUpperCase());
    }

    public static class NullEventException extends RuntimeException {

        public NullEventException() {
        }

        public NullEventException(String message) {
            super(message);
        }
    }

    public static class NoPropertiesException extends RuntimeException {

        public NoPropertiesException() {
        }

        public NoPropertiesException(String message) {
            super(message);
        }
    }
}