/*
 * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.yangtools.util;

import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import org.opendaylight.yangtools.triemap.MutableTrieMap;
import org.opendaylight.yangtools.triemap.TrieMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A TrieMap facade tracking modifications. Since we change structures based on
 * their size, and determining the size of a TrieMap is expensive, we make sure
 * to update it as we go.
 *
 * <p>FIXME: this map does not support modification view the keySet()/values()/entrySet()
 *        methods.
 *
 * @param <K> Key type
 * @param <V> Value type
 */
final class ReadWriteTrieMap<K, V> implements Map<K, V> {
    private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyTrieMap.class);
    private final MutableTrieMap<K, V> delegate;
    private int size;

    ReadWriteTrieMap() {
        this.delegate = TrieMap.create();
        this.size = 0;
    }

    ReadWriteTrieMap(final MutableTrieMap<K, V> delegate, final int size) {
        this.delegate = Preconditions.checkNotNull(delegate);
        this.size = size;
    }

    Map<K, V> toReadOnly() {
        final Map<K, V> ret = new ReadOnlyTrieMap<>(delegate, size);
        LOG.trace("Converted read-write TrieMap {} to read-only {}", this, ret);
        return ret;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean containsKey(final Object key) {
        return delegate.containsKey(key);
    }

    @Override
    public boolean containsValue(final Object value) {
        return delegate.containsValue(value);
    }

    @Override
    public V get(final Object key) {
        return delegate.get(key);
    }

    @Override
    public V put(final K key, final V value) {
        final V ret = delegate.put(key, value);
        if (ret == null) {
            size++;
        }
        return ret;
    }

    @Override
    public V remove(final Object key) {
        final V ret = delegate.remove(key);
        if (ret != null) {
            size--;
        }
        return ret;
    }

    @Override
    @SuppressWarnings("checkstyle:parameterName")
    public void putAll(@Nonnull final Map<? extends K, ? extends V> m) {
        for (Entry<? extends K, ? extends V> e : m.entrySet()) {
            put(e.getKey(), e.getValue());
        }
    }

    @Override
    public void clear() {
        delegate.clear();
        size = 0;
    }

    @Override
    public Set<K> keySet() {
        return Collections.unmodifiableSet(delegate.keySet());
    }

    @Override
    public Collection<V> values() {
        return Collections.unmodifiableCollection(delegate.values());
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return Collections.unmodifiableSet(delegate.entrySet());
    }

    @Override
    public boolean equals(final Object obj) {
        return delegate.equals(obj);
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }

    @Override
    public String toString() {
        return delegate.toString();
    }
}

@@ -384,24 +384,72 @@
 conditions;%0A
+import com.google.common.collect.ForwardingMap;%0A
 import java.
@@ -938,16 +938,20 @@
 *%0A * %3Cp%3E
+%0A * 
 FIXME: t
@@ -1029,18 +1029,8 @@
 et()
-%0A *       
  met
@@ -1129,19 +1129,26 @@
  V%3E 
-implement
+extend
 s 
+Forwarding
 Map%3C
@@ -1241,16 +1241,17 @@
 class);%0A
+%0A
     priv
@@ -1291,16 +1291,17 @@
 legate;%0A
+%0A
     priv
@@ -1579,24 +1579,107 @@
 ize;%0A    %7D%0A%0A
+    @Override%0A    protected Map%3CK, V%3E delegate() %7B%0A        return delegate;%0A    %7D%0A%0A
     Map%3CK, V
@@ -2020,336 +2020,8 @@
  %7D%0A%0A
-    @Override%0A    public boolean containsKey(final Object key) %7B%0A        return delegate.containsKey(key);%0A    %7D%0A%0A    @Override%0A    public boolean containsValue(final Object value) %7B%0A        return delegate.containsValue(value);%0A    %7D%0A%0A    @Override%0A    public V get(final Object key) %7B%0A        return delegate.get(key);%0A    %7D%0A%0A
     
@@ -3113,284 +3113,7 @@
    %7D
-%0A%0A    @Override%0A    public boolean equals(final Object obj) %7B%0A        return delegate.equals(obj);%0A    %7D%0A%0A    @Override%0A    public int hashCode() %7B%0A        return delegate.hashCode();%0A    %7D%0A%0A    @Override%0A    public String toString() %7B%0A        return delegate.toString();%0A    %7D
 %0A%7D%0A

Make ReadWriteTrieMap extend ForwardingMap