package edu.stanford.nlp.ling;

import edu.stanford.nlp.stats.ClassicCounter;
import edu.stanford.nlp.stats.Counter;

import java.util.Collection;
import java.util.Collections;

/**
 * A basic implementation of the Datum interface that can be constructed with a
 * Collection of features and one more more labels. The features must be
 * specified at construction, but the labels can be set and/or changed later.
 *
 * @author Jenny Finkel
 *         <a href="mailto:jrfinkel@stanford.edu">jrfinkel@stanford.edu</a>
 * @author Sarah Spikes (sdspikes@stanford.edu) [templatized]
 *
 * @param <L>
 *          The type of the label of the datum
 * @param <F>
 *          The type of individual features stored in the datum
 */
public class RVFDatum<L, F> implements Datum<L, F> {

  private static final long serialVersionUID = -255312811814660438L;

  /**
   * features for this Datum
   */
  private final Counter<F> features;

  /**
   * labels for this Datum. Invariant: always non-null
   */
  private L label; // = null;

  /**
   * Id of this instance
   */
  private String id = null;
  
  /**
   * Constructs a new RVFDatum with the given features and label.
   */
  public RVFDatum(Counter<F> features, L label) {
    this.features = features;
    setLabel(label);
  }

  /**
   * Constructs a new RVFDatum taking the data from a Datum. <i>Implementation
   * note:</i> This constructor allocates its own counter over features, but is
   * only guaranteed correct if the label and feature names are immutable.
   *
   * @param m The Datum to copy.
   */
  public RVFDatum(Datum<L, F> m) {
    this.features = new ClassicCounter<>();
    for (F key : m.asFeatures()) {
      features.incrementCount(key, 1.0);
    }
    setLabel(m.label());
  }

  /**
   * Constructs a new RVFDatum with the given features and no labels.
   */
  public RVFDatum(Counter<F> features) {
    this.features = features;
  }

  /**
   * Constructs a new RVFDatum with no features or labels.
   */
  public RVFDatum() {
    this((ClassicCounter<F>) null);
  }

  /**
   * Returns the Counter of features and values
   */
  public Counter<F> asFeaturesCounter() {
    return features;
  }

  /**
   * Returns the list of features without values
   */
  public Collection<F> asFeatures() {
    return features.keySet();
  }

  /**
   * Removes all currently assigned Labels for this Datum then adds the given
   * Label. Calling <tt>setLabel(null)</tt> effectively clears all labels.
   */
  public void setLabel(L label) {
    this.label = label;
  }
  
  /**
   * Sets id for this instance
   * @param id
   */
  public void setID(String id){
    this.id = id;
  }

  /**
   * Returns a String representation of this BasicDatum (lists features and
   * labels).
   */
  @Override
  public String toString() {
    return "RVFDatum[id="+id+", features=" + asFeaturesCounter() + ",label=" + label() + "]";
  }

  public L label() {
    return label;
  }

  public Collection<L> labels() {
    return Collections.singletonList(label);
  }

  public double getFeatureCount(F feature) {
    return features.getCount(feature);
  }
  
  public String id(){
    return id;
  }

  /**
   * Returns whether the given RVFDatum contains the same features with the same
   * values as this RVFDatum. An RVFDatum can only be equal to another RVFDatum.
   * <i>Implementation note:</i> Doesn't check the labels, should we change
   * this?
   */
  @Override
  @SuppressWarnings("unchecked")
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof RVFDatum)) {
      return (false);
    }
    RVFDatum<L, F> d = (RVFDatum<L, F>) o;
    return features.equals(d.asFeaturesCounter());
  }

  /** {@inheritDoc} */
  @Override
  public int hashCode() {
    return features.hashCode();
  }

}
