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

import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.exception.InitializeException;
import com.airlock.iam.plugin.configeditor.command.ChangePropertyConfigurationCommand;
import com.airlock.iam.plugin.configeditor.command.CreateNewConnectedPluginConfigurationCommand;
import com.airlock.iam.plugin.configeditor.data.ConfigurableProperty;
import com.airlock.iam.plugin.configeditor.data.PropertyAdapter;
import com.airlock.iam.plugin.configeditor.ui.dialog.NewPluginDialog;
import com.airlock.iam.plugin.configeditor.util.GuiResources;
import com.airlock.iam.plugin.framework.application.configuration.DefaultPluginConfiguration;
import com.airlock.iam.plugin.framework.application.configuration.MutablePluginConfiguration;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.ui.Image;
import com.vaadin.ui.UI;
import com.vaadin.v7.data.Property;
import com.vaadin.v7.data.Validator;
import com.vaadin.v7.data.Property.ValueChangeEvent;
import com.vaadin.v7.ui.AbstractField;
import com.vaadin.v7.ui.ComboBox;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;

public class MedusaPluginField extends MedusaField<PluginProperty, Object> {
   private final Image followIcon;
   private final Image newIcon = this.makeIcon(GuiResources.ICON_NEW, "Create a new plugin.", "with-hand", this);

   @SuppressFBWarnings(value = "MC_OVERRIDABLE_METHOD_CALL_IN_CONSTRUCTOR", justification = "See ALIAM-15315")
   public MedusaPluginField(PropertyAdapter<PluginProperty> propertyAdapter) {
      super(propertyAdapter, true);
      this.newIcon.addStyleName("newPluginIcon");
      this.followIcon = this.makeIcon(GuiResources.ICON_FOLLOW, "Navigate to this plugin.", "with-hand", this);
      this.followIcon.addStyleName("navigateToPluginIcon");
   }

   @Override
   public AbstractField<Object> createInnerField() {
      ComboBox combo = new PluginComboBox();
      combo.setWidth("100%");
      combo.setInvalidAllowed(false);
      combo.setNullSelectionAllowed(true);
      combo.setBuffered(false);
      combo.setImmediate(true);
      return combo;
   }

   public void setPropertyDataSource(Property newDataSource) {
      Optional<Property<?>> adapter = ValueMapperPropertyAdapter.fromPropertyDataSource(newDataSource, this::mapGlobalPluginValueToNull);
      super.setPropertyDataSource(adapter.orElse(newDataSource));
      this.getDndWrapper().setDropHandler(new PluginDropHandler(this.getApplication(), this.getInnerField()));
      this.updateUiAndValidate();
   }

   private Object mapGlobalPluginValueToNull(ConfigurableProperty configurableProperty) {
      return configurableProperty.isValueFromGlobalReference() ? null : configurableProperty.getValue();
   }

   @Override
   public Class<?> getType() {
      return MutablePluginConfiguration.class;
   }

   protected void setInternalValue(Object newValue) {
      super.setInternalValue(newValue);
      boolean backupReadonly = this.getPropertyDataSource().isReadOnly();
      this.getInnerField().setReadOnly(false);
      this.getInnerField().setValue(newValue);
      this.getInnerField().setReadOnly(backupReadonly);
   }

   @Override
   public Object getValue() {
      return this.getInnerField().getValue();
   }

   public PluginComboBox getInnerField() {
      return (PluginComboBox)super.getInnerField();
   }

   @Override
   public void click(ClickEvent event) {
      super.click(event);
      if (event.getSource() == this.getInheritIcon()) {
         this.updateSelectablePluginConfigurations();
         this.setInternalValue(this.getPropertyAdapter().getValue().orElse(null));
         this.updateUiAndValidate();
      } else if (event.getSource() == this.getOverrideIcon()) {
         Object valueBeforeChangeOfInnerField = this.getPropertyAdapter().getValue().orElse(null);
         this.updateSelectablePluginConfigurations();
         this.setInternalValue(valueBeforeChangeOfInnerField);
         this.updateValueInConfiguration();
      } else if (event.getSource() == this.newIcon) {
         this.createNewPlugin();
      } else if (event.getSource() == this.followIcon) {
         this.getApplication().selectConfiguration((MutablePluginConfiguration)this.getConfigurableProperty().getValue());
      }
   }

   private void updateSelectablePluginConfigurations() {
      boolean readOnlyState = this.getInnerField().isReadOnly();
      this.getInnerField().setReadOnly(false);
      this.getInnerField().updateSelectablePluginConfigurations();
      this.getInnerField().setReadOnly(readOnlyState);
   }

   private void createNewPlugin() {
      NewPluginDialog dialog;
      try {
         dialog = new NewPluginDialog(this.getPropertyAdapter().getPluginConfiguration(), null, this.getPropertyAdapter().getConfigFieldType());
      } catch (InitializeException e) {
         throw new IllegalStateException(e);
      }

      UI.getCurrent().addWindow(dialog);
      dialog.addCloseListener(e -> {
         MutablePluginConfiguration pc = ((NewPluginDialog)e.getSource()).getPluginConfiguration();
         if (pc != null) {
            new CreateNewConnectedPluginConfigurationCommand(this.getApplication().getConfigurationDictionary(), pc, this.getConfigurableProperty()).execute();
            this.getApplication().updateValidationState();
            this.getApplication().updateFormFields();
            if (dialog.isEditAfterCreatingPlugin()) {
               this.getApplication().selectConfiguration(pc);
            }
         }
      });
   }

   @Override
   public void valueChange(ValueChangeEvent event) {
      if (this.updateValueInConfiguration()) {
         super.valueChange(event);
      }
   }

   private boolean updateValueInConfiguration() {
      MutablePluginConfiguration newSelectedItem = (MutablePluginConfiguration)this.getInnerField().getValue();
      MutablePluginConfiguration itemBeforeChange = (MutablePluginConfiguration)this.mapGlobalPluginValueToNull(this.getConfigurableProperty());
      if (newSelectedItem == null) {
         if (this.getConfigurableProperty().getValueStatus().equals(ConfigurableProperty.ValueStatus.INHERITED_FROM_PARENT)) {
            return false;
         }

         newSelectedItem = this.getDefaultIfAvailable();
      } else if (newSelectedItem.equals(itemBeforeChange)) {
         return false;
      }

      this.executePropertyChangedCommand(newSelectedItem);
      return true;
   }

   private MutablePluginConfiguration getDefaultIfAvailable() {
      Class<?> defaultValue = ((PluginProperty)this.getPropertyAdapter().getFieldAnnotation()).defaultValue();
      if (defaultValue != Object.class) {
         MutablePluginConfiguration defaultPc = new DefaultPluginConfiguration(null, defaultValue.getName());
         defaultPc.setSynthetic(true);
         defaultPc.setGlobalConfiguration(this.getApplication().getConfigurationDictionary());
         return defaultPc;
      } else {
         return null;
      }
   }

   private void executePropertyChangedCommand(MutablePluginConfiguration newValue) {
      ChangePropertyConfigurationCommand<MutablePluginConfiguration> command = ChangePropertyConfigurationCommand.createChangePluginPropertyConfigurationCmdBuilder()
         .property(this.getConfigurableProperty())
         .newValue(newValue)
         .build();
      command.execute();
   }

   @Override
   protected boolean isDefaultIconVisible(Object value) {
      return this.isValueFromDefault() && !this.getConfigurableProperty().isInherited();
   }

   @Override
   protected void updateUI(ConfigurableProperty property) {
      this.addCommonIconsForProperty(property);
      this.setIconVisible(this.newIcon, !this.getConfigurableProperty().isReadOnly(), false);
      this.setIconVisible(this.followIcon, this.getConfigurableProperty().getValue() != null, false);
   }

   public void addValidator(Validator validator) {
   }
}
