package managers.nodes;

import com.fasterxml.jackson.databind.JsonNode;
import java.util.List;
import managers.BaseManager;
import managers.functions.JsonFunction;
import managers.functions.SuccessFunction;
import models.nodes.Node;
import neo4play.NodeService;
import play.libs.F.Function;
import play.libs.F.Promise;
import play.libs.WS;


public abstract class NodeManager extends BaseManager {

    protected Promise<Boolean> exists(String label, JsonNode properties) {
        Promise<JsonNode> json = get(label, properties);
        return json.map(
            new Function<JsonNode, Boolean>() {
                public Boolean apply(JsonNode json) {
                    return json.get("data").size() > 0;
                }
            });
    }

    protected Promise<List<JsonNode>> all(String label) {
        Promise<WS.Response> response = NodeService.getNodes(label);
        return response.map(
            new Function<WS.Response, List<JsonNode>>() {
                public List<JsonNode> apply(WS.Response response) {
                    JsonNode json = response.asJson();
                    return json.get("data").findValues("data");
                }
            });
    }

    protected Promise<JsonNode> get(String label, JsonNode properties) {
        Promise<WS.Response> response = NodeService
            .getNode(label, properties);
        return response.map(new JsonFunction()); // JsonFunction should return(something like) json.get("data"), cf. `Function` for `all` above
    }

    public Promise<Boolean> create(final JsonNode properties) {
        Promise<String> location = beginTransaction();
        Promise<Boolean> created = location.flatMap(
            new Function<String, Promise<Boolean>>() {
                public Promise<Boolean> apply(final String location) {
                    Promise<Boolean> created = create(properties, location);
                    return created.flatMap(
                        new Function<Boolean, Promise<Boolean>>() {
                            public Promise<Boolean> apply(Boolean created) {
                                if (created) {
                                    return commitTransaction(location);
                                }
                                return Promise.pure(false);
                            }
                        });
                }
            });
        return created;
    };

    protected Promise<Boolean> update(
        String label, JsonNode oldProperties, JsonNode newProperties) {
        Promise<WS.Response> response = NodeService.updateNode(
            label, oldProperties, newProperties);
        return response.map(new SuccessFunction());
    }

    protected Promise<Boolean> update(
        String label, JsonNode oldProperties, JsonNode newProperties,
        String location) {
        Promise<WS.Response> response = NodeService.updateNode(
            label, oldProperties, newProperties, location);
        return response.map(new SuccessFunction());
    }

    public Promise<Boolean> delete(final JsonNode properties) {
        Promise<String> location = beginTransaction();
        Promise<Boolean> deleted = location.flatMap(
            new Function<String, Promise<Boolean>>() {
                public Promise<Boolean> apply(final String location) {
                    Promise<Boolean> deleted = delete(properties, location);
                    return deleted.flatMap(
                        new Function<Boolean, Promise<Boolean>>() {
                            public Promise<Boolean> apply(Boolean deleted) {
                                if (deleted) {
                                    return commitTransaction(location);
                                }
                                return Promise.pure(false);
                            }
                        });
                }
            });
        return deleted;
    };

    public Promise<Boolean> connect(
        final JsonNode startNode, final JsonNode endNode) {
        Promise<String> location = beginTransaction();
        Promise<Boolean> connected = location.flatMap(
            new Function<String, Promise<Boolean>>() {
                public Promise<Boolean> apply(final String location) {
                    Promise<Boolean> connected =
                        connect(startNode, endNode, location);
                    return connected.flatMap(
                        new Function<Boolean, Promise<Boolean>>() {
                            public Promise<Boolean> apply(Boolean connected) {
                                if (connected) {
                                    return commitTransaction(location);
                                }
                                return Promise.pure(false);
                            }
                        });
                }
            });
        return connected;
    }

    public Promise<Boolean> disconnect(
        final JsonNode startNode, final JsonNode endNode) {
        Promise<String> location = beginTransaction();
        Promise<Boolean> disconnected = location.flatMap(
            new Function<String, Promise<Boolean>>() {
                public Promise<Boolean> apply(final String location) {
                    Promise<Boolean> disconnected =
                        disconnect(startNode, endNode, location);
                    return disconnected.flatMap(
                        new Function<Boolean, Promise<Boolean>>() {
                            public Promise<Boolean> apply(
                                Boolean disconnected) {
                                if (disconnected) {
                                    return commitTransaction(location);
                                }
                                return Promise.pure(false);
                            }
                        });
                }
            });
        return disconnected;
    }

    public abstract Promise<Boolean> exists(JsonNode properties);

    public abstract Promise<? extends List<? extends Node>> all();

    public abstract Promise<? extends Node> get(JsonNode properties);

    protected abstract Promise<Boolean> create(
        JsonNode properties, String location);

    public abstract Promise<Boolean> update(
        JsonNode oldProperties, JsonNode newProperties);

    protected abstract Promise<Boolean> delete(
        JsonNode properties, String location);

    protected abstract Promise<Boolean> connect(
        JsonNode startNode, JsonNode endNode, String location);

    protected abstract Promise<Boolean> disconnect(
        JsonNode startNode, JsonNode endNode, String location);

}

@@ -393,16 +393,29 @@
 ager %7B%0A%0A
+    // READ%0A%0A
     prot
@@ -473,32 +473,32 @@
 e properties) %7B%0A
-
         Promise%3C
@@ -1513,32 +1513,47 @@
 l%60 above%0A    %7D%0A%0A
+    // CREATE%0A%0A
     public Promi
@@ -2433,24 +2433,39 @@
 ed;%0A    %7D;%0A%0A
+    // UPDATE%0A%0A
     protecte
@@ -3047,32 +3047,47 @@
 tion());%0A    %7D%0A%0A
+    // DELETE%0A%0A
     public Promi
@@ -3967,24 +3967,59 @@
 ed;%0A    %7D;%0A%0A
+    // Connections to other nodes%0A%0A
     public P
@@ -6004,39 +6004,40 @@
 n disconnected;%0A
-
     %7D%0A%0A
+%0A
     public abstr

Enable operations that write to the DB to be rolled back on error by wrapping them in transactions.