package ddg.DefUseCFG;

import java.util.Collection;
import java.util.List;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.IndexHits;

import databaseNodes.EdgeTypes;
import misc.Pair;
import neo4j.readWriteDB.Neo4JDBInterface;
import neo4j.traversals.readWriteDB.Traversals;

public class ReadWriteDbFactory extends DefUseCFGFactory
{

	DefUseCFG cfg;
	List<String> parameters;

	@Override
	public DefUseCFG create(Long funcId)
	{
		cfg = new DefUseCFG();

		getStatementsOfFunction(funcId);
		getUsesAndDefs();
		getParentBlocks();
		getChildBlocks();
		getExitNode(funcId);
		getParameters(funcId);
		cfg.addUsesForExitNode();

		return cfg;
	}

	private void getParameters(Long funcId)
	{
		String query = "type:Parameter AND functionId:" + funcId;
		IndexHits<Node> hits = Neo4JDBInterface.queryIndex(query);
		for (Node node : hits)
		{
			Collection<Object> params = cfg.getSymbolsDefinedBy(node.getId());
			for (Object o : params)
			{
				cfg.addParameter((String) o);
			}
		}

	}

	private void getExitNode(Long funcId)
	{
		String query = "type:CFGExitNode AND functionId:" + funcId;
		IndexHits<Node> hits = Neo4JDBInterface.queryIndex(query);
		for (Node node : hits)
		{
			cfg.setExitNode(node.getId());
		}

	}

	private void getStatementsOfFunction(Long funcId)
	{
		String query = "isCFGNode:True AND functionId:" + funcId;
		IndexHits<Node> hits = Neo4JDBInterface.queryIndex(query);
		for (Node node : hits)
			cfg.addStatement(node.getId());
	}

	private void getUsesAndDefs()
	{
		for (Object obj : cfg.getStatements())
		{
			Long statementId = (Long) obj;

			List<Pair<Long, String>> used = Traversals
					.getSymbolsUsedByStatement(statementId);
			for (Pair<Long, String> symbolIdAndCode : used)
			{
				Long symbolId = symbolIdAndCode.getL();
				String symbolCode = symbolIdAndCode.getR();
				cfg.addSymbolUsed(statementId, symbolCode);
				cfg.setSetSymbolId(symbolCode, symbolId);
			}

			List<Pair<Long, String>> defined = Traversals
					.getSymbolsDefinedByStatement(statementId);
			for (Pair<Long, String> symbolIdAndCode : defined)
			{
				Long symbolId = symbolIdAndCode.getL();
				String symbolCode = symbolIdAndCode.getR();
				cfg.addSymbolDefined(statementId, symbolCode);
				cfg.setSetSymbolId(symbolCode, symbolId);
			}
		}
	}

	private void getParentBlocks()
	{
		for (Object obj : cfg.getStatements())
		{
			Long statementId = (Long) obj;

			Node statement = Neo4JDBInterface.getNodeById(statementId);
			Iterable<Relationship> rels = statement
					.getRelationships(Direction.INCOMING);
			for (Relationship rel : rels)
			{
				if (!rel.getType().name().toString().equals(EdgeTypes.FLOWS_TO))
					continue;
				long parentId = rel.getStartNode().getId();
				cfg.addParentBlock(statementId, parentId);
			}
		}
	}

	private void getChildBlocks()
	{
		for (Object obj : cfg.getStatements())
		{
			Long statementId = (Long) obj;

			Node statement = Neo4JDBInterface.getNodeById(statementId);
			Iterable<Relationship> rels = statement
					.getRelationships(Direction.OUTGOING);
			for (Relationship rel : rels)
			{
				if (!rel.getType().name().toString().equals(EdgeTypes.FLOWS_TO))
					continue;

				long childId = rel.getEndNode().getId();
				cfg.addChildBlock(statementId, childId);
			}
		}
	}

}
