/**
 * Copyright (C) 2011 Akiban Technologies Inc.
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 */

package com.akiban.server.store.statistics;

import com.akiban.ais.model.Index;

import java.util.List;

/** Index statistics
 */
public class IndexStatistics
{
    private long analysisTimestamp, rowCount, sampledCount;
    private Histogram[] histograms; // Indexed by column count.

    protected IndexStatistics(Index index) {
        this.histograms = new Histogram[index.getKeyColumns().size()];
    }

    public long getAnalysisTimestamp() {
        return analysisTimestamp;
    }
    public void setAnalysisTimestamp(long analysisTimestamp) {
        this.analysisTimestamp = analysisTimestamp;
    }

    public long getRowCount() {
        return rowCount;
    }
    protected void setRowCount(long rowCount) {
        this.rowCount = rowCount;
    }

    public long getSampledCount() {
        return sampledCount;
    }
    protected void setSampledCount(long sampledCount) {
        this.sampledCount = sampledCount;
    }

    public Histogram getHistogram(int columnCount) {
        return histograms[columnCount - 1];
    }

    protected void addHistogram(Histogram histogram) {
        assert (histograms[histogram.getColumnCount() - 1] == null);
        histograms[histogram.getColumnCount() - 1] = histogram;
    }
    
    public static class Histogram {
        private int columnCount;
        private List<HistogramEntry> entries;
        
        protected Histogram(int columnCount, List<HistogramEntry> entries) {
            this.columnCount = columnCount;
            this.entries = entries;
        }

        public int getColumnCount() {
            return columnCount;
        }
        public List<HistogramEntry> getEntries() {
            return entries;
        }

        public long totalDistinctCount() {
            long total = 0;
            for (HistogramEntry entry : entries) {
                if (entry.getEqualCount() > 0)
                    total++;
                total += entry.getDistinctCount();
            }
            return total;
        }

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

        public String toString(Index index) {
            StringBuilder str = new StringBuilder(getClass().getSimpleName());
            if (index != null) {
                str.append(" for ").append(index.getIndexName()).append("(");
                for (int j = 0; j < columnCount; j++) {
                    if (j > 0) str.append(", ");
                    str.append(index.getKeyColumns().get(j).getColumn().getName());
                }
                str.append("):\n");
            }
            str.append(entries);
            return str.toString();
        }

    }

    public static class HistogramEntry extends HistogramEntryDescription {
        private byte[] keyBytes;

        protected HistogramEntry(String keyString, byte[] keyBytes,
                                 long equalCount, long lessCount, long distinctCount) {
            super(keyString, equalCount, lessCount, distinctCount);
            this.keyBytes = keyBytes;
        }

        public byte[] getKeyBytes() {
            return keyBytes;
        }
    }
    
    public static class HistogramEntryDescription {

        protected String keyString;
        protected long equalCount;
        protected long lessCount;
        protected long distinctCount;

        public HistogramEntryDescription(String keyString, long equalCount, long lessCount, long distinctCount) {
            this.distinctCount = distinctCount;
            this.equalCount = equalCount;
            this.keyString = keyString;
            this.lessCount = lessCount;
        }

        public String getKeyString() {
            return keyString;
        }

        public long getEqualCount() {
            return equalCount;
        }

        public long getLessCount() {
            return lessCount;
        }

        public long getDistinctCount() {
            return distinctCount;
        }

        @Override
        final public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof HistogramEntryDescription)) return false;

            HistogramEntryDescription that = (HistogramEntryDescription) o;

            return distinctCount == that.distinctCount
                    && equalCount == that.equalCount
                    && lessCount == that.lessCount
                    && keyString.equals(that.keyString);

        }

        @Override
        final public int hashCode() {
            int result = keyString.hashCode();
            result = 31 * result + (int) (equalCount ^ (equalCount >>> 32));
            result = 31 * result + (int) (lessCount ^ (lessCount >>> 32));
            result = 31 * result + (int) (distinctCount ^ (distinctCount >>> 32));
            return result;
        }

        @Override
        public String toString() {
            return "{" + getKeyString() +
                    ": = " + getEqualCount() +
                    ", < " + getLessCount() +
                    ", distinct " + getDistinctCount() +
                    "}";
        }
    }

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

    public String toString(Index index) {
        StringBuilder str = new StringBuilder(super.toString());
        if (index != null)
            str.append(" for ").append(index);
        for (int i = 0; i < histograms.length; i++) {
            Histogram h = histograms[i];
            if (h == null) continue;
            str.append("\n");
            str.append(h.toString(index));
        }
        return str.toString();
    }

}

@@ -1051,32 +1051,98 @@
 size()%5D;%0A    %7D%0A%0A
+    /** The system time at which the statistics were gathered. */%0A
     public long 
@@ -1162,24 +1162,24 @@
 mestamp() %7B%0A
-
         retu
@@ -1320,32 +1320,97 @@
 mestamp;%0A    %7D%0A%0A
+    /** The number of rows in the index when it was analyzed. */%0A
     public long 
@@ -1537,32 +1537,152 @@
 owCount;%0A    %7D%0A%0A
+    /** The number of rows that were actually sampled.%0A     * Right now, always equal to %3Ccode%3ErowCount%3C/code%3E.%0A     */%0A
     public long 
@@ -4538,32 +4538,94 @@
 unt;%0A        %7D%0A%0A
+        /** A user-visible form of the key for this entry. */%0A
         public S
@@ -4700,79 +4700,300 @@
     
-public long getEqualCount() %7B%0A            return equalCount;%0A        %7D%0A
+/** The number of samples that were equal to the key value. */%0A        public long getEqualCount() %7B%0A            return equalCount;%0A        %7D%0A%0A        /** The number of samples that were less than the key value%0A         * (and greater than the previous entry's key value, if any).%0A         */
 %0A   
@@ -5059,32 +5059,101 @@
 unt;%0A        %7D%0A%0A
+        /** The number of distinct values in the less-than range. */%0A
         public l

Update javadoc.