package com.airlock.iam.plugin.configeditor.field;

import com.airlock.iam.plugin.configeditor.data.PropertyAdapter;
import com.airlock.iam.plugin.configeditor.util.GuiResources;
import com.airlock.iam.plugin.framework.domain.model.property.EnumPropertyTranslator;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Image;
import com.vaadin.v7.data.Property;
import com.vaadin.v7.data.Property.ValueChangeEvent;
import com.vaadin.v7.data.Property.ValueChangeListener;
import com.vaadin.v7.data.util.IndexedContainer;
import com.vaadin.v7.shared.ui.combobox.FilteringMode;
import com.vaadin.v7.ui.AbstractField;
import com.vaadin.v7.ui.AbstractSelect;
import com.vaadin.v7.ui.ComboBox;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.lang.annotation.Annotation;
import java.util.Optional;

public class MedusaEnumField extends MedusaField<Annotation, Object> {
   private static final String PLUS_ICON_TOOLIP = "Add a new value by entering it in the field.";
   private static final String DEFAULT_SUFFIX = " - DEFAULT";
   private final boolean allowNewItems;

   @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "See ALIAM-15315")
   public MedusaEnumField(PropertyAdapter<? extends Annotation> propertyAdapter, boolean allowNewItems) {
      super(propertyAdapter);
      this.allowNewItems = allowNewItems;
      ComboBox innerField = (ComboBox)this.getInnerField();
      this.setInnerFieldProperties(innerField);
      this.handleNewItemsAllowed(innerField);
   }

   @Override
   public AbstractField<Object> createInnerField() {
      ComboBox field = new NonEmptyDefaultValueToNullSelectionItemCombobox();
      field.setWidth("100%");
      field.setImmediate(true);
      field.setBuffered(false);
      field.setInvalidAllowed(false);
      field.setNewItemsAllowed(false);
      field.setNullSelectionAllowed(true);
      field.setFilteringMode(FilteringMode.CONTAINS);
      return field;
   }

   public void fillStrings(String[] allowedValues) {
      AbstractSelect box = (AbstractSelect)this.getInnerField();
      IndexedContainer container = new IndexedContainer();
      box.setContainerDataSource(container);

      for (String value : allowedValues) {
         if (this.equalsDefaultValue(value)) {
            String nullItemId = value + " - DEFAULT";
            container.addItemAt(0, nullItemId);
            box.setNullSelectionItemId(nullItemId);
         } else {
            container.addItem(value);
         }
      }
   }

   public void fillEnums(Enum<?>[] enumConstants) {
      AbstractSelect box = (AbstractSelect)this.getInnerField();
      IndexedContainer container = new IndexedContainer();
      box.setContainerDataSource(container);

      for (Enum<?> enumConstant : enumConstants) {
         String caption = EnumPropertyTranslator.getDisplayValue(enumConstant);
         if (this.equalsDefaultValue(enumConstant)) {
            container.addItemAt(0, enumConstant);
            box.setNullSelectionItemId(enumConstant);
            box.setItemCaption(enumConstant, caption + " - DEFAULT");
         } else {
            container.addItem(enumConstant);
            box.setItemCaption(enumConstant, caption);
         }
      }
   }

   public void setPropertyDataSource(Property newDataSource) {
      this.getInnerField().setPropertyDataSource(newDataSource);
      if (this.allowNewItems && newDataSource != null && newDataSource.getValue() != null && !this.getPropertyAdapter().isValueFromDefault()) {
         ((AbstractSelect)this.getInnerField()).addItem(newDataSource.getValue());
      }

      this.updateUiAndValidate();
   }

   public Property<?> getPropertyDataSource() {
      return this.getInnerField().getPropertyDataSource();
   }

   @Override
   protected boolean equalsDefaultValue(Object value) {
      Optional<?> optionalDefault = this.getPropertyAdapter().getDefaultValue();
      return optionalDefault.isPresent() ? optionalDefault.get().equals(value) : false;
   }

   private void handleNewItemsAllowed(ComboBox select) {
      if (this.allowNewItems) {
         this.enableAllowNewItems();
      } else {
         this.disableTextInput(select);
      }
   }

   private void disableTextInput(ComboBox select) {
      select.addValueChangeListener(new MedusaEnumField.EnumFieldValueChangeListener(select));
   }

   private void setInnerFieldProperties(ComboBox select) {
      select.setNewItemsAllowed(this.allowNewItems);
      select.setInvalidAllowed(this.allowNewItems);
      select.setInvalidCommitted(this.allowNewItems);
   }

   private void enableAllowNewItems() {
      Image plusIcon = this.makeIcon(GuiResources.ICON_PLUS, "Add a new value by entering it in the field.", null, null);
      plusIcon.addStyleName("plusIcon");
      this.getFieldLayout().addComponent(plusIcon);
      this.getFieldLayout().setComponentAlignment(plusIcon, Alignment.MIDDLE_LEFT);
   }

   private static final class EnumFieldValueChangeListener implements ValueChangeListener {
      private final ComboBox select;

      private EnumFieldValueChangeListener(ComboBox select) {
         this.select = select;
      }

      public void valueChange(ValueChangeEvent event) {
         this.select.setTextInputAllowed(event.getProperty().getValue() == null);
      }
   }
}
