package org.eclipse.hawk.sqlite.iteration;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.eclipse.hawk.core.graph.IGraphIterable;
import org.eclipse.hawk.core.graph.IGraphNode;
import org.eclipse.hawk.sqlite.SQLiteDatabase;
import org.eclipse.hawk.sqlite.SQLiteNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StatementGraphNodeIterable implements IGraphIterable<IGraphNode> {
	private static final Logger LOGGER = LoggerFactory.getLogger(StatementGraphNodeIterable.class);

	private final SQLiteDatabase db;
	private final StatementSupplier stmtIterator, stmtSize, stmtSingle;
	
	public StatementGraphNodeIterable(SQLiteDatabase db, StatementSupplier stmtIterator, StatementSupplier stmtSize, StatementSupplier stmtSingle) {
		this.db = db;
		this.stmtIterator = stmtIterator;
		this.stmtSize = stmtSize;
		this.stmtSingle = stmtSingle;
	}

	@Override
	public Iterator<IGraphNode> iterator() {
		try {
			PreparedStatement stmt = stmtIterator.get();
			stmt.execute();
			return new ResultSetIterator<>(stmt.getResultSet(), this::getNodeFromResultSet);
		} catch (SQLException e) {
			LOGGER.error(e.getMessage(), e);
			return Collections.emptyIterator();
		}
	}

	@Override
	public int size() {
		try {
			PreparedStatement stmt = stmtSize.get();
			stmt.execute();
			try (ResultSet rs = stmt.getResultSet()) {
				rs.next();
				return rs.getInt(1);
			}
		} catch (SQLException e) {
			LOGGER.error(e.getMessage(), e);
			return 0;
		}
	}

	@Override
	public IGraphNode getSingle() {
		try {
			PreparedStatement stmt = stmtSingle.get();
			stmt.execute();
			try (ResultSet rs = stmt.getResultSet()) {
				if (rs.next()) {
					return new SQLiteNode(db, rs.getInt(1));
				}
			}
		} catch (SQLException e) {
			LOGGER.error(e.getMessage(), e);
		}
		throw new NoSuchElementException();
	}

	private SQLiteNode getNodeFromResultSet(ResultSet rs) throws SQLException {
		final int nodeId = rs.getInt(1);
		return new SQLiteNode(db, nodeId);
	}
}