package eu.dnetlib.enabling.database;

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import eu.dnetlib.enabling.database.utils.DatabaseUtils;
import eu.dnetlib.enabling.resultset.factory.ResultSetFactory;
import eu.dnetlib.enabling.tools.AbstractBaseService;
import eu.dnetlib.rmi.common.ResultSet;
import eu.dnetlib.rmi.data.DatabaseException;
import eu.dnetlib.rmi.data.DatabaseService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;

public class DatabaseServiceImpl extends AbstractBaseService implements DatabaseService {

	private static final Log log = LogFactory.getLog(DatabaseServiceImpl.class); // NOPMD by marko on 11/24/08 5:02 PM

	private DatabaseServiceCore core;

	@Autowired
	private DatabaseUtils databaseUtils;

	private ResultSetFactory resultsetFactory;
	
	private DatabaseBlackBoardNotificationHandler blackBoardNotificationHandler; 

	private ExecutorService threadPool = Executors.newCachedThreadPool();

	@Override
	public ResultSet<String> dumpTable(final String db, final String table) throws DatabaseException {
		try {
			return core.generateResultSet(db, table, null);
		} catch (Throwable e) {
			throw new DatabaseException(e);
		}
	}

	@Override
	public ResultSet<String> dumpTableAndLogs(final String db, final String table, final Date from, final Date until) throws DatabaseException {
		try {
			return core.generateResultSet(db, table, from, until);
		} catch (Throwable e) {
			throw new DatabaseException(e);
		}
	}

	@Override
	public void notify(final String subscriptionId, final String topic, final String isId, final String message) {
		log.info("Received notification " + subscriptionId + ", TOPIC: " + topic);
		
		if (topic.contains("BLACKBOARD")) {
			blackBoardNotificationHandler.notified(subscriptionId, topic, isId, message);
			return;
		}
	}

	@Override
	public void importFromEPR(final String db, final ResultSet<String> rs, final String xslt) throws DatabaseException {

		final ResultSet<String> mappedEpr = (xslt == null || xslt.isEmpty()) ? rs : resultsetFactory.xsltMap(rs, xslt);

		try {
			threadPool.execute(() -> {
				try {
					core.importFromResultset(db, mappedEpr);
				} catch (final Exception e) {
					log.error("Error in thread when importing from epr", e);
					throw new RuntimeException(e);
				}
			});
		} catch (RuntimeException e) {
			throw new DatabaseException(e);
		}
	}

	@Required
	public void setCore(final DatabaseServiceCore core) {
		this.core = core;
	}

	@Override
	public ResultSet<String> searchSQL(final String db, final String sql) throws DatabaseException {
		try {
		return core.generateResultSet(db, sql);
		} catch (Throwable e) {
			throw new DatabaseException(e);
		}
	}
	
	@Override
	public ResultSet<String> alternativeSearchSQL(String db, String sql, String sqlForSize) throws DatabaseException {
		try {
			return core.generateResultsetWithSize(db, sql, sqlForSize);
		} catch (Throwable e) {
			throw new DatabaseException(e);
		}
	}
	
	@Override
	public void updateSQL(final String db, final String sql) throws DatabaseException {
		databaseUtils.executeSql(db, sql);
	}	

	@Override
	public ResultSet<String> xsltSearchSQL(final String db, final String sql, final String xslt) throws DatabaseException {
		try {
			return resultsetFactory.xsltMap(searchSQL(db, sql), xslt);
		} catch (Throwable e) {
			throw new DatabaseException("Error returning a transformed resultSet", e);
		}
	}

	@Override
	public  ResultSet<String> alternativeXsltSearchSQL(String db, String sql, String sqlForSize, String xslt) throws DatabaseException {
		try {
			return resultsetFactory.xsltMap(alternativeSearchSQL(db, sql, sqlForSize), xslt);
		} catch (Throwable e) {
			throw new DatabaseException("Error returning a transformed resultSet", e);
		}
	}
	
	
	@Override
	public boolean contains(final String db, final String table, final String column, final String value) throws DatabaseException {
		return databaseUtils.contains(db, table, column, value);
	}	

	public ExecutorService getThreadPool() {
		return threadPool;
	}

	public void setThreadPool(ExecutorService threadPool) {
		this.threadPool = threadPool;
	}

	public DatabaseBlackBoardNotificationHandler getBlackBoardNotificationHandler() {
		return blackBoardNotificationHandler;
	}

	@Required
	public void setBlackBoardNotificationHandler(DatabaseBlackBoardNotificationHandler blackBoardNotificationHandler) {
		this.blackBoardNotificationHandler = blackBoardNotificationHandler;
	}

}
