package net.ripe.db.whois.common.rpsl.attrs;

import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import net.ripe.db.whois.common.domain.CIString;

import javax.annotation.concurrent.Immutable;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static net.ripe.db.whois.common.domain.CIString.ciString;

@Immutable
public class MntRoutes {
    private static final Pattern MNT_ROUTES_PATTERN = Pattern.compile("(?i)^([A-Z](?:[A-Z0-9_-]){1,80})(?:[ ]+(\\{.*\\}|ANY))?$");
    private static final Splitter ADDRESS_PREFIX_RANGES_SPLITTER = Splitter.onPattern(",| ").trimResults().omitEmptyStrings();

    private final CIString maintainer;
    private final boolean anyRange;
    private final List<AddressPrefixRange> addressPrefixRanges;

    public MntRoutes(final String maintainer, final boolean anyRange, final List<AddressPrefixRange> addressPrefixRanges) {
        this.maintainer = ciString(maintainer);
        this.anyRange = anyRange;
        this.addressPrefixRanges = Collections.unmodifiableList(Lists.newArrayList(addressPrefixRanges));
    }

    public CIString getMaintainer() {
        return maintainer;
    }

    public boolean isAnyRange() {
        return anyRange || addressPrefixRanges.isEmpty();
    }

    public List<AddressPrefixRange> getAddressPrefixRanges() {
        return addressPrefixRanges;
    }

    @Override
    public int hashCode() { //TODO: untested
    	return toString().hashCode();
    }
    
    @Override
    public String toString() { //TODO: untested
    	String ret = maintainer + " " + anyRange;

    	if(addressPrefixRanges.size() > 3) { //if few, list them
    		ret += " [";
    		for(int i=0; i<3; i++) {
    			if(i!=0) //add trailing commas after all but first index
    				ret += ", ";
    			ret += "(" + addressPrefixRanges.get(i).toString() + ")";
    		}
    		ret += "]";
    		
    	} else { //don't list; just compute hash sum
    		ret += " addr_prefix_range_hash_total:";
    		long hashSum = 0;
        	for(AddressPrefixRange a : addressPrefixRanges) {
        		hashSum += a.hashCode(); //summing hashCodes is the technique normally used by HashSet, and we don't care about list order
        	}
        	ret += hashSum; //implicit toString and concat
    	}
    	return ret;
    }
    
    @Override
    public boolean equals(final Object o) { //TODO: untested
    	if(o == this)
    		return true;
    	if(o == null || !(o instanceof MntRoutes))
    		return false;
    	else {
    		final MntRoutes that = (MntRoutes) o;
    		
    		//using HashSets here so as to disregard the order of the maintainer routes in the source lists.
    		final HashSet<AddressPrefixRange> thisAPR = new HashSet<AddressPrefixRange>();
    		final HashSet<AddressPrefixRange> thatAPR = new HashSet<AddressPrefixRange>();
    		
    		thisAPR.addAll(addressPrefixRanges);
    		thatAPR.addAll(that.addressPrefixRanges);
    		
    		return maintainer.equals(that.maintainer) && anyRange==that.anyRange && thisAPR.equals(thatAPR);
    	}
    }
    
    public static MntRoutes parse(final CIString value) {
        return parse(value.toString());
    }

    public static MntRoutes parse(final String value) {
        final Matcher matcher = MNT_ROUTES_PATTERN.matcher(value);
        if (!matcher.matches()) {
            throw new AttributeParseException("Invalid syntax", value);
        }

        final String maintainer = matcher.group(1);

        final List<AddressPrefixRange> addressPrefixRanges = Lists.newArrayList();
        boolean anyRange = false;

        final String addressPrefixRangesString = matcher.group(2);
        if (addressPrefixRangesString != null) {
            if (addressPrefixRangesString.equalsIgnoreCase("ANY")) {
                anyRange = true;
            } else {
                final String noBrackets = addressPrefixRangesString.substring(1, addressPrefixRangesString.length() - 1);
                final Iterable<String> rangeStrings = ADDRESS_PREFIX_RANGES_SPLITTER.split(noBrackets);
                for (final String rangeString : rangeStrings) {
                    addressPrefixRanges.add(AddressPrefixRange.parse(rangeString));
                }
            }
        }

        return new MntRoutes(maintainer, anyRange, addressPrefixRanges);
    }
}

@@ -1680,71 +1680,31 @@
    %09
-if(addressPrefixRanges.size() %3E 3) %7B //if few, list them
+long hashSum = 0;
 %0A    %09
-%09
 ret 
@@ -1717,16 +1717,21 @@
 %22;%0A    %09
+%0A    
 %09for(int
@@ -1742,9 +1742,34 @@
 ; i%3C
-3
+addressPrefixRanges.size()
 ; i+
@@ -1783,65 +1783,81 @@
   %09%09
-%09
 if(i
-!=0) //add trailing commas after all but first index
+%3C3) %7B //print first 3 entries, return hash sum of all%0A    %09%09%09if(i!=0)
 %0A   
@@ -1957,366 +1957,97 @@
   %09%09
-ret += %22%5D%22;%0A    %09%09%0A    %09%7D else %7B //don't list; just compute hash sum%0A    %09%09ret
+hashSum
  +=
- %22
  addr
-_prefix_range_hash_total:%22;%0A    %09%09long hashSum = 0;%0A        %09for(AddressPrefixRange a : addressPrefixRanges) %7B%0A        %09%09hashSum += a.hashCode(); //summing hashCodes is the technique normally used by HashSet, and we don't care about list order%0A        %09%7D%0A        %09ret
+essPrefixRanges.get(i).hashCode();%0A    %09%7D%0A    %09ret += %22%5D summed_hashes:%22
  +
-=
  has
@@ -2055,46 +2055,8 @@
 Sum;
- //implicit toString and concat%0A    %09%7D
 %0A   

Improve MntRoutes.java toString(), fix logic issue