package pas.multiset;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

/**
 * Multiset collection type for Strings.
 *
 * @author CS 240 Instructors and ???
 * @version V3, 1/2024
 *
 */
public class Multiset implements Iterable<String> {

  private ArrayList<String> elements;

  /**
   * Create an empty Multiset.
   */
  public Multiset() {
    elements = new ArrayList<String>();
  }

  /**
   * Add an item.
   *
   * @param item The item to add
   * @return Always returns true.
   */
  public boolean add(String item) {
    return elements.add(item);
  }

  /**
   * Add some number of occurrences for an item.
   *
   * @param item Item to add
   * @param occurances The number of occurrences of the item
   * @return The count of the item before the operation.
   */
  public int add(String item, int occurances) {
    int num = getCount(item);
    for (int i = 0; i < occurances; i++) {
      elements.add(item);
    }
    return num;
  }

  /**
   * Remove all elements.
   */
  public void clear() {
    elements.clear();
  }

  /**
   * Return true if the provided item is contained in the Multiset.
   *
   * @param item The item to check
   * @return true if the item is in the Multiset, false otherwise
   */
  public boolean contains(String item) {
    return elements.contains(item);
  }

  /**
   * Return a count of the number of occurrences of the provided item in the Multiset.
   *
   * @param item The item to count
   * @return The number of occurrences
   */
  public int getCount(String item) {
    int total = 0;

    for (String element : elements) {
      if (element.equals(item)) {
        total++;
      }
    }

    return total;
  }


  /**
   * Return true if the provided object is equal to this Multiset. Two Multisets are considered to
   * be equal if they contain the same elements with the same counts.
   *
   * @param other The object to check for equality
   * @return true if the object is equal to this Multiset
   */
  @Override
  public boolean equals(Object other) {

    if (!(other instanceof Multiset) || ((Multiset) other).size() != size()) {
      return false;
    }

    for (String element : elements) {
      if (this.getCount(element) != ((Multiset) other).getCount(element)) {
        return false;
      }
    }

    return true;
  }

  /**
   * Return an iterator for this Multiset. Repeated elements will be returned the appropriate number
   * of times by the iterator.
   *
   * @return The iterator
   */
  public Iterator<String> iterator() {
    return elements.iterator();
  }

  /**
   * Reduce the count of the provided element by one. Remove the element if the count reaches 0.
   *
   * @param item The element to remove
   * @return true if the element was contained in the Multiset
   */
  public boolean remove(String item) {
    return elements.remove(item);
  }

  /**
   * Return the total number of elements stored in the Multiset, taking into account the number of
   * occurrences of each element.
   *
   * @return The total number of elements
   */
  public int size() {
    return elements.size();
  }

  /**
   * Return a String representation of this Multiset. A string representation of each element will
   * be included along with the count for that element. For example, the Multiset [a, a, b] could be
   * represented by the String "[a x 2, b x 1]". The order of the elements is not specified.
   *
   * @return A string representation of the Multiset
   */
  @Override
  public String toString() {
    HashSet<String> added = new HashSet<>();
    StringBuilder sb = new StringBuilder();
    sb.append("[");

    for (String element : elements) {

      if (!added.contains(element)) {

        if (added.size() > 0) {
          sb.append(", ");
        }

        sb.append(element.toString() + " x " + this.getCount(element));
      }
      added.add(element);
    }
    sb.append("]");
    return sb.toString();
  }


}
