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

import com.airlock.iam.plugin.configeditor.util.GuiResources;
import com.airlock.iam.plugin.framework.misc.properties.RegexHelper;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.Sizeable.Unit;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.v7.data.Validator.InvalidValueException;
import com.vaadin.v7.ui.TextField;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class TestRegexDialog extends Window {
   private static final String STYLENAME_REGEXP_TEST_FORM = "regexp-test-form";
   private final TextField expressionField;
   private final boolean caseInsensitive;
   private final boolean multiline;
   private final boolean dotall;
   private final boolean matchEntireInput;
   private final TextField regexField;
   private final TextArea testField;
   private final Label testResult;
   private String newRegex;

   public TestRegexDialog(
      TextField expressionField, String propertyName, String matchingInfo, boolean caseInsensitive, boolean multiline, boolean dotall, boolean matchEntireInput
   ) {
      this.expressionField = expressionField;
      this.caseInsensitive = caseInsensitive;
      this.multiline = multiline;
      this.dotall = dotall;
      this.matchEntireInput = matchEntireInput;
      this.configureDialog(propertyName);
      VerticalLayout mainLayout = new VerticalLayout();
      this.regexField = this.makeRegexInputField(expressionField);
      this.testField = this.makeTestEntryField();
      this.testField.addStyleName("regexp-test-form");
      this.testResult = this.makeTestResultField();
      mainLayout.addComponent(this.makeInnerLayout(matchingInfo));
      mainLayout.addComponent(this.makeButtonsLayout());
      this.setContent(mainLayout);
      this.testField.focus();
      UI.getCurrent().addWindow(this);
      this.setId("testRegexDialog");
      this.regexField.setId("expressionInputField");
      this.testField.setId("testStringInputField");
      this.testResult.setId("resultField");
   }

   private Panel makeInnerLayout(String matchingInfo) {
      Panel panel = new Panel("Regular expression matching");
      VerticalLayout layout = new VerticalLayout();
      layout.setSpacing(false);
      layout.addComponent(this.createLabel(matchingInfo));
      layout.addComponent(this.wrapComponentsInFormlayout());
      panel.setContent(layout);
      return panel;
   }

   private HorizontalLayout makeButtonsLayout() {
      HorizontalLayout hl = new HorizontalLayout();
      hl.addComponent(this.makeTestButton());
      Button saveButton = this.makeSaveButton();
      hl.addComponent(saveButton);
      hl.setWidth("100%");
      hl.setComponentAlignment(saveButton, Alignment.MIDDLE_RIGHT);
      return hl;
   }

   private Button makeSaveButton() {
      Button button = new Button("Save Pattern");
      button.addStyleName("primary");
      button.setId("primaryButton");
      button.setEnabled(!this.expressionField.isReadOnly());
      button.setDescription("Saves the regular expression, in case it was modified.");
      button.addClickListener(this::saveButtonClick);
      return button;
   }

   private Button makeTestButton() {
      Button testButton = new Button("Test (F2)");
      testButton.setId("testRegexButton");
      testButton.addClickListener(event -> this.matchTestString());
      testButton.setClickShortcut(113, new int[0]);
      testButton.setDescription("Press this button to test the text against the expression.");
      return testButton;
   }

   private FormLayout wrapComponentsInFormlayout() {
      FormLayout formLayout = new FormLayout();
      formLayout.addComponent(this.regexField);
      formLayout.addComponent(this.testField);
      formLayout.addComponent(this.testResult);
      formLayout.setMargin(false);
      return formLayout;
   }

   private Label makeTestResultField() {
      Label field = new Label();
      field.setWidth(100.0F, Unit.PERCENTAGE);
      field.setCaption("Result:");
      field.setContentMode(ContentMode.HTML);
      return field;
   }

   private TextArea makeTestEntryField() {
      TextArea field = new TextArea("Test String:");
      field.setRows(5);
      field.setWordWrap(false);
      field.setWidth("100%");
      field.setDescription("Enter a test string for matching against the regular expression.");
      field.addValueChangeListener(event -> this.matchTestString());
      return field;
   }

   private TextField makeRegexInputField(TextField field) {
      TextField inputField = new TextField("Expression:");
      inputField.setWidth("100%");
      inputField.setDescription("The regular expression to be tested.");
      inputField.setValue((String)field.getValue());
      inputField.setReadOnly(field.isReadOnly());
      inputField.addValidator(value -> {
         try {
            this.getCompiledPattern((String)value);
         } catch (PatternSyntaxException e) {
            throw new InvalidValueException("Invalid regular expression syntax: " + e.getMessage());
         }
      });
      return inputField;
   }

   private Label createLabel(String matchingInfo) {
      Label info = new Label();
      info.setWidth(100.0F, Unit.PERCENTAGE);
      info.setValue(matchingInfo);
      info.setContentMode(ContentMode.HTML);
      return info;
   }

   private void configureDialog(String propertyName) {
      this.setWidth(600.0F, Unit.PIXELS);
      this.setCaption("Test " + propertyName);
      this.setModal(true);
      this.setResizable(false);
   }

   public Pattern getCompiledPattern(String regex) {
      try {
         return Pattern.compile(regex, RegexHelper.flags(!this.caseInsensitive, this.multiline, this.dotall));
      } catch (PatternSyntaxException e) {
         throw new InvalidValueException("Invalid regular expression syntax: " + e.getMessage());
      }
   }

   private void matchTestString() {
      try {
         Pattern pattern = this.getCompiledPattern((String)this.regexField.getValue());
         Matcher matcher = pattern.matcher(this.testField.getValue());
         StringBuilder sb = new StringBuilder(18);
         if (this.matchEntireInput) {
            if (matcher.matches()) {
               sb.append("MATCH! ").append(image(GuiResources.ICON_VALIDATION_OK));
            } else {
               sb.append("no match ").append(image(GuiResources.ICON_VALIDATION_ERROR));
            }
         } else {
            int matchCount = 0;

            while (matcher.find()) {
               matchCount++;
            }

            sb.append(matchCount).append(" successful finds ");
            if (matchCount > 0) {
               sb.append(image(GuiResources.ICON_VALIDATION_OK));
            } else {
               sb.append(image(GuiResources.ICON_VALIDATION_ERROR));
            }
         }

         this.testResult.setValue(sb.toString());
      } catch (InvalidValueException e) {
         this.testResult.setValue("Pattern invalid " + image(GuiResources.ICON_VALIDATION_WARNING));
      }
   }

   private static String image(ThemeResource resource) {
      return "<img src=\"VAADIN/themes/medusa/" + resource + "\" height=\"16px\" style=\"vertical-align: inherit;\"/>";
   }

   public void saveButtonClick(ClickEvent event) {
      if (!this.expressionField.isReadOnly()) {
         this.newRegex = (String)this.regexField.getValue();
      }

      UI.getCurrent().removeWindow(this);
   }

   public String getNewRegex() {
      return this.newRegex;
   }
}
