package org.metaborg.core.language;

import java.io.Serializable;
import java.util.Comparator;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.collect.ComparisonChain;

/**
 * Representation for the version of a language. Follows the versioning style of OSGI to be compatible with Eclipse
 * plugins.
 */
public class LanguageVersion implements Comparable<LanguageVersion>, Serializable {
    private static final long serialVersionUID = -4814753959508772739L;
    private static final String SNAPSHOT = "SNAPSHOT";

    private final int major;
    private final int minor;
    private final int patch;
    private final String qualifier;

    public LanguageVersion(int major, int minor, int patch, String qualifier) {
        this.major = major;
        this.minor = minor;
        this.patch = patch;
        this.qualifier = qualifier;
    }

    public int major() {
        return major;
    }

    public int minor() {
        return minor;
    }

    public int patch() {
        return patch;
    }

    public String qualifier() {
        return qualifier;
    }


    @Override public int compareTo(LanguageVersion other) {
        // @formatter:off
        return ComparisonChain.start()
            .compare(this.major, other.major)
            .compare(this.minor, other.minor)
            .compare(this.patch, other.patch)
            .compare(this.qualifier, other.qualifier, new Comparator<String>() {
                @Override public int compare(String qualifier, String other) {
                    int result = qualifier.compareToIgnoreCase(other);
                    if(result != 0) {
                        if(SNAPSHOT.equalsIgnoreCase(qualifier)) {
                            return 1;
                        } else if(SNAPSHOT.equalsIgnoreCase(other)) {
                            return -1;
                        } else {
                            return result;
                        }
                    }
                    return 0;
                }})
            .result()
            ;
        // @formatter:on
    }

    @Override public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + major;
        result = prime * result + minor;
        result = prime * result + patch;
        result = prime * result + Objects.hashCode(qualifier);
        return result;
    }

    @Override public boolean equals(Object obj) {
        if(this == obj)
            return true;
        if(obj == null)
            return false;
        if(getClass() != obj.getClass())
            return false;
        final LanguageVersion other = (LanguageVersion) obj;
        if(major != other.major)
            return false;
        if(minor != other.minor)
            return false;
        if(patch != other.patch)
            return false;
        if(!qualifier.equalsIgnoreCase(other.qualifier))
            return false;
        return true;
    }

    @Override public String toString() {
        return String.format("%d.%d.%d%s", major, minor, patch, (qualifier.isEmpty() ? "" : ("-" + qualifier)));
    }

    private static final Pattern VERSION_PATTERN = Pattern
        .compile("((\\d+)(\\.(\\d+)(\\.(\\d+))?)?(-(\\w[\\w\\-]*))?)?");

    public static boolean valid(String version) {
        final Matcher matcher = VERSION_PATTERN.matcher(version);
        if(!matcher.matches()) {
            return false;
        }
        return true;
    }

    public static LanguageVersion parse(String version) {
        final Matcher matcher = VERSION_PATTERN.matcher(version);
        if(!matcher.matches()) {
            throw new IllegalArgumentException("Invalid version string " + version);
        }

        String major = matcher.group(2);
        major = major == null || major.isEmpty() ? "0" : major;

        String minor = matcher.group(4);
        minor = minor == null || minor.isEmpty() ? "0" : minor;

        String patch = matcher.group(6);
        patch = patch == null || patch.isEmpty() ? "0" : patch;

        String qualifier = matcher.group(8);
        qualifier = qualifier == null || qualifier.isEmpty() ? "" : qualifier;

        return new LanguageVersion(Integer.parseInt(major), Integer.parseInt(minor), Integer.parseInt(patch), qualifier);
    }
}

@@ -3217,25 +3217,16 @@
  Pattern
-%0A        
 .compile
@@ -3228,23 +3228,24 @@
 mpile(%22(
-(
 %5C%5Cd+)(
+?:
 %5C%5C.(%5C%5Cd+
@@ -3246,16 +3246,18 @@
 .(%5C%5Cd+)(
+?:
 %5C%5C.(%5C%5Cd+
@@ -3266,25 +3266,21 @@
 ?)?(
--(%5C%5Cw%5B%5C%5Cw%5C%5C-%5D*))?
+?:(?:%5C%5C-)(.+)
 )?%22)
@@ -3785,17 +3785,17 @@
 r.group(
-2
+1
 );%0A     
@@ -3891,17 +3891,17 @@
 r.group(
-4
+2
 );%0A     
@@ -4001,9 +4001,9 @@
 oup(
-6
+3
 );%0A 
@@ -4107,17 +4107,17 @@
 r.group(
-8
+4
 );%0A     

Make version check more liberal