/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package org.apache.nutch.util;

import java.util.*;

public class Histogram<E> {

  private Map<E, HistogramEntry> map = new HashMap<E, HistogramEntry>();
  private float totalValue = 0;
  private int totalCount = 0;

  public void add(E x) {
    add(x, 1);
  }

  public void add(E x, float value) {
    HistogramEntry entry;
    if (map.containsKey(x)) {
      entry = map.get(x);
      entry.value += value;
      entry.count++;
    } else {
      entry = new HistogramEntry();
      entry.value = value;
      entry.count = 1;
      map.put(x, entry);
    }
    totalValue += value;
    totalCount += 1;
  }

  public Set<E> getKeys() {
    return map.keySet();
  }

  public float getValue(E x) {
    return map.get(x).value;
  }

  public int getCount(E x) {
    return map.get(x).count;
  }

  public void add(Histogram<E> other) {
    for (E x : other.getKeys()) {
      add(x, other.getValue(x));
    }
  }

  public Histogram<E> normalize() {
    Histogram<E> normalized = new Histogram<E>();
    Set<E> keys = getKeys();
    for (E x : keys) {
      normalized.add(x, getValue(x) / totalValue);
    }
    return normalized;
  }

  public List<E> sortInverseByValue() {
    List<Map.Entry<E, HistogramEntry>> list = new Vector<Map.Entry<E, HistogramEntry>>(
        map.entrySet());

    // Sort the list using an annonymous inner class implementing Comparator for
    // the compare method
    java.util.Collections.sort(list,
        new Comparator<Map.Entry<E, HistogramEntry>>() {
          public int compare(Map.Entry<E, HistogramEntry> entry,
              Map.Entry<E, HistogramEntry> entry1) {
            return (entry.getValue().equals(entry1.getValue()) ? 0 : (entry
                .getValue().value < entry1.getValue().value ? 1 : -1));
          }
        });
    List<E> list2 = new Vector<E>();
    for (Map.Entry<E, HistogramEntry> entry : list) {
      list2.add(entry.getKey());
    }
    return list2;
  }

  public List<E> sortByValue() {
    List<Map.Entry<E, HistogramEntry>> list = new Vector<Map.Entry<E, HistogramEntry>>(
        map.entrySet());

    // Sort the list using an annonymous inner class implementing Comparator for
    // the compare method
    java.util.Collections.sort(list,
        new Comparator<Map.Entry<E, HistogramEntry>>() {
          public int compare(Map.Entry<E, HistogramEntry> entry,
              Map.Entry<E, HistogramEntry> entry1) {
            return (entry.getValue().equals(entry1.getValue()) ? 0 : (entry
                .getValue().value > entry1.getValue().value ? 1 : -1));
          }
        });
    List<E> list2 = new Vector<E>();
    for (Map.Entry<E, HistogramEntry> entry : list) {
      list2.add(entry.getKey());
    }
    return list2;
  }

  public String toString(List<E> items) {
    StringBuilder strBuilder = new StringBuilder();
    for (E item : items) {
      strBuilder.append(map.get(item).value).append(",").append(item)
          .append("\n");
    }
    return strBuilder.toString();
  }

  class HistogramEntry {
    float value;
    int count;
  }
}
