package org.eclipse.hawk.sqlite;

import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;

import org.eclipse.hawk.core.graph.IGraphEdge;
import org.eclipse.hawk.core.graph.IGraphNode;
import org.eclipse.hawk.sqlite.SQLiteDatabase.SQLiteConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SQLiteEdge implements IGraphEdge {

	private static final Logger LOGGER = LoggerFactory.getLogger(SQLiteEdge.class);
	private final SQLiteDatabase db;
	private final int id, from, to;
	private final String type;

	public SQLiteEdge(SQLiteDatabase db, int edgeId, int from, int to, String type) {
		this.db = db;
		this.id = edgeId;
		this.type = type;

		this.from = from;
		this.to = to;
	}

	@Override
	public Object getId() {
		return id;
	}

	@Override
	public String getType() {
		return type;
	}

	@Override
	public Set<String> getPropertyKeys() {
		try {
			final SQLiteConnection conn = db.getConnection();
			final PreparedStatement stmt = conn.getEdgePropKeysStatement(id);
			stmt.execute();
			return conn.getStrings(stmt);
		} catch (SQLException e) {
			LOGGER.error(e.getMessage(), e);
			return Collections.emptySet();
		}
	}

	@Override
	public Object getProperty(String name) {
		try {
			final SQLiteConnection conn = db.getConnection();
			PreparedStatement stmt = conn.getEdgePropValueStatement(id, name);
			stmt.execute();
			return conn.getPropertyValue(stmt);
		} catch (SQLException | ClassNotFoundException | IOException e) {
			LOGGER.error(e.getMessage(), e);
		}
		
		return null;
	}

	@Override
	public void setProperty(String name, Object value) {
		try {
			final SQLiteConnection conn = db.getConnection();
			value = conn.preprocessPropertyValue(value);
			PreparedStatement stmt = conn.getUpsertEdgePropStatement(id, name, value);
			int rowCount = stmt.executeUpdate();
			assert rowCount == 1 : "One row should have been inserted/updated when setting the property";
		} catch (SQLException | IOException e) {
			LOGGER.error(e.getMessage(), e);
		}
	}

	@Override
	public IGraphNode getStartNode() {
		return new SQLiteNode(db, from);
	}

	@Override
	public IGraphNode getEndNode() {
		return new SQLiteNode(db, to);
	}

	@Override
	public void delete() {
		try {
			final SQLiteConnection conn = db.getConnection();
			conn.getDeleteEdgePropertiesStatement(id).executeUpdate();
			conn.getDeleteEdgeStatement(id).executeUpdate();
		} catch (SQLException e) {
			LOGGER.error(e.getMessage(), e);
		}
	}

	@Override
	public void removeProperty(String name) {
		try {
			PreparedStatement stmt = db.getConnection().getDeleteEdgePropStatement(id, name);
			stmt.executeUpdate();
		} catch (SQLException e) {
			LOGGER.error(e.getMessage(), e);
		}
	}

	@Override
	public int hashCode() {
		return Objects.hash(id);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		SQLiteEdge other = (SQLiteEdge) obj;
		return id == other.id;
	}

	@Override
	public String toString() {
		return String.format("SQLiteEdge [id=%s]", id);
	}
	
}
