package org.javersion.object;

import static com.google.common.collect.ImmutableSet.of;
import static java.util.Arrays.asList;

import java.util.Set;

import org.javersion.core.Merge;
import org.javersion.core.Revision;
import org.javersion.core.Version;
import org.javersion.core.VersionGraph;
import org.javersion.core.VersionNode;
import org.javersion.object.types.ValueType;
import org.javersion.path.PropertyPath;
import org.javersion.path.Schema;

public class ObjectVersionManager<O, M> {

    private VersionGraph<PropertyPath, Object, M, ?, ?> versionGraph;

    private Set<Revision> heads;

    private final ObjectSerializer<O> serializer;

    final boolean useSchemaFilter;

    public ObjectVersionManager(Class<O> clazz) {
        this(new ObjectSerializer<>(clazz), false);
    }

    public ObjectVersionManager(ObjectSerializer<O> serializer, boolean useSchemaFilter) {
        this.serializer = serializer;
        this.useSchemaFilter = useSchemaFilter;
    }

    public ObjectVersionManager<O, M> init() {
        this.versionGraph = ObjectVersionGraph.init();
        return this;
    }

    public ObjectVersionManager<O, M> init(Iterable<ObjectVersion<M>> versions) {
        return init(ObjectVersionGraph.init(versions));
    }

    public ObjectVersionManager<O, M> init(VersionGraph<PropertyPath, Object, M, ?, ?> versionGraph) {
        this.versionGraph = versionGraph;
        return this;
    }

    public ManagedObjectVersionBuilder<M> versionBuilder(O object) {
        ManagedObjectVersionBuilder<M> builder = new ManagedObjectVersionBuilder<M>(this, serializer.toPropertyMap(object));
        // TODO: Set Revision for known node
        builder.parents(heads);
        return builder;
    }

    public MergeObject<O, M> mergeRevisions(Revision... revisions) {
        return mergeRevisions(asList(revisions));
    }

    public MergeObject<O, M> mergeRevisions(Iterable<Revision> revisions) {
        return mergeObject(versionGraph.mergeRevisions(revisions));
    }

    public MergeObject<O, M> mergeBranches(String... branches) {
        return mergeBranches(asList(branches));
    }

    public MergeObject<O, M> mergeBranches(Iterable<String> branches) {
        return mergeObject(versionGraph.mergeBranches(branches));
    }

    private MergeObject<O, M> mergeObject(Merge<PropertyPath, Object, M> merge) {
        MergeObject<O, M> mergeObject = new MergeObject<>(toObject(merge), merge);
        heads = merge.getMergeHeads();
        return mergeObject;
    }

    private O toObject(Merge<PropertyPath, Object, M> merge) {
        return serializer.fromPropertyMap(merge.getProperties());
    }

    public void commit(Version<PropertyPath, Object, M> version) {
        versionGraph = versionGraph.commit(version);
        heads = of(version.revision);
    }

    public VersionGraph<PropertyPath, Object, M, ?, ?> getVersionGraph() {
        return versionGraph;
    }

    public Set<Revision> getHeads() {
        return heads;
    }

    public VersionNode<PropertyPath, Object, M> getVersionNode(Revision revision) {
        return versionGraph.getVersionNode(revision);
    }

    public Set<String> getBranches() {
        return versionGraph.getBranches();
    }

    public Schema<ValueType> getSchema() {
        return serializer.schemaRoot;
    }
}

@@ -120,16 +120,102 @@
 asList;%0A
+import static org.javersion.core.Version.DEFAULT_BRANCH;%0A%0Aimport java.util.Collection;
 %0Aimport 
@@ -1477,24 +1477,46 @@
 rsionGraph;%0A
+        heads = null;%0A
         retu
@@ -1729,53 +1729,8 @@
 ));%0A
-        // TODO: Set Revision for known node%0A
     
@@ -2228,24 +2228,26 @@
 ranches(
-Iterable
+Collection
 %3CString%3E
@@ -2251,32 +2251,152 @@
 ng%3E branches) %7B%0A
+        if (branches.isEmpty()) %7B%0A            return mergeObject(versionGraph.mergeBranches(DEFAULT_BRANCH));%0A        %7D%0A
         return m

Clear heads on init and use default branch in merge if nothing else is supplied