package uk.ac.ebi.quickgo.annotation.model;

import com.google.common.base.Preconditions;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * Represents a set of statistics values for a given type.
 *
 * @author Ricardo Antunes
 */
public class StatisticsByType {
    private final String type;
    private final int distinctValueCount;
    private final List<StatisticsValue> values;

    public StatisticsByType(String type, int distinctValueCount) {
        Preconditions.checkArgument(type != null && !type.isEmpty(), "Statistics type cannot be null or empty.");
        Preconditions.checkArgument(distinctValueCount >= 0, "Distinct Value Count should be be greater than zero.");
        this.type = type;
        this.distinctValueCount = distinctValueCount;
        this.values = new ArrayList<>();
    }

    public StatisticsByType(String type) {
        this(type, 0);
    }

    public String getType() {
        return type;
    }

    public int getDistinctValueCount() {
        return truncateToTensOfThousands(distinctValueCount);
    }

    public void addValue(StatisticsValue value) {
        Preconditions.checkArgument(value != null, "Stats value cannot be null");
        values.add(value);
    }

    public List<StatisticsValue> getValues() {
        return values;
    }

    @Override public int hashCode() {
        int result = type.hashCode();
        result = 31 * result + values.hashCode();
        return result;
    }

    @Override public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        StatisticsByType that = (StatisticsByType) o;

        if (distinctValueCount != that.distinctValueCount) {
            return false;
        }
        if (type != null ? !type.equals(that.type) : that.type != null) {
            return false;
        }
        return values != null ? values.equals(that.values) : that.values == null;
    }

    @Override public String toString() {
        return "StatisticsByType{" +
                "type='" + type + '\'' +
                ", distinctValueCount=" + distinctValueCount +
                ", values=" + values +
                '}';
    }

    private int truncateToTensOfThousands(int distinctValueCount) {
        if (distinctValueCount < 10001) {
            return distinctValueCount;
        }
        int value = new BigDecimal(distinctValueCount).divide(new BigDecimal(1000), RoundingMode.HALF_UP).intValue();
        return Integer.parseInt(String.format("%d000", value));
    }
}

@@ -373,36 +373,34 @@
 e final int 
-distinctValu
+approximat
 eCount;%0A    
@@ -452,61 +452,667 @@
     
-public StatisticsByType(String type, int distinctValu
+/**%0A     * Create an instances of StatisticsByType%0A     * @param type the type i.e. name of the thing the statistics represents e.g. 'goId', 'aspect'%0A     * @param approximateCount An estimated (due to the limitations of the persistence layer) count of the unique%0A     * elements represented by this object. For example, if the type was 'goId', the approximateCount will be the%0A     * number of unique goIds within the statistics search result. This MIGHT NOT be the same as the size of the%0A     * values collection, since this can be curtailed by the limit defined for the facet query response.%0A     */%0A    public StatisticsByType(String type, int approximat
 eCou
@@ -1267,28 +1267,26 @@
 rgument(
-distinctValu
+approximat
 eCount %3E
@@ -1390,41 +1390,37 @@
 his.
-distinctValueCount = distinctValu
+approximateCount = approximat
 eCou
@@ -1610,59 +1610,573 @@
     
-public int getDistinctValueCount() %7B%0A        return
+/**%0A     * An estimated count of the unique elements represented by this object. If the estimated count is less than the%0A     * size of the results this object contains, then this method will return the count of the number of statistics%0A     * elements this instance holds,  otherwise the estimated value will be returned, truncated to the  nearest tens of%0A     * thousands (if over ten thousand) to reflect it's lack of accuracy.%0A     * @return int of%0A     */%0A    public int getApproximateCount() %7B%0A        return approximateCount %3C values.size() ? values.size() :
  tru
@@ -2198,28 +2198,26 @@
 ousands(
-distinctValu
+approximat
 eCount);
@@ -2886,36 +2886,34 @@
         if (
-distinctValu
+approximat
 eCount != th
@@ -2915,28 +2915,26 @@
 != that.
-distinctValu
+approximat
 eCount) 
@@ -3312,43 +3312,39 @@
  %22, 
-distinctValueCount=%22 + distinctValu
+approximateCount=%22 + approximat
 eCou
@@ -3461,28 +3461,26 @@
 nds(int 
-distinctValu
+approximat
 eCount) 
@@ -3493,28 +3493,26 @@
     if (
-distinctValu
+approximat
 eCount %3C
@@ -3540,28 +3540,26 @@
  return 
-distinctValu
+approximat
 eCount;%0A
@@ -3607,20 +3607,18 @@
 mal(
-distinctValu
+approximat
 eCou

Rename distinctValueCount to approximateCount. Use the bucket size instead of count if count is less than bucket size.