/*
 * 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.controller.md.sal.dom.store.impl;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;

import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.UnsignedLong;

public final class StoreUtils {
    private static final int STRINGTREE_INDENT = 4;

    private final static Function<Identifiable<Object>, Object> EXTRACT_IDENTIFIER = new Function<Identifiable<Object>, Object>() {
        @Override
        public Object apply(final Identifiable<Object> input) {
            return input.getIdentifier();
        }
    };

    private StoreUtils() {
        throw new UnsupportedOperationException("Utility class should not be instantiated");
    }

    public static final UnsignedLong increase(final UnsignedLong original) {
        return original.plus(UnsignedLong.ONE);
    }

    public static final InstanceIdentifier append(final InstanceIdentifier parent, final PathArgument arg) {

        return new InstanceIdentifier(ImmutableList.<PathArgument> builder().addAll(parent.getPath()).add(arg).build());
    }

    public static AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> initialChangeEvent(
            final InstanceIdentifier path, final StoreMetadataNode data) {
        return new InitialDataChangeEvent(path, data.getData());
    }

    /*
     * Suppressing warnings here allows us to fool the compiler enough
     * such that we can reuse a single function for all applicable types
     * and present it in a type-safe manner to our users.
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static <V> Function<Identifiable<V>, V> identifierExtractor() {
        return (Function) EXTRACT_IDENTIFIER;
    }

    private static final class InitialDataChangeEvent implements
            AsyncDataChangeEvent<InstanceIdentifier, NormalizedNode<?, ?>> {

        private final ImmutableMap<InstanceIdentifier, NormalizedNode<?, ?>> payload;
        private final NormalizedNode<?, ?> data;

        public InitialDataChangeEvent(final InstanceIdentifier path, final NormalizedNode<?, ?> data) {
            payload = ImmutableMap.<InstanceIdentifier, NormalizedNode<?, ?>> of(path, data);
            this.data = data;
        }

        @Override
        public Map<InstanceIdentifier, NormalizedNode<?, ?>> getCreatedData() {
            return payload;
        }

        @Override
        public Map<InstanceIdentifier, ? extends NormalizedNode<?, ?>> getOriginalData() {
            return Collections.emptyMap();
        }

        @Override
        public NormalizedNode<?, ?> getOriginalSubtree() {
            return null;
        }

        @Override
        public Set<InstanceIdentifier> getRemovedPaths() {
            return Collections.emptySet();
        }

        @Override
        public Map<InstanceIdentifier, NormalizedNode<?, ?>> getUpdatedData() {
            return payload;
        }

        @Override
        public NormalizedNode<?, ?> getUpdatedSubtree() {
            return data;
        }
    }

    public static <V> Set<V> toIdentifierSet(final Iterable<? extends Identifiable<V>> children) {
        return FluentIterable.from(children).transform(StoreUtils.<V> identifierExtractor()).toSet();
    }

    public static String toStringTree(final NormalizedNode<?, ?> node) {
        StringBuilder builder = new StringBuilder();
        toStringTree(builder, node, 0);
        return builder.toString();
    }

    private static void toStringTree(final StringBuilder builder, final NormalizedNode<?, ?> node, final int offset) {
        final String prefix = Strings.repeat(" ", offset);

        builder.append(prefix).append(toStringTree(node.getIdentifier()));
        if (node instanceof NormalizedNodeContainer<?, ?, ?>) {
            final NormalizedNodeContainer<?, ?, ?> container = (NormalizedNodeContainer<?, ?, ?>) node;

            builder.append(" {\n");
            for (NormalizedNode<?, ?> child : container.getValue()) {
                toStringTree(builder, child, offset + STRINGTREE_INDENT);
            }

            builder.append(prefix).append('}');
        } else {
            builder.append(' ').append(node.getValue());
        }
        builder.append('\n');
    }

    private static String toStringTree(final PathArgument identifier) {
        if (identifier instanceof NodeIdentifierWithPredicates) {
            StringBuilder builder = new StringBuilder();
            builder.append(identifier.getNodeType().getLocalName());
            builder.append(((NodeIdentifierWithPredicates) identifier).getKeyValues().values());
            return builder.toString();
        } else if (identifier instanceof AugmentationIdentifier) {
            return "augmentation";
        }
        return identifier.getNodeType().getLocalName();
    }
}

@@ -382,226 +382,13 @@
 til.
-Collections;%0Aimport java.util.Map;%0Aimport java.util.Set;%0A%0Aimport org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;%0Aimport org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
+Set;%0A
 %0Aimp
@@ -1110,55 +1110,8 @@
 st;%0A
-import com.google.common.collect.ImmutableMap;%0A
 impo
@@ -1647,626 +1647,8 @@
  %7D%0A%0A
-    public static final UnsignedLong increase(final UnsignedLong original) %7B%0A        return original.plus(UnsignedLong.ONE);%0A    %7D%0A%0A    public static final InstanceIdentifier append(final InstanceIdentifier parent, final PathArgument arg) %7B%0A%0A        return new InstanceIdentifier(ImmutableList.%3CPathArgument%3E builder().addAll(parent.getPath()).add(arg).build());%0A    %7D%0A%0A    public static AsyncDataChangeEvent%3CInstanceIdentifier, NormalizedNode%3C?, ?%3E%3E initialChangeEvent(%0A            final InstanceIdentifier path, final StoreMetadataNode data) %7B%0A        return new InitialDataChangeEvent(path, data.getData());%0A    %7D%0A%0A
     
@@ -2036,38 +2036,37 @@
 ER;%0A    %7D%0A%0A    p
-rivate
+ublic
  static final cl
@@ -2067,266 +2067,116 @@
 nal 
-class InitialDataChangeEvent implements%0A            AsyncDataChangeEvent%3CInstanceIdentifier, NormalizedNode%3C?, ?%3E%3E %7B%0A%0A        private final ImmutableMap%3CInstanceIdentifier, NormalizedNode%3C?, ?%3E%3E payload;%0A        private final NormalizedNode%3C?, ?%3E data
+UnsignedLong increase(final UnsignedLong original) %7B%0A        return original.plus(UnsignedLong.ONE)
 ;%0A
-%0A
     
+%7D%0A%0A
     
@@ -2186,31 +2186,15 @@
 lic 
-InitialDataChangeEvent(
+static 
 fina
@@ -2218,278 +2218,70 @@
 ier 
-path, final NormalizedNode%3C?, ?%3E data) %7B%0A            payload = ImmutableMap.%3CInstanceIdentifier, NormalizedNode%3C?, ?%3E%3E of(path, data);%0A            this.data = data;%0A        %7D%0A%0A        @Override%0A        public Map%3CInstanceIdentifier, NormalizedNode%3C?, ?%3E%3E getCreatedData(
+append(final InstanceIdentifier parent, final PathArgument arg
 ) %7B%0A
@@ -2292,693 +2292,120 @@
     
-    
 return 
-payload;%0A        %7D%0A%0A        @Override%0A        public Map%3CInstanceIdentifier, ? extends NormalizedNode%3C?, ?%3E%3E getOriginalData() %7B%0A            return Collections.emptyMap();%0A        %7D%0A%0A        @Override%0A        public NormalizedNode%3C?, ?%3E getOriginalSubtree() %7B%0A            return null;%0A        %7D%0A%0A        @Override%0A        public Set%3CInstanceIdentifier%3E getRemoved
+new InstanceIdentifier(ImmutableList.%3CPathArgument%3E builder().addAll(parent.get
 Path
-s
 ()
- %7B%0A            return Collections.emptySet();%0A        %7D%0A%0A        @Override%0A        public Map%3CInstanceIdentifier, NormalizedNode%3C?, ?%3E%3E getUpdatedData() %7B%0A            return payload;%0A        %7D%0A%0A        @Override%0A        public NormalizedNode%3C?, ?%3E getUpdatedSubtree() %7B%0A            return data;%0A        %7D
+).add(arg).build());
 %0A   

remove StoreUtils.initialChangeEvent()