// Sorts properties in the order of single value required, multi value required, optional single
  // value, optional multi value
  private void sortProperties(
      List<String> propertyNames, final PackageArtifact artifact, final String type) {
    Collections.sort(
        propertyNames,
        (propertyOne, propertyTwo) -> {
          int propertyOneMaxOccurs =
              packageOntologyService.getPropertyMaxOccurrences(artifact, propertyOne, type);
          int propertyOneMinOccurs =
              packageOntologyService.getPropertyMinOccurrences(artifact, propertyOne, type);

          int propertyTwoMaxOccurs =
              packageOntologyService.getPropertyMaxOccurrences(artifact, propertyTwo, type);
          int propertyTwoMinOccurs =
              packageOntologyService.getPropertyMinOccurrences(artifact, propertyTwo, type);

          if (propertyOneMinOccurs == propertyTwoMinOccurs
              && propertyOneMaxOccurs == propertyTwoMaxOccurs) {
            return 0;
          }

          if (propertyOneMinOccurs == propertyTwoMinOccurs) {
            if (propertyOneMaxOccurs < propertyTwoMaxOccurs) {
              return -1;
            }
          } else if (propertyOneMinOccurs > propertyTwoMinOccurs) {
            return -1;
          }

          return 1;
        });
  }
  /**
   * Creates the inheritance tab in the popup.
   *
   * @param artifact The popup artifact
   * @return inheritance tab or null if nothing to do
   */
  public VBox createInheritanceTab(final PackageArtifact artifact) {

    final VBox inheritanceBox = new VBox(12);
    inheritanceBox.getStyleClass().add(PACKAGE_TOOL_POPUP_PROPERTY_TAB);
    boolean hasInheritableProperties = false;

    // create label to explain what this tab is about.
    Label inheritanceTabIntroLabel = new Label(labels.get(Labels.LabelKey.INHERITANCE_TAB_INTRO));
    inheritanceTabIntroLabel.setPrefWidth(450);
    inheritanceTabIntroLabel.setWrapText(true);
    inheritanceBox.getChildren().add(inheritanceTabIntroLabel);

    // create label to explain usage of buttons.
    Label inheritanceButtonExplainedLabel =
        new Label(labels.get(Labels.LabelKey.INHERITANCE_BUTTON_EXPLAINED));
    inheritanceButtonExplainedLabel.setPrefWidth(450);
    inheritanceButtonExplainedLabel.setWrapText(true);
    inheritanceBox.getChildren().add(inheritanceButtonExplainedLabel);

    Separator groupSeparator = new Separator();
    inheritanceBox.getChildren().add(groupSeparator);

    // Loop through properties for the given artifact.
    for (String propertyName : packageOntologyService.getProperties(artifact).keySet()) {
      // If the property is inheritable, create a button which would allow the values to be apply to
      // children
      // appropriately
      if (packageOntologyService.isInheritableProperty(artifact, propertyName)) {
        inheritanceBox.getChildren().add(createInheritanceBox(artifact.getType(), propertyName));

        groupSeparator = new Separator();
        inheritanceBox.getChildren().add(groupSeparator);
        hasInheritableProperties = true;
      }
    }

    if (!hasInheritableProperties) {
      Label noInheritablePropertyLabel =
          new Label(labels.get(Labels.LabelKey.NO_INHERITABLE_PROPERTY));
      inheritanceTabIntroLabel.setPrefWidth(450);
      inheritanceTabIntroLabel.setWrapText(true);
      inheritanceBox.getChildren().add(noInheritablePropertyLabel);
    }

    return inheritanceBox;
  }
  /**
   * Creates the tab for displaying creator properties. This tab is constructed using the {@code
   * createPropertyBox} and {@code createGroupPropertySection} methods found below.
   *
   * @param artifact The popup artifact
   * @return content or null if nothing for user to do
   */
  private VBox createCreatorTab(final PackageArtifact artifact) {
    final VBox propertiesBox = new VBox(12);
    propertiesBox.getStyleClass().add(PACKAGE_TOOL_POPUP_PROPERTY_TAB);

    Label requiredLabel = new Label(labels.get(Labels.LabelKey.REQUIRED_FIELDS_LABEL));
    requiredLabel.setMaxWidth(300);
    requiredLabel.setWrapText(true);
    requiredLabel.setTextAlignment(TextAlignment.CENTER);

    propertiesBox.getChildren().add(requiredLabel);

    final Map<String, String> properties = packageOntologyService.getProperties(artifact);

    List<String> sortedProperties = new ArrayList<>();

    // Get the creator property set and then create a sorted list from it.
    sortedProperties.addAll(packageOntologyService.getCreatorProperties(artifact));
    sortProperties(sortedProperties, artifact, "");

    // Loop through all the creator properties as defined in the ontology.
    for (final String property : sortedProperties) {
      final PackageDescriptionViewImpl.ArtifactPropertyContainer container =
          new PackageDescriptionViewImpl.ArtifactPropertyContainer();

      // If the property is complex use the group property creation, otherwise use the simple
      // property set up.
      if (packageOntologyService.isPropertyComplex(properties.get(property))) {
        container.isComplex = true;
        VBox complexPropertyBox =
            createGroupPropertySection(
                artifact, property, properties.get(property), false, container);
        propertiesBox.getChildren().add(complexPropertyBox);
        int maxOccurances =
            packageOntologyService.getPropertyMaxOccurrences(artifact, property, "");

        // If the ontology allows for more than one of the property add a button which will add more
        // groups when pressed.
        if (maxOccurances > 1) {
          final Button addNewButton =
              new Button(labels.get(Labels.LabelKey.ADD_NEW_BUTTON) + " " + property);
          addNewButton.setMaxWidth(addNewButtonMaxWidth);
          propertiesBox.getChildren().add(addNewButton);
          addNewButton.setDisable(true);

          final GroupPropertyChangeListener listener =
              new GroupPropertyChangeListener(addNewButton, container);

          for (Node n : propertiesBox.getChildren()) {
            if (n instanceof VBox) {
              addChangeListenerToSectionFields((VBox) n, listener);
            }
          }

          listener.changed(null, "n/a", "n/a");

          addNewButton.setOnAction(
              arg0 -> {
                VBox complexPropertyBox1 =
                    createGroupPropertySection(
                        artifact, property, properties.get(property), true, container);
                int buttonIndex = propertiesBox.getChildren().indexOf(addNewButton);

                propertiesBox.getChildren().add(buttonIndex, complexPropertyBox1);

                addChangeListenerToSectionFields(complexPropertyBox1, listener);
                addNewButton.setDisable(true);
                requestFocusForNewGroup(complexPropertyBox1);
              });
        }
      } else {
        // Otherwise create just the simple property
        int maxOccurances =
            packageOntologyService.getPropertyMaxOccurrences(artifact, property, "");
        int minOccurances =
            packageOntologyService.getPropertyMinOccurrences(artifact, property, "");
        boolean systemGenerated =
            packageOntologyService.isSystemSuppliedProperty(artifact, property);

        Set<StringProperty> fields = new HashSet<>();

        propertiesBox
            .getChildren()
            .add(
                new TextPropertyBox(
                    artifact,
                    "",
                    ontologyLabels.get(property),
                    property,
                    artifact.getSimplePropertyValues(property),
                    maxOccurances,
                    fields,
                    minOccurances,
                    systemGenerated,
                    packageOntologyService,
                    labels,
                    messages,
                    applyButtonValidationListener));
        container.values = fields;
      }

      artifactPropertyFields.put(property, container);
    }

    // Return null if nothing to edit.
    if (propertiesBox.getChildren().size() == 1) {
      return null;
    }

    return propertiesBox;
  }
  /**
   * Handles the creation of group properties, group properties are properties that are linked together in some manner.
   * Group properties are constructed using the {@code createPropertyBox) method found below.
   *
   * @param artifact
   * @param propertyName
   * @param propertyType
   * @param empty
   * @param container
   * @return  the VBox
   */
  private VBox createGroupPropertySection(
      PackageArtifact artifact,
      String propertyName,
      String propertyType,
      boolean empty,
      PackageDescriptionViewImpl.ArtifactPropertyContainer container) {
    VBox complexPropertyBox = new VBox(8);
    Separator separator = new Separator();
    complexPropertyBox.getChildren().add(separator);

    // If the artifact has the property and we're not adding an empty field add the sub property
    // values
    if (artifact.getPropertyNames().contains(propertyName) && !empty) {
      for (PackageArtifact.PropertyValueGroup group :
          artifact.getPropertyValueGroups(propertyName)) {
        Map<String, Set<StringProperty>> subPropertyFields = new HashMap<>();

        Label propertyNameLabel = new Label(ontologyLabels.get(propertyName));
        propertyNameLabel.setPrefWidth(100);
        propertyNameLabel.setWrapText(true);
        complexPropertyBox.getChildren().add(propertyNameLabel);

        List<String> sortedProperties = new ArrayList<>();

        // Get the creator property set and then create a sorted list from it.
        sortedProperties.addAll(packageOntologyService.getGroupPropertyNames(propertyType));
        sortProperties(sortedProperties, artifact, propertyType);

        for (String fieldName : sortedProperties) {
          Set<String> values = group.getSubPropertyValues(fieldName);
          int maxOccurs =
              packageOntologyService.getPropertyMaxOccurrences(artifact, fieldName, propertyType);
          int minOccurs =
              packageOntologyService.getPropertyMinOccurrences(artifact, fieldName, propertyType);
          boolean systemGenerated =
              packageOntologyService.isSystemSuppliedProperty(artifact, fieldName);
          Set<StringProperty> fields = new HashSet<>();
          complexPropertyBox
              .getChildren()
              .add(
                  new TextPropertyBox(
                      artifact,
                      propertyName,
                      ontologyLabels.get(fieldName),
                      fieldName,
                      values,
                      maxOccurs,
                      fields,
                      minOccurs,
                      systemGenerated,
                      packageOntologyService,
                      labels,
                      messages,
                      applyButtonValidationListener));
          subPropertyFields.put(fieldName, fields);
        }
        container.subProperties.add(subPropertyFields);
      }
      // Otherwise just add the empty text fields for the possible property values.
    } else {
      Map<String, Set<StringProperty>> subPropertyFields = new HashMap<>();

      Label propertyNameLabel = new Label(ontologyLabels.get(propertyName));
      propertyNameLabel.setPrefWidth(100);
      propertyNameLabel.setWrapText(true);
      complexPropertyBox.getChildren().add(propertyNameLabel);

      List<String> sortedProperties = new ArrayList<>();

      // Get the creator property set and then create a sorted list from it.
      sortedProperties.addAll(packageOntologyService.getGroupPropertyNames(propertyType));
      sortProperties(sortedProperties, artifact, propertyType);

      // For each field create a property box
      for (String fieldName : sortedProperties) {
        // String fieldType = packageOntologyService.getComplexPropertySubPropertyType(propertyType,
        // fieldName);
        int maxOccurs =
            packageOntologyService.getPropertyMaxOccurrences(artifact, fieldName, propertyType);
        int minOccurs =
            packageOntologyService.getPropertyMinOccurrences(artifact, fieldName, propertyType);
        boolean systemGenerated =
            packageOntologyService.isSystemSuppliedProperty(artifact, fieldName);
        Set<StringProperty> fields = new HashSet<>();
        complexPropertyBox
            .getChildren()
            .add(
                new TextPropertyBox(
                    artifact,
                    propertyName,
                    ontologyLabels.get(fieldName),
                    fieldName,
                    null,
                    maxOccurs,
                    fields,
                    minOccurs,
                    systemGenerated,
                    packageOntologyService,
                    labels,
                    messages,
                    applyButtonValidationListener));

        subPropertyFields.put(fieldName, fields);
      }

      container.subProperties.add(subPropertyFields);
    }
    return complexPropertyBox;
  }
  /*
   * Creates the general properties tab, general properties are any properties that aren't defined to be creator properties,
   * by the ontology.
   * @param artifact
   * @return the VBox for the general tab
   */
  private VBox createGeneralTab(final PackageArtifact artifact) {
    final VBox propertiesBox = new VBox(12);

    propertiesBox.getStyleClass().add(PACKAGE_TOOL_POPUP_PROPERTY_TAB);
    Set<String> creatorProperties = packageOntologyService.getCreatorProperties(artifact);
    final Map<String, String> properties = packageOntologyService.getProperties(artifact);

    Label requiredLabel = new Label(labels.get(Labels.LabelKey.REQUIRED_FIELDS_LABEL));
    requiredLabel.setMaxWidth(400);
    requiredLabel.setWrapText(true);
    requiredLabel.setTextAlignment(TextAlignment.CENTER);

    propertiesBox.getChildren().add(requiredLabel);
    List<String> sortedProperties = new ArrayList<>();

    // Get the property name key set and then create a sorted list from it.
    sortedProperties.addAll(properties.keySet());
    sortProperties(sortedProperties, artifact, "");

    // Loop through all the available properties
    for (final String property : sortedProperties) {
      // If the property isn't a creator property we include it in this tab
      if (!creatorProperties.contains(property)) {
        final PackageDescriptionViewImpl.ArtifactPropertyContainer container =
            new PackageDescriptionViewImpl.ArtifactPropertyContainer();

        // If the property is complex use the group property creation.
        if (packageOntologyService.isPropertyComplex(properties.get(property))) {
          container.isComplex = true;
          VBox complexPropertyBox =
              createGroupPropertySection(
                  artifact, property, properties.get(property), false, container);
          propertiesBox.getChildren().add(complexPropertyBox);
          int maxOccurrences =
              packageOntologyService.getPropertyMaxOccurrences(artifact, property, "");

          // If the property allows for more than one value include a button to add more fields.
          if (maxOccurrences > 1) {
            final Button addNewButton =
                new Button(
                    labels.get(Labels.LabelKey.ADD_NEW_BUTTON)
                        + " "
                        + ontologyLabels.get(property));
            addNewButton.setMaxWidth(addNewButtonMaxWidth);
            addNewButton.setDisable(true);
            propertiesBox.getChildren().add(addNewButton);

            final GroupPropertyChangeListener listener =
                new GroupPropertyChangeListener(addNewButton, container);

            for (Node n : propertiesBox.getChildren()) {
              if (n instanceof VBox) {
                addChangeListenerToSectionFields((VBox) n, listener);
              }
            }

            listener.changed(null, "n/a", "n/a");

            addNewButton.setOnAction(
                arg0 -> {
                  VBox complexPropertyBox1 =
                      createGroupPropertySection(
                          artifact, property, properties.get(property), true, container);
                  int buttonIndex = propertiesBox.getChildren().indexOf(addNewButton);

                  propertiesBox.getChildren().add(buttonIndex, complexPropertyBox1);

                  addChangeListenerToSectionFields(complexPropertyBox1, listener);
                  addNewButton.setDisable(true);
                  requestFocusForNewGroup(complexPropertyBox1);
                });
            Separator groupSeparator = new Separator();
            propertiesBox.getChildren().add(groupSeparator);
          }

        } else {
          // If it's a simple property use the create property box.
          int maxOccurances =
              packageOntologyService.getPropertyMaxOccurrences(artifact, property, "");
          int minOccurances =
              packageOntologyService.getPropertyMinOccurrences(artifact, property, "");
          boolean systemGenerated =
              packageOntologyService.isSystemSuppliedProperty(artifact, property);

          Set<StringProperty> fieldProperties = new HashSet<>();
          if (packageOntologyService.isDisciplineProperty(artifact, property)) {
            propertiesBox
                .getChildren()
                .add(
                    new DisciplinePropertyBox(
                        ontologyLabels.get(property),
                        artifact.getSimplePropertyValues(property),
                        maxOccurances,
                        fieldProperties,
                        minOccurances,
                        systemGenerated,
                        availableDisciplines));
          } else {
            propertiesBox
                .getChildren()
                .add(
                    new TextPropertyBox(
                        artifact,
                        "",
                        ontologyLabels.get(property),
                        property,
                        artifact.getSimplePropertyValues(property),
                        maxOccurances,
                        fieldProperties,
                        minOccurances,
                        systemGenerated,
                        packageOntologyService,
                        labels,
                        messages,
                        applyButtonValidationListener));
          }
          container.values = fieldProperties;
        }

        artifactPropertyFields.put(property, container);
      }
    }
    return propertiesBox;
  }