/*
 * Copyright (C) 2012 The Guava Authors
 *
 * 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.common.collect;

import java.util.AbstractMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;

import javax.annotation.Nullable;

/**
 * Skeletal implementation of {@link NavigableMap}.
 * 
 * @author Louis Wasserman
 */
abstract class AbstractNavigableMap<K, V> extends AbstractMap<K, V> implements NavigableMap<K, V> {

	@Override
	@Nullable
	public abstract V get(@Nullable Object key);

	@Override
	@Nullable
	public Entry<K, V> firstEntry() {
		return Iterators.getNext(entryIterator(), null);
	}

	@Override
	@Nullable
	public Entry<K, V> lastEntry() {
		return Iterators.getNext(descendingEntryIterator(), null);
	}

	@Override
	@Nullable
	public Entry<K, V> pollFirstEntry() {
		return Iterators.pollNext(entryIterator());
	}

	@Override
	@Nullable
	public Entry<K, V> pollLastEntry() {
		return Iterators.pollNext(descendingEntryIterator());
	}

	@Override
	public K firstKey() {
		Entry<K, V> entry = firstEntry();
		if (entry == null) {
			throw new NoSuchElementException();
		} else {
			return entry.getKey();
		}
	}

	@Override
	public K lastKey() {
		Entry<K, V> entry = lastEntry();
		if (entry == null) {
			throw new NoSuchElementException();
		} else {
			return entry.getKey();
		}
	}

	@Override
	@Nullable
	public Entry<K, V> lowerEntry(K key) {
		return headMap(key, false).lastEntry();
	}

	@Override
	@Nullable
	public Entry<K, V> floorEntry(K key) {
		return headMap(key, true).lastEntry();
	}

	@Override
	@Nullable
	public Entry<K, V> ceilingEntry(K key) {
		return tailMap(key, true).firstEntry();
	}

	@Override
	@Nullable
	public Entry<K, V> higherEntry(K key) {
		return tailMap(key, false).firstEntry();
	}

	@Override
	public K lowerKey(K key) {
		return Maps.keyOrNull(lowerEntry(key));
	}

	@Override
	public K floorKey(K key) {
		return Maps.keyOrNull(floorEntry(key));
	}

	@Override
	public K ceilingKey(K key) {
		return Maps.keyOrNull(ceilingEntry(key));
	}

	@Override
	public K higherKey(K key) {
		return Maps.keyOrNull(higherEntry(key));
	}

	abstract Iterator<Entry<K, V>> entryIterator();

	abstract Iterator<Entry<K, V>> descendingEntryIterator();

	@Override
	public SortedMap<K, V> subMap(K fromKey, K toKey) {
		return subMap(fromKey, true, toKey, false);
	}

	@Override
	public SortedMap<K, V> headMap(K toKey) {
		return headMap(toKey, false);
	}

	@Override
	public SortedMap<K, V> tailMap(K fromKey) {
		return tailMap(fromKey, true);
	}

	@Override
	public NavigableSet<K> navigableKeySet() {
		return new Maps.NavigableKeySet<K, V>(this);
	}

	@Override
	public Set<K> keySet() {
		return navigableKeySet();
	}

	@Override
	public abstract int size();

	@Override
	public Set<Entry<K, V>> entrySet() {
		return new Maps.EntrySet<K, V>() {
			@Override
			Map<K, V> map() {
				return AbstractNavigableMap.this;
			}

			@Override
			public Iterator<Entry<K, V>> iterator() {
				return entryIterator();
			}
		};
	}

	@Override
	public NavigableSet<K> descendingKeySet() {
		return descendingMap().navigableKeySet();
	}

	@Override
	public NavigableMap<K, V> descendingMap() {
		return new DescendingMap();
	}

	private final class DescendingMap extends Maps.DescendingMap<K, V> {
		@Override
		NavigableMap<K, V> forward() {
			return AbstractNavigableMap.this;
		}

		@Override
		Iterator<Entry<K, V>> entryIterator() {
			return descendingEntryIterator();
		}
	}

}