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

import com.airlock.iam.plugin.configeditor.session.ConfigEditorSessionAccessor;
import com.airlock.iam.plugin.configeditor.session.FlowVisualizationCache;
import com.airlock.iam.plugin.configeditor.ui.flowviz.FlowVisualizationModel;
import com.airlock.iam.plugin.configeditor.ui.flowviz.FlowVisualizationModelSerializer;
import com.airlock.iam.plugin.configeditor.ui.flowviz.FlowVisualizationState;
import com.airlock.iam.plugin.configeditor.util.FontAwesomeExtended;
import com.airlock.iam.plugin.configeditor.util.GuiResources;
import com.vaadin.server.Page;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.ui.AbsoluteLayout;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Button.ClickListener;

public class FlowVisualizerWindow extends Window {
   private final FlowVisualizationModel model;

   public FlowVisualizerWindow(FlowVisualizationModel model) {
      this.model = model;
      this.setWidth(100.0F, Unit.PERCENTAGE);
      this.setHeight(100.0F, Unit.PERCENTAGE);
      this.setCaption("%s (environment: %s - context: %s)".formatted(model.title(), environmentIdentifier(model), contextIdentifier(model)));
      this.setResizable(false);
      this.setModal(true);
      this.setClosable(true);
      this.setDraggable(false);
      this.setContent(this.buildLayout());
      this.setId("flowVisualizerWindow");
      this.addStyleName("flow-visualizer-window");
      FlowVisualizationModelSerializer serializer = new FlowVisualizationModelSerializer(model);
      executeJavaScript("visualizeFlow(%s, '%s')".formatted(serializer.convertToJson(), getFlowOrientation(model)));
   }

   private Component buildLayout() {
      AbsoluteLayout layout = new AbsoluteLayout();
      layout.addStyleName("flow-visualizer");
      layout.setSizeFull();
      AbsoluteLayout flowLayout = this.flowLayout();
      VerticalLayout infoLayout = infoLayout();
      layout.addComponent(flowLayout, "right: 28em;");
      layout.addComponent(infoLayout, "right: 0;");
      return layout;
   }

   private AbsoluteLayout flowLayout() {
      AbsoluteLayout flowLayout = new AbsoluteLayout();
      flowLayout.setSizeFull();
      VerticalLayout graphContainer = new VerticalLayout();
      graphContainer.setId("flowVisualizerLayout");
      graphContainer.setMargin(false);
      graphContainer.setSizeFull();
      flowLayout.addComponent(this.controlLayout(), "right: 3em; bottom: 3em; z-index:1000;");
      flowLayout.addComponent(graphContainer);
      return flowLayout;
   }

   private AbsoluteLayout controlLayout() {
      AbsoluteLayout controlLayout = new AbsoluteLayout();
      controlLayout.addStyleName("flow-visualizer-control");
      controlLayout.setWidth(33.0F, Unit.EM);
      controlLayout.setHeight(3.0F, Unit.EM);
      Button svgDownloadButton = controlButton(FontAwesomeExtended.DOWNLOAD, "createSvgBlob()", 6.0F);
      svgDownloadButton.setCaption("SVG");
      Button pngDownloadButton = controlButton(FontAwesomeExtended.DOWNLOAD, "createPngBlob()", 6.0F);
      pngDownloadButton.setCaption("PNG");
      Button swapOrientationVerticalButton = controlSwitchButtons(
         GuiResources.ICON_FLOW_VISUALIZER_ORIENTATION_VERTICAL, this.verticalOrientationClickListener(), 3.0F, "Vertical"
      );
      Button swapOrientationHorizontalButton = controlSwitchButtons(
         GuiResources.ICON_FLOW_VISUALIZER_ORIENTATION_HORIZONTAL, this.horizontalOrientationClickListener(), 3.0F, "Horizontal"
      );
      Button zoomOutButton = controlButton(FontAwesomeExtended.MINUS, "zoomOut()", 3.0F);
      Button zoomToFitButton = controlButton(FontAwesomeExtended.EXPAND, "zoomToFit()", 3.0F);
      Button zoomInButton = controlButton(FontAwesomeExtended.PLUS, "zoomIn()", 3.0F);
      controlLayout.addComponent(svgDownloadButton, "left: 0em;");
      controlLayout.addComponent(pngDownloadButton, "left: 6em;");
      controlLayout.addComponent(swapOrientationVerticalButton, "left: 15em;");
      controlLayout.addComponent(swapOrientationHorizontalButton, "left: 18em;");
      controlLayout.addComponent(zoomOutButton, "left: 24em;");
      controlLayout.addComponent(zoomToFitButton, "left: 27em;");
      controlLayout.addComponent(zoomInButton, "left: 30em;");
      return controlLayout;
   }

   private static Button controlSwitchButtons(ThemeResource icon, ClickListener clickListener, float width, String id) {
      Button button = new Button(icon, clickListener);
      button.addStyleName("flow-visualizer-control-button-selection-option");
      button.setId("FlowVisualizerControlButton" + id);
      button.setWidth(width, Unit.EM);
      button.setHeight(3.0F, Unit.EM);
      return button;
   }

   private static Button controlButton(FontAwesomeExtended icon, String callback, float width) {
      Button button = new Button(icon, event -> executeJavaScript(callback));
      button.addStyleName("flow-visualizer-control-button");
      button.setWidth(width, Unit.EM);
      button.setHeight(3.0F, Unit.EM);
      return button;
   }

   private static VerticalLayout infoLayout() {
      VerticalLayout infoLayout = new VerticalLayout();
      infoLayout.setId("flowVisualizerInfo");
      infoLayout.addStyleName("flow-visualizer-info");
      infoLayout.setSpacing(false);
      infoLayout.setMargin(false);
      infoLayout.setWidth(28.0F, Unit.EM);
      infoLayout.setHeightFull();
      infoLayout.addComponents(new Component[]{stepPropertyLayout()});
      return infoLayout;
   }

   private static VerticalLayout stepPropertyLayout() {
      VerticalLayout stepPropertyLayout = new VerticalLayout();
      stepPropertyLayout.setId("flowVisualizerStepInfoLayout");
      stepPropertyLayout.addStyleName("flow-visualizer-step-properties");
      stepPropertyLayout.setMargin(false);
      stepPropertyLayout.setSpacing(false);
      stepPropertyLayout.addComponents(
         new Component[]{
            FlowVisualizerWindow.InfoProperty.infoProperty("propertyInfoSelectedElement", "Selected Element", "propertyValueSelectedElement"),
            FlowVisualizerWindow.InfoProperty.infoProperty("propertyInfoStepId", "Step ID", "propertyValueStepId"),
            FlowVisualizerWindow.InfoProperty.infoProperty("propertyInfoPreCondition", "Pre Condition", "propertyValuePreCondition"),
            FlowVisualizerWindow.InfoProperty.infoProperty("propertyInfoSkipCondition", "Skip Condition", "propertyValueSkipCondition"),
            FlowVisualizerWindow.InfoProperty.infoProperty("propertyInfoTagsOnSuccess", "Tags On Success", "propertyValueTagsOnSuccess"),
            FlowVisualizerWindow.InfoProperty.infoProperty("propertyInfoOptionCondition", "Condition", "propertyValueOptionCondition")
         }
      );
      return stepPropertyLayout;
   }

   private ClickListener horizontalOrientationClickListener() {
      return e -> {
         executeJavaScript("setHorizontalOrientation()");
         this.updateFlowOrientationState(FlowVisualizationState.FlowOrientation.HORIZONTAL);
      };
   }

   private ClickListener verticalOrientationClickListener() {
      return e -> {
         executeJavaScript("setVerticalOrientation()");
         this.updateFlowOrientationState(FlowVisualizationState.FlowOrientation.VERTICAL);
      };
   }

   private static FlowVisualizationState.FlowOrientation getFlowOrientation(FlowVisualizationModel model) {
      return state().get(model.id()).map(FlowVisualizationState::getOrientation).orElse(FlowVisualizationState.FlowOrientation.HORIZONTAL);
   }

   private void updateFlowOrientationState(FlowVisualizationState.FlowOrientation orientation) {
      state().setOrientation(this.model.id(), orientation);
   }

   private static FlowVisualizationCache state() {
      return ConfigEditorSessionAccessor.resolve(FlowVisualizationCache.class);
   }

   private static String environmentIdentifier(FlowVisualizationModel model) {
      return model.environment().getIdentifier().orElse("common");
   }

   private static String contextIdentifier(FlowVisualizationModel model) {
      return model.context().isDefaultContext() ? "default" : model.context().getIdentifier();
   }

   private static void executeJavaScript(String script) {
      Page.getCurrent().getJavaScript().execute(script);
   }

   static final class InfoProperty extends VerticalLayout {
      public static FlowVisualizerWindow.InfoProperty infoProperty(String propertyId, String tile, String valueId) {
         return new FlowVisualizerWindow.InfoProperty(propertyId, tile, valueId, "-");
      }

      private InfoProperty(String propertyId, String title, String valueId, String value) {
         this.setId(propertyId);
         this.addStyleName("flow-visualizer-property");
         this.setMargin(false);
         this.setSpacing(false);
         Label titleLabel = new Label(title);
         titleLabel.addStyleName("flow-visualizer-info-label");
         Label valueLabel = new Label(value);
         valueLabel.setId(valueId);
         valueLabel.addStyleName("flow-visualizer-info-value");
         valueLabel.addStyleName("flow-visualizer-info-no-value");
         this.addComponents(new Component[]{titleLabel, valueLabel});
      }
   }
}
