/**
 * Copyright 2009 Google Inc.
 *
 * 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 org.waveprotocol.wave.model.adt.docbased;

import org.waveprotocol.wave.model.adt.ObservableMonotonicMap;
import org.waveprotocol.wave.model.document.util.DocumentEventRouter;
import org.waveprotocol.wave.model.util.Serializer;

/**
 * Provides a map of keys to monotonically increasing values, as a region of a concurrent document.
 *
 * @param <E> document's element type
 * @param <K> map key type
 * @param <C> map value type (comparable)
 */
public class DocumentBasedMonotonicMap<E, K, C extends Comparable<C>>
    extends AbstractDocumentBasedMap<E, K, C> implements ObservableMonotonicMap<K, C> {

  /**
   * Monotonicity enforcement. The entry parameter is ignored.
   *
   * @return true iff the new value is greater than the current value or null.
   */
  @Override
  protected boolean canReplace(E oldEntry, E newEntry, C currentValue, C value) {
    if (currentValue == null || currentValue.compareTo(value) < 0) {
      return true;
    }
    return false;
  }

  /** Do not alter the map for entries that would be rejected for non-monotonicity. */
  @Override
  protected boolean isRedundantPut(C currentValue, C newValue) {
    if (currentValue == null) {
      return newValue == null;
    } else if (newValue != null) {
      // Reject currentValue >= newValue.
      return currentValue.compareTo(newValue) >= 0;
    } else {
      // This condition (currentValue not null, newValue is null) should only
      // occur when the map is being deleted.
      return false;
    }
  }

  /**
   * Creates a monotonic map.
   *
   * @param router           router for the document holding the map state
   * @param entryContainer   element in which entry elements should be created
   * @param keySerializer    converter between strings and keys
   * @param valueSerializer  converter between strings and values
   * @param entryTagName     name to use for entry elements
   * @param keyAttrName      name to use for key attributes
   * @param valueAttrName    name to use for value attributes
   * @param activeCleanUp    if true, will delete observed redundant entries
   */
  DocumentBasedMonotonicMap(DocumentEventRouter<? super E, E, ?> router,
      E entryContainer, Serializer<K> keySerializer, Serializer<C> valueSerializer,
      String entryTagName, String keyAttrName, String valueAttrName,
      boolean activeCleanUp) {
    super(router, entryContainer, keySerializer, valueSerializer, entryTagName, keyAttrName,
        valueAttrName, activeCleanUp);
  }

  /**
   * Creates a monotonic map.
   */
  public static <E, K, C extends Comparable<C>> DocumentBasedMonotonicMap<E, K, C> create(
      DocumentEventRouter<? super E, E, ?> router,
      E entryContainer, Serializer<K> keySerializer, Serializer<C> valueSerializer,
      String entryTagName, String keyAttrName, String valueAttrName) {
    DocumentBasedMonotonicMap<E, K, C> map = new DocumentBasedMonotonicMap<E, K, C>(
        router, entryContainer, keySerializer, valueSerializer,
        entryTagName, keyAttrName, valueAttrName, true);
    map.dispatchAndLoad();
    return map;
  }
}
