package com.airlock.iam.plugin.configeditor.ui.plugintrees;

import com.airlock.iam.configeditor.shared.domain.IamModuleConfigRoot;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.exception.InitializeException;
import com.airlock.iam.plugin.configeditor.ConfigEditorApplication;
import com.airlock.iam.plugin.configeditor.command.CreateNewUnconnectedPluginConfigurationCommand;
import com.airlock.iam.plugin.configeditor.ui.dialog.CleanUpUnconnectedPluginsDialog;
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.MutablePluginConfiguration;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.MouseEvents.ClickListener;
import com.vaadin.server.Resource;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.Embedded;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.UI;
import com.vaadin.v7.data.Property.ValueChangeEvent;
import com.vaadin.v7.data.Property.ValueChangeListener;
import com.vaadin.v7.ui.ComboBox;
import java.util.Optional;

class ConfigurationTreeToolbar {
   private final ConfigEditorApplication application = (ConfigEditorApplication)UI.getCurrent();
   private final TreeContainer treeContainer;
   private final HorizontalLayout layout;
   private ComboBox treeSelect;
   private Embedded newPluginIcon;
   private Optional<Embedded> rootNodeIcon;
   private Embedded cleanUpIcon;
   private Embedded linkWithEditorIcon;

   public ConfigurationTreeToolbar(TreeContainer treeContainer) {
      this.treeContainer = treeContainer;
      this.layout = new HorizontalLayout();
      this.layout.setEnabled(false);
      this.layout.addStyleName("treeselect");
      this.makeTreeSelect();
      this.makeIcons();
   }

   public void selectTree(ConfigurationTree tree) {
      this.treeSelect.setValue(tree);
   }

   public AbstractOrderedLayout getLayout() {
      return this.layout;
   }

   private void makeTreeSelect() {
      this.treeSelect = new ComboBox();
      this.treeSelect.addItem(this.treeContainer.getModulesTree());
      this.treeSelect.addItem(this.treeContainer.getUnconnectedPluginsTree());
      if (this.treeContainer.getInheritanceTree().isPresent()) {
         this.treeSelect.addItem(this.treeContainer.getInheritanceTree().get());
      }

      this.treeSelect.setValue(null);
      this.treeSelect.addValueChangeListener(new ConfigurationTreeToolbar.TreeSelectListener());
      this.treeSelect.setNullSelectionAllowed(false);
      this.treeSelect.setInvalidAllowed(false);
      this.treeSelect.setNewItemsAllowed(false);
      this.treeSelect.setImmediate(true);
      this.treeSelect.setBuffered(false);
      this.treeSelect.setTextInputAllowed(false);
      this.layout.addComponent(this.treeSelect);
   }

   private void makeIcons() {
      this.makeNewPluginIcon();
      this.makeRootNodeIcon();
      this.makeCleanUpIcon();
      this.makeLinkWithEditorIcon();
   }

   private void makeNewPluginIcon() {
      this.newPluginIcon = new Embedded(null, GuiResources.ICON_NEW);
      this.newPluginIcon.setId("newPluginIcon");
      this.newPluginIcon.setVisible(true);
      this.newPluginIcon.setHeight(16.0F, Unit.PIXELS);
      this.newPluginIcon.setWidth(16.0F, Unit.PIXELS);
      this.newPluginIcon.setDescription("Create a new plugin.");
      this.newPluginIcon.addStyleName("with-hand");
      this.newPluginIcon.addClickListener(new ClickListener() {
         public void click(ClickEvent event) {
            try {
               this.createNewPlugin();
            } catch (InitializeException e) {
               throw new IllegalStateException("Plugin couldn't be loaded. Possibly illegal state of configuration");
            }
         }

         private void createNewPlugin() throws InitializeException {
            NewPluginDialog dialog = new NewPluginDialog(null, null, Object.class);
            UI.getCurrent().addWindow(dialog);
            dialog.hideParentSelect();
            dialog.makeIdentifierMandatory();
            dialog.hideCreateButton();
            dialog.addCloseListener(e -> {
               MutablePluginConfiguration pc = ((NewPluginDialog)e.getSource()).getPluginConfiguration();
               if (pc != null) {
                  new CreateNewUnconnectedPluginConfigurationCommand(ConfigurationTreeToolbar.this.application.getConfigurationDictionary(), pc).execute();
                  ConfigurationTreeToolbar.this.application.updateValidationState();
                  ConfigurationTreeToolbar.this.application.updateFormFields();
                  if (dialog.isEditAfterCreatingPlugin()) {
                     ConfigurationTreeToolbar.this.application.selectConfiguration(pc);
                     ConfigurationTreeToolbar.this.application.getPluginTreeUI().selectUnconnectedPluginsTree();
                  }
               }
            });
         }
      });
   }

   private void makeRootNodeIcon() {
      Embedded icon = new Embedded(null, GuiResources.ICON_ROOT_NODE);
      icon.setId("selectRootNodeIcon");
      icon.setDescription("Set the root node of the plugin tree.");
      icon.addStyleName("with-hand");
      icon.setHeight(16.0F, Unit.PIXELS);
      icon.setWidth(16.0F, Unit.PIXELS);
      this.rootNodeIcon = Optional.of(icon);
      icon.addClickListener(event -> this.application.setRootNode());
   }

   private void makeCleanUpIcon() {
      this.cleanUpIcon = new Embedded(null, GuiResources.ICON_CLEANUP);
      this.cleanUpIcon.setId("deleteAllUnconnectedPluginsIcon");
      this.cleanUpIcon.setDescription("Remove all unconnected plugins.");
      this.cleanUpIcon.setHeight(16.0F, Unit.PIXELS);
      this.cleanUpIcon.setWidth(16.0F, Unit.PIXELS);
      this.cleanUpIcon.addStyleName("with-hand");
      this.cleanUpIcon.addClickListener(new ClickListener() {
         public void click(ClickEvent event) {
            CleanUpUnconnectedPluginsDialog dialog = new CleanUpUnconnectedPluginsDialog();
            dialog.addCloseListener(e -> {
               if (dialog.isDeleteRequested()) {
                  this.cleanupUnconnectedPlugins();
                  this.setPluginConfigurationEmpty();
               }
            });
         }

         private void cleanupUnconnectedPlugins() {
            ConfigurationTreeToolbar.this.application.getConfigurationDictionary().cleanUpPlugins(IamModuleConfigRoot.getRootPluginDisplayNames());
         }

         private void setPluginConfigurationEmpty() {
            ConfigurationTreeToolbar.this.application.selectConfiguration(null);
            ConfigurationTreeToolbar.this.application.setSelectedContext(ConfigurationContext.defaultContext());
            ConfigurationTreeToolbar.this.application.updateValidationState();
         }
      });
   }

   private void makeLinkWithEditorIcon() {
      this.linkWithEditorIcon = new Embedded(null, this.getIconForLinkWithEditorIcon());
      this.linkWithEditorIcon.setId("linkWithEditorIcon");
      this.linkWithEditorIcon.setWidth(16.0F, Unit.PIXELS);
      this.linkWithEditorIcon.setHeight(16.0F, Unit.PIXELS);
      this.linkWithEditorIcon.setDescription(this.getDescriptionForLinkWithEditorIcon());
      this.linkWithEditorIcon.addStyleName("with-hand");
      this.linkWithEditorIcon.addClickListener(event -> {
         this.application.setLinkPluginTreeWithEditor(!this.application.isPluginTreeLinkedWithEditor());
         this.linkWithEditorIcon.setSource(this.getIconForLinkWithEditorIcon());
         this.linkWithEditorIcon.setDescription(this.getDescriptionForLinkWithEditorIcon());
      });
   }

   private Resource getIconForLinkWithEditorIcon() {
      return this.application.isPluginTreeLinkedWithEditor() ? GuiResources.ICON_LINK_WITH_EDITOR_ENABLED : GuiResources.ICON_LINK_WITH_EDITOR_DISABLED;
   }

   private String getDescriptionForLinkWithEditorIcon() {
      return this.application.isPluginTreeLinkedWithEditor()
         ? "Click to not select all occurrences of plugin being edited in tree."
         : "Click to always select all occurrences of plugin being edited in tree.";
   }

   private final class TreeSelectListener implements ValueChangeListener {
      public void valueChange(ValueChangeEvent event) {
         ConfigurationTree tree = (ConfigurationTree)event.getProperty().getValue();
         ConfigurationTreeToolbar.this.treeContainer.showTree(tree);
         if (tree == ConfigurationTreeToolbar.this.treeContainer.getModulesTree()) {
            this.showIcons(
               ConfigurationTreeToolbar.this.newPluginIcon,
               ConfigurationTreeToolbar.this.rootNodeIcon.orElse(null),
               ConfigurationTreeToolbar.this.linkWithEditorIcon
            );
         } else if (tree == ConfigurationTreeToolbar.this.treeContainer.getUnconnectedPluginsTree()) {
            this.showIcons(ConfigurationTreeToolbar.this.newPluginIcon, ConfigurationTreeToolbar.this.cleanUpIcon);
         } else if (ConfigurationTreeToolbar.this.treeContainer.getInheritanceTree().isPresent()
            && tree == ConfigurationTreeToolbar.this.treeContainer.getInheritanceTree().get()) {
            this.showIcons(ConfigurationTreeToolbar.this.rootNodeIcon.orElse(null));
         }
      }

      private void showIcons(Embedded... iconsToShow) {
         this.hideAllIcons();

         for (Embedded icon : iconsToShow) {
            if (icon != null) {
               ConfigurationTreeToolbar.this.layout.addComponent(icon);
               ConfigurationTreeToolbar.this.layout.setComponentAlignment(icon, Alignment.MIDDLE_RIGHT);
            }
         }
      }

      private void hideAllIcons() {
         ConfigurationTreeToolbar.this.layout.removeComponent(ConfigurationTreeToolbar.this.newPluginIcon);
         ConfigurationTreeToolbar.this.layout.removeComponent(ConfigurationTreeToolbar.this.linkWithEditorIcon);
         ConfigurationTreeToolbar.this.layout.removeComponent(ConfigurationTreeToolbar.this.cleanUpIcon);
         if (ConfigurationTreeToolbar.this.rootNodeIcon.isPresent()) {
            ConfigurationTreeToolbar.this.layout.removeComponent((Component)ConfigurationTreeToolbar.this.rootNodeIcon.get());
         }
      }
   }
}
