package org.asciidoc.intellij.psi;

import org.jetbrains.annotations.NotNull;

import java.util.Objects;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AntoraVersionDescriptor implements Comparable<AntoraVersionDescriptor> {

  private static final Pattern SEMANTIC_PATTERN = Pattern.compile("v?(?<major>[0-9])+\\..*");

  private final String version;
  private final String prerelease;

  private Scheme scheme;

  public enum Scheme implements Comparable<Scheme> {
    NAMED,
    SEMANTIC
  }

  public AntoraVersionDescriptor(String version, String prerelease) {
    this.version = version;
    this.prerelease = prerelease;
    if (version != null) {
      Matcher matcher = SEMANTIC_PATTERN.matcher(version);
      if (matcher.matches()) {
        scheme = Scheme.SEMANTIC;
      } else {
        scheme = Scheme.NAMED;
      }
    }
  }

  @Override
  public int compareTo(@NotNull AntoraVersionDescriptor o) {
    if (version == null && o.version != null) {
      return -1;
    }
    if (version != null && o.version == null) {
      return 1;
    }
    if (version == null) {
      return 0;
    }
    if (version.equals("master") && !o.version.equals("master")) {
      return 1;
    }
    if (!version.equals("master") && o.version.equals("master")) {
      return -1;
    }
    if (version.equals("") && !o.version.equals("")) {
      return 1;
    }
    if (!version.equals("") && o.version.equals("")) {
      return -1;
    }
    if (prerelease == null && o.prerelease != null) {
      return 1;
    } else if (prerelease != null && o.prerelease == null) {
      return -1;
    } else if (prerelease != null) {
      return prerelease.compareToIgnoreCase(o.prerelease);
    }
    int result = -scheme.compareTo(o.scheme);
    if (result != 0) {
      return result;
    }
    if (scheme == Scheme.NAMED) {
      return version.compareToIgnoreCase(o.version);
    } else {
      return compareSemantic(version, o.version);
    }
  }

  private int compareSemantic(String a, String b) {
    StringTokenizer numsA = new StringTokenizer(a, ".");
    StringTokenizer numsB = new StringTokenizer(b, ".");
    for (int i = 0; i < 3; i++) {
      double numA;
      try {
        if (numsA.hasMoreElements()) {
          numA = Double.parseDouble(numsA.nextToken());
        } else {
          numA = 0;
        }
      } catch (NumberFormatException e) {
        numA = Double.NaN;
      }
      double numB;
      try {
        if (numsB.hasMoreElements()) {
          numB = Double.parseDouble(numsB.nextToken());
        } else {
          numB = 0;
        }
      } catch (NumberFormatException e) {
        numB = Double.NaN;
      }
      if (numA > numB) {
        return 1;
      } else if (numB > numA) {
        return -1;
      } else if (Double.isNaN(numA)) {
        if (!Double.isNaN(numB)) {
          return -1;
        }
      } else if (Double.isNaN(numB)) {
        return 1;
      }
    }
    return 0;
  }

  @Override
  public int hashCode() {
    return version.hashCode();
  }

  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof AntoraVersionDescriptor)) {
      return false;
    }
    return Objects.equals(((AntoraVersionDescriptor) obj).version, version);
  }

  @Override
  public String toString() {
    return version;
  }
}

@@ -1178,63 +1178,50 @@
 if (
-version.equals(%22master%22)
+prerelease == null
  && 
-!
 o.
-version.equals(%22master%22)
+prerelease != null
 ) %7B%0A
@@ -1245,225 +1245,300 @@
    %7D
-%0A    if (!version.equals(%22master%22) && o.version.equals(%22master%22)) %7B%0A      return -1;%0A    %7D%0A    if (version.equals(%22%22) && !o.version.equals(%22%22)) %7B%0A      return 1;%0A    %7D%0A    if (!version.equals(%22%22) && o.version.equals(%22
+ else if (prerelease != null && o.prerelease == null) %7B%0A      return -1;%0A    %7D else if (prerelease != null && Objects.equals(version, o.version)) %7B%0A      return prerelease.compareToIgnoreCase(o.prerelease);%0A    %7D%0A    if (Objects.equals(version, %22master%22) && !Objects.equals(o.version, %22master
 %22)) 
@@ -1544,33 +1544,32 @@
  %7B%0A      return 
--
 1;%0A    %7D%0A    if 
@@ -1573,50 +1573,81 @@
 if (
-prerelease == null && o.prerelease != null
+!Objects.equals(version, %22master%22) && Objects.equals(o.version, %22master%22)
 ) %7B%0A
@@ -1655,32 +1655,33 @@
      return 
+-
 1;%0A    %7D
  else if (pr
@@ -1672,60 +1672,78 @@
    %7D
- else if (prerelease != null && o.prerelease == null
+%0A    if (Objects.equals(version, %22%22) && !Objects.equals(o.version, %22%22)
 ) %7B%0A
@@ -1751,33 +1751,32 @@
      return 
--
 1;%0A    %7D
  else if (pr
@@ -1767,97 +1767,97 @@
    %7D
- else if (prerelease != null) %7B%0A      return prerelease.compareToIgnoreCase(o.prerelease)
+%0A    if (!Objects.equals(version, %22%22) && Objects.equals(o.version, %22%22)) %7B%0A      return -1
 ;%0A  

rework ordering pre pre-releases (#728)