package eu.dnetlib.r2d2.neo4j.dao;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.ReturnableEvaluator;
import org.neo4j.graphdb.StopEvaluator;
import org.neo4j.graphdb.TraversalPosition;
import org.neo4j.graphdb.Traverser;
import org.neo4j.graphdb.Traverser.Order;

import com.google.common.collect.Iterables;

import eu.dnetlib.r2d2.neo4j.Neo4jBean;
import eu.dnetlib.r2d2.neo4j.domain.Neo4jItem;
import eu.dnetlib.r2d2.neo4j.domain.Relationships;
import eu.dnetlib.r2d2.neo4j.util.DepthStopEvaluator;

public class Neo4jItemDao extends Neo4JDao<Neo4jItem> implements ItemDao {

	public Neo4jItemDao() {
		super(Relationships._ITEMS, Relationships._ITEM, Neo4jItem.class, new ItemExterminator());
	}

	@Override
	protected Neo4jItem createBean() {
		return new Neo4jItem();
	}

	@Override
	public Neo4jItem getItemForEntry(String entryId) {
		Node entryNode = this.getNode(entryId);
		
		@SuppressWarnings("deprecation")
		Traverser tr = entryNode.traverse(
				Order.DEPTH_FIRST, StopEvaluator.DEPTH_ONE, 
				new ReturnableEvaluator() {

					public boolean isReturnableNode(TraversalPosition pos) {
						if (pos.depth() == 1) {
							Node n = pos.currentNode();
							String type = (String) n.getProperty(Neo4jBean.PREFIX, null);
							
							return (type != null && type.equals(Neo4jItem.class.getName()));
						}
						
						return false;
					}
				},
				Relationships.IO_ENTRY, Direction.INCOMING);
		
		return Iterables.getOnlyElement(new BeanIterable(tr), null);
	}

	@Override
	public void setItemForEntry(String itemId, String entryId) {
		Neo4jItem item = this.getItemForEntry(entryId);
		
		if (item != null) {
			this.removeRelationship(itemId, entryId, Relationships.IO_ENTRY);
		}
		
		if (itemId != null)
			this.createRelationship(itemId, entryId, Relationships.IO_ENTRY);
	}
	
	@Override
	public Iterable<Neo4jItem> getItemsInReadingList(String rlId) {
		Node rlNode = this.getNode(rlId);
		
		@SuppressWarnings("deprecation")
		Traverser tr = rlNode.traverse(
				Order.DEPTH_FIRST, new DepthStopEvaluator(2),
				new ReturnableEvaluator() {
					public boolean isReturnableNode(TraversalPosition pos) {
						
						if (pos.depth() > 0) {
							Node node = pos.currentNode();
	
							String type = (String) node.getProperty(Neo4jBean.PREFIX, null);
							
							return type.equals(Neo4jItem.class.getName());
						}
						
						return false;
					}
				},
				Relationships.ENTRY, Direction.INCOMING, 
				Relationships.IO_ENTRY, Direction.INCOMING);
		
		return new BeanIterable(tr);
	}
}

class ItemExterminator extends ExterminationPolicy {

	@Override
	public boolean encounteredRelation(Node thisNode, Node otherNode, 
			Direction direction, Relationship rel) {
		
		if (rel.getType().equals(Relationships.IO_ENTRY))
			return true;
		
		return false;
	}

	@Override
	public String getName() {
		return "Item";
	}
}