/*
 * 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.tree;

import static com.google.common.base.Preconditions.checkState;

import java.util.Map;

import org.opendaylight.yangtools.concepts.Identifiable;
import org.opendaylight.yangtools.concepts.Immutable;
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.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.UnsignedLong;

public class StoreMetadataNode implements Immutable, Identifiable<PathArgument>, StoreTreeNode<StoreMetadataNode> {

    private final UnsignedLong nodeVersion;
    private final UnsignedLong subtreeVersion;
    private final NormalizedNode<?, ?> data;

    private final Map<PathArgument, StoreMetadataNode> children;

    protected StoreMetadataNode(final NormalizedNode<?, ?> data, final UnsignedLong nodeVersion,
            final UnsignedLong subtreeVersion, final Map<PathArgument, StoreMetadataNode> children) {
        this.nodeVersion = nodeVersion;
        this.subtreeVersion = subtreeVersion;
        this.data = data;
        this.children = ImmutableMap.copyOf(children);

    }

    public static Builder builder() {
        return new Builder();
    }

    public UnsignedLong getNodeVersion() {
        return this.nodeVersion;
    }

    @Override
    public PathArgument getIdentifier() {
        return data.getIdentifier();
    }

    public UnsignedLong getSubtreeVersion() {
        return subtreeVersion;
    }

    public NormalizedNode<?, ?> getData() {
        return this.data;
    }

    public Iterable<StoreMetadataNode> getChildren() {
        return children.values();
    }

    @Override
    public Optional<StoreMetadataNode> getChild(final PathArgument key) {
        return Optional.fromNullable(children.get(key));
    }

    @Override
    public String toString() {
        return "StoreMetadataNode [identifier=" + getIdentifier() + ", nodeVersion=" + nodeVersion + "]";
    }

    public static Optional<UnsignedLong> getVersion(final Optional<StoreMetadataNode> currentMetadata) {
        if (currentMetadata.isPresent()) {
            return Optional.of(currentMetadata.get().getNodeVersion());
        }
        return Optional.absent();
    }

    public static Optional<StoreMetadataNode> getChild(final Optional<StoreMetadataNode> parent,
            final PathArgument child) {
        if (parent.isPresent()) {
            return parent.get().getChild(child);
        }
        return Optional.absent();
    }

    public static final StoreMetadataNode createRecursivelly(final NormalizedNode<?, ?> node,
            final UnsignedLong nodeVersion, final UnsignedLong subtreeVersion) {
        Builder builder = builder() //
                .setNodeVersion(nodeVersion) //
                .setSubtreeVersion(subtreeVersion) //
                .setData(node);
        if (node instanceof NormalizedNodeContainer<?, ?, ?>) {

            @SuppressWarnings("unchecked")
            NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> nodeContainer = (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) node;
            for (NormalizedNode<?, ?> subNode : nodeContainer.getValue()) {
                builder.add(createRecursivelly(subNode, nodeVersion, subtreeVersion));
            }
        }
        return builder.build();
    }

    public static class Builder {

        private UnsignedLong nodeVersion;
        private UnsignedLong subtreeVersion;
        private NormalizedNode<?, ?> data;
        private final ImmutableMap.Builder<PathArgument, StoreMetadataNode> children = ImmutableMap.builder();

        private Builder() {}


        public UnsignedLong getVersion() {
            return nodeVersion;

        }

        public Builder setNodeVersion(final UnsignedLong version) {
            this.nodeVersion = version;
            return this;
        }

        public Builder setSubtreeVersion(final UnsignedLong version) {
            this.subtreeVersion = version;
            return this;
        }

        public Builder setData(final NormalizedNode<?, ?> data) {
            this.data = data;
            return this;
        }

        public Builder add(final StoreMetadataNode node) {
            children.put(node.getIdentifier(), node);
            return this;
        }

        public StoreMetadataNode build() {
            checkState(data != null, "Data node should not be null.");
            checkState(subtreeVersion.compareTo(nodeVersion) >= 0,
                    "Subtree version must be equals or greater than node version.");
            return new StoreMetadataNode(data, nodeVersion, subtreeVersion, children.build());
        }
    }

    public static StoreMetadataNode createRecursivelly(final NormalizedNode<?, ?> node, final UnsignedLong version) {
        return createRecursivelly(node, version, version);
    }

}

@@ -426,24 +426,56 @@
 heckState;%0A%0A
+import java.util.LinkedHashMap;%0A
 import java.
@@ -484,16 +484,16 @@
 il.Map;%0A
-
 %0Aimport 
@@ -869,16 +869,61 @@
 tional;%0A
+import com.google.common.base.Preconditions;%0A
 import c
@@ -948,27 +948,24 @@
 ollect.I
-mmut
+ter
 able
-Map
+s
 ;%0Aimport
@@ -1325,24 +1325,193 @@
  children;%0A%0A
+    /**%0A     *%0A     * @param data%0A     * @param nodeVersion%0A     * @param subtreeVersion%0A     * @param children Map of children, must not be modified externally%0A     */%0A
     protecte
@@ -1837,27 +1837,34 @@
 n = 
-ImmutableMap.copyOf
+Preconditions.checkNotNull
 (chi
@@ -1871,17 +1871,16 @@
 ldren);%0A
-%0A
     %7D%0A%0A 
@@ -2367,16 +2367,47 @@
  return 
+Iterables.unmodifiableIterable(
 children
@@ -2415,16 +2415,17 @@
 values()
+)
 ;%0A    %7D%0A
@@ -4284,34 +4284,11 @@
 ate 
-final ImmutableMap.Builder
+Map
 %3CPat
@@ -4332,30 +4332,68 @@
 n = 
-ImmutableMap.builder()
+new LinkedHashMap%3C%3E();%0A        private boolean dirty = false
 ;%0A%0A 
@@ -4990,24 +4990,152 @@
 ode node) %7B%0A
+            if (dirty) %7B%0A                children = new LinkedHashMap%3C%3E(children);%0A                dirty = false;%0A            %7D%0A
             
@@ -5474,24 +5474,50 @@
 version.%22);%0A
+            dirty = true;%0A
             
@@ -5588,24 +5588,16 @@
 children
-.build()
 );%0A     

Remove ImmutableMap in favor of LinkedHashMap