// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed 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 com.google.collide.client.util.collections;

import javax.annotation.Nonnull;

import com.google.collide.json.shared.JsonArray;
import com.google.collide.json.shared.JsonStringMap;
import com.google.common.base.Preconditions;

/**
 * Implementation of sorted multiset of strings based
 * on {@link SkipListStringSet}.
 *
 * <p>This collection allows storing Strings so that <ul>
 * <li>adding / removing item is performed in {@code O(1)} if there is no equal
 * item in collection, otherwise {@code O(lg N)}
 * <li>searching item is performed in {@code O(lg N)}
 * <li>search finds earliest (in sorting order) item greater or equal to given
 * <li>fetching next item is performed in {@code O(1)}
 * <li>every unique item appears only once when iterating
 * <li>iterator returns items in the sorted order
 * </ul>
 *
 * <p>Actually this object is composed of skip list (for sorted search result)
 * and map (for better performance).
 *
 * <p>{@link #remove} will fail in situation then item counter should become
 * less than zero.
 *
 * <p>{@link #search} is delegated to {@link SkipListStringSet} instance.
 *
 * <p>{@code null} items should not be passed to add / remove / search.
 *
 */
public final class SkipListStringBag implements StringMultiset {

  private JsonStringMap<Counter> itemCounters;

  private SkipListStringSet itemsSet;

  public SkipListStringBag() {
    clear();
  }

  @Override
  public final void addAll(@Nonnull JsonArray<String> items) {
    // TODO: Check if iterate is faster.
    for (int i = 0, l = items.size(); i < l; i++) {
      add(items.get(i));
    }
  }

  @Override
  public final void add(@Nonnull String item) {
    Counter counter = itemCounters.get(item);
    if (counter == null) {
      counter = new Counter();
      itemCounters.put(item, counter);
      itemsSet.add(item);
    } else {
      counter.increment();
    }
  }

  @Override
  public final void removeAll(@Nonnull JsonArray<String> items) {
    // TODO: Check if iterate is faster.
    for (int i = 0, l = items.size(); i < l; i++) {
      remove(items.get(i));
    }
  }

  @Override
  public final void remove(@Nonnull String item) {
    Counter counter = itemCounters.get(item);
    // TODO: Remove this precondition.
    Preconditions.checkNotNull(counter, "trying to remove absent item: %s", item);
    if (counter.decrement()) {
      itemCounters.remove(item);
      itemsSet.remove(item);
    }
  }

  @Override
  public boolean contains(@Nonnull String item) {
    return itemCounters.containsKey(item);
  }

  public final Iterable<String> search(@Nonnull String item) {
    return itemsSet.search(item);
  }

  @Override
  public void clear() {
    itemCounters = ClientStringMap.create();
    itemsSet = SkipListStringSet.create();
  }
}
