/*
 * 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 static com.google.common.base.Preconditions.checkState;

import java.util.Map.Entry;

import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
import org.opendaylight.controller.md.sal.dom.store.impl.tree.TreeNodeUtils;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;

class MutableDataTree {

    private static final Logger log = LoggerFactory.getLogger(MutableDataTree.class);

    final DataAndMetadataSnapshot snapshot;
    final NodeModification rootModification;
    final ModificationApplyOperation strategyTree;

    private boolean sealed = false;

    private MutableDataTree(final DataAndMetadataSnapshot snapshot, final ModificationApplyOperation strategyTree) {
        this.snapshot = snapshot;
        this.strategyTree = strategyTree;
        this.rootModification = NodeModification.createUnmodified(snapshot.getMetadataTree());
    }

    public void write(final InstanceIdentifier path, final NormalizedNode<?, ?> value) {
        checkSealed();
        resolveModificationFor(path).write(value);
    }

    public void delete(final InstanceIdentifier path) {
        checkSealed();
        resolveModificationFor(path).delete();
    }

    public Optional<NormalizedNode<?, ?>> read(final InstanceIdentifier path) {
        Entry<InstanceIdentifier, NodeModification> modification = TreeNodeUtils.findClosestsOrFirstMatch(rootModification, path, NodeModification.IS_TERMINAL_PREDICATE);

        Optional<StoreMetadataNode> result = resolveSnapshot(modification);
        if (result.isPresent()) {
            NormalizedNode<?, ?> data = result.get().getData();
            return NormalizedNodeUtils.findNode(modification.getKey(), data, path);
        }
        return Optional.absent();

    }

    private Optional<StoreMetadataNode> resolveSnapshot(
            final Entry<InstanceIdentifier, NodeModification> keyModification) {
        InstanceIdentifier path = keyModification.getKey();
        NodeModification modification = keyModification.getValue();
        return resolveSnapshot(path, modification);
    }

    private Optional<StoreMetadataNode> resolveSnapshot(final InstanceIdentifier path,
            final NodeModification modification) {
        try {
            Optional<Optional<StoreMetadataNode>> potentialSnapshot = modification.getSnapshotCache();
            if(potentialSnapshot.isPresent()) {
                return potentialSnapshot.get();
            }
            return resolveModificationStrategy(path).apply(modification, modification.getOriginal(),
                    StoreUtils.increase(snapshot.getMetadataTree().getSubtreeVersion()));
        } catch (Exception e) {
            log.error("Could not create snapshot for {}", path,e);
            throw e;
        }
    }

    private ModificationApplyOperation resolveModificationStrategy(final InstanceIdentifier path) {
        log.trace("Resolving modification apply strategy for {}", path);
        return TreeNodeUtils.findNodeChecked(strategyTree, path);
    }

    private OperationWithModification resolveModificationFor(final InstanceIdentifier path) {
        NodeModification modification = rootModification;
        // We ensure strategy is present.
        ModificationApplyOperation operation = resolveModificationStrategy(path);
        for (PathArgument pathArg : path.getPath()) {
            modification = modification.modifyChild(pathArg);
        }
        return OperationWithModification.from(operation, modification);
    }

    public static MutableDataTree from(final DataAndMetadataSnapshot snapshot, final ModificationApplyOperation resolver) {
        return new MutableDataTree(snapshot, resolver);
    }

    public void seal() {
        sealed = true;
        rootModification.seal();
    }

    private void checkSealed() {
        checkState(!sealed, "Data Tree is sealed. No further modifications allowed.");
    }

    protected NodeModification getRootModification() {
        return rootModification;
    }

    @Override
    public String toString() {
        return "MutableDataTree [modification=" + rootModification + "]";
    }


}

@@ -452,16 +452,66 @@
 p.Entry;
+%0Aimport java.util.concurrent.atomic.AtomicBoolean;
 %0A%0Aimport
@@ -1137,16 +1137,177 @@
 tional;%0A
+import com.google.common.base.Preconditions;%0A%0A/*%0A * FIXME: the thread safety of concurrent write/delete/read/seal operations%0A *        needs to be evaluated.%0A */
 %0Aclass M
@@ -1323,17 +1323,16 @@
 aTree %7B%0A
-%0A
     priv
@@ -1355,19 +1355,19 @@
  Logger 
-log
+LOG
  = Logge
@@ -1413,101 +1413,81 @@
 s);%0A
-%0A    final DataAndMetadataSnapshot snapshot;%0A    final NodeModification rootModification;%0A   
+    private final AtomicBoolean sealed = new AtomicBoolean();%0A    private
  fin
@@ -1530,17 +1530,16 @@
 gyTree;%0A
-%0A
     priv
@@ -1546,30 +1546,99 @@
 ate 
-boolean sealed = false
+final DataAndMetadataSnapshot snapshot;%0A    private final NodeModification rootModification
 ;%0A%0A 
@@ -2788,17 +2788,16 @@
 sent();%0A
-%0A
     %7D%0A%0A 
@@ -3720,19 +3720,19 @@
         
-log
+LOG
 .error(%22
@@ -3925,11 +3925,11 @@
     
-log
+LOG
 .tra
@@ -4764,21 +4764,158 @@
     
-sealed = true
+final boolean success = sealed.compareAndSet(false, true);%0A        Preconditions.checkState(success, %22Attempted to seal an already-sealed Data Tree.%22)
 ;%0A  
@@ -5011,16 +5011,22 @@
 (!sealed
+.get()
 , %22Data 
@@ -5305,12 +5305,10 @@
 ;%0A    %7D%0A
-%0A%0A
 %7D%0A

improve safety of MutableDataTree