package com.bio4j.angulillos.neo4j;

import java.util.stream.Stream;
import java.util.Iterator;
import java.util.Optional;

import com.bio4j.angulillos.*;
import static com.bio4j.angulillos.conversions.*;

// Neo4j
import org.neo4j.graphdb.*;
import static org.neo4j.graphdb.Direction.*;

import org.neo4j.graphdb.Transaction;
// import org.neo4j.graphdb.schema.ConstraintDefinition;
// import org.neo4j.graphdb.schema.ConstraintCreator;


public class Neo4jUntypedGraph
implements
  UntypedGraph<Node, Relationship>
{
  private final GraphDatabaseService neo4jGraph;
  public  final GraphDatabaseService neo4jGraph() { return this.neo4jGraph; }

  public Neo4jUntypedGraph(GraphDatabaseService neo4jGraph) { this.neo4jGraph = neo4jGraph; }


  @Override
  public void commit() {}

  @Override
  public void shutdown() { neo4jGraph().shutdown(); }

  @Override
  public void rollback() {}

  private RelationshipType relType(AnyEdgeType edgeType) {
    return DynamicRelationshipType.withName(edgeType._label());
  }

  @Override
  public Relationship addEdge(Node from, AnyEdgeType edgeType, Node to) {

    return from.createRelationshipTo(to, relType(edgeType));
  }

  @Override
  public Node addVertex(AnyVertexType vertexType) {

    return neo4jGraph().createNode(DynamicLabel.label(vertexType._label()));
  }


  //////////////////////////////////////////////////////////////////////////////////

  @Override
  public <V> V getPropertyV(Node vertex, AnyProperty property) {

    @SuppressWarnings("unchecked")
    V value = (V) vertex.getProperty(property._label());
    return value;
  }

  @Override
  public <V> Node setPropertyV(Node vertex, AnyProperty property, V value) {

    vertex.setProperty(property._label(), value);
    return vertex;
  }

  @Override
  public <V> V getPropertyE(Relationship edge, AnyProperty property) {

    @SuppressWarnings("unchecked")
    V value = (V) edge.getProperty(property._label());
    return value;
  }

  @Override
  public <V> Relationship setPropertyE(Relationship edge, AnyProperty property, V value) {

    edge.setProperty(property._label(), value);
    return edge;
  }

  @Override
  public Node source(Relationship edge) {

    return edge.getStartNode();
  }

  @Override
  public Node target(Relationship edge) {

    return edge.getEndNode();
  }

  @Override
  public Stream<Relationship> outE(Node vertex, AnyEdgeType edgeType) {

    return stream( vertex.getRelationships(relType(edgeType), OUTGOING) );
  }

  @Override
  public Stream<Node> outV(Node vertex, AnyEdgeType edgeType) {

    return stream(
      vertex.getRelationships(relType(edgeType), OUTGOING)
    ).map( Relationship::getEndNode );
  }

  @Override
  public Stream<Relationship> inE(Node vertex, AnyEdgeType edgeType) {

    return stream( vertex.getRelationships(relType(edgeType), INCOMING) );
  }

  @Override
  public Stream<Node> inV(Node vertex, AnyEdgeType edgeType) {

    return stream (
      vertex.getRelationships(relType(edgeType), INCOMING)
    ).map( Relationship::getStartNode );
  }

  // <
  //   N extends TypedVertex<N,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType>,
  //   NT extends TypedVertex.Type<N,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType>,
  //   P extends Property<N,NT,P,V,G,I,Node,AnyVertexType,Relationship,AnyEdgeType>, V,
  //   G extends TypedGraph<G,I,Node,AnyVertexType,Relationship,AnyEdgeType>,
  //   I extends Neo4jUntypedGraph
  // > ConstraintCreator uniqueConstraintFor(P property) {
  //
  //   return neo4jGraph().schema().constraintFor(property.elementType().raw())
  //     .assertPropertyIsUnique(property.name());
  // }
  //
  // <
  //   N extends TypedVertex<N,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType>,
  //   NT extends TypedVertex.Type<N,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType>,
  //   P extends Property<N,NT,P,V,G,I,Node,AnyVertexType,Relationship,AnyEdgeType>, V,
  //   G extends TypedGraph<G,I,Node,AnyVertexType,Relationship,AnyEdgeType>,
  //   I extends Neo4jUntypedGraph
  // > ConstraintDefinition createOrGetUniqueConstraintFor(P property) {
  //
  //   return Optional.ofNullable (
  //     neo4jGraph().schema()
  //     .getConstraints( property.elementType().raw() )
  //     .iterator().next()
  //   ).orElseGet(
  //     () -> uniqueConstraintFor(property).create()
  //   );
  // }

}

@@ -286,159 +286,8 @@
 *;%0A%0A
-import org.neo4j.graphdb.Transaction;%0A// import org.neo4j.graphdb.schema.ConstraintDefinition;%0A// import org.neo4j.graphdb.schema.ConstraintCreator;%0A%0A%0A
 publ
@@ -617,24 +617,68 @@
 d commit() %7B
+ throw new UnsupportedOperationException(); 
 %7D%0A%0A  @Overri
@@ -773,16 +773,60 @@
 back() %7B
+ throw new UnsupportedOperationException(); 
 %7D%0A%0A  pri
@@ -2977,1300 +2977,6 @@
   %7D%0A
-%0A  // %3C%0A  //   N extends TypedVertex%3CN,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E,%0A  //   NT extends TypedVertex.Type%3CN,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E,%0A  //   P extends Property%3CN,NT,P,V,G,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E, V,%0A  //   G extends TypedGraph%3CG,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E,%0A  //   I extends Neo4jUntypedGraph%0A  // %3E ConstraintCreator uniqueConstraintFor(P property) %7B%0A  //%0A  //   return neo4jGraph().schema().constraintFor(property.elementType().raw())%0A  //     .assertPropertyIsUnique(property.name());%0A  // %7D%0A  //%0A  // %3C%0A  //   N extends TypedVertex%3CN,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E,%0A  //   NT extends TypedVertex.Type%3CN,NT,G,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E,%0A  //   P extends Property%3CN,NT,P,V,G,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E, V,%0A  //   G extends TypedGraph%3CG,I,Node,AnyVertexType,Relationship,AnyEdgeType%3E,%0A  //   I extends Neo4jUntypedGraph%0A  // %3E ConstraintDefinition createOrGetUniqueConstraintFor(P property) %7B%0A  //%0A  //   return Optional.ofNullable (%0A  //     neo4jGraph().schema()%0A  //     .getConstraints( property.elementType().raw() )%0A  //     .iterator().next()%0A  //   ).orElseGet(%0A  //     () -%3E uniqueConstraintFor(property).create()%0A  //   );%0A  // %7D%0A%0A
 %7D%0A

make unimplementable methods throw unsupported operation