package eu.dnetlib.enabling.database;

import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.xml.ws.wsaddressing.W3CEndpointReference;

import com.google.gson.Gson;
import eu.dnetlib.enabling.database.rmi.DatabaseException;
import eu.dnetlib.enabling.resultset.XSLTMappedResultSetFactory;
import eu.dnetlib.enabling.resultset.client.utils.EPRUtils;
import eu.dnetlib.enabling.tools.blackboard.AbstractBlackboardNotificationHandler;
import eu.dnetlib.enabling.tools.blackboard.BlackboardJob;
import eu.dnetlib.enabling.tools.blackboard.BlackboardServerHandler;
import eu.dnetlib.enabling.tools.blackboard.NotificationHandler;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Required;

public class DatabaseBlackBoardNotificationHandler extends AbstractBlackboardNotificationHandler<BlackboardServerHandler> implements NotificationHandler {

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

	private DatabaseServiceCore core;

	private ExecutorService threadPool = Executors.newCachedThreadPool();

	private XSLTMappedResultSetFactory xsltResultsetFactory;

	@Override
	protected void processJob(final BlackboardJob job) {
		threadPool.execute(new Runnable() {

			@Override
			public void run() {
				try {
					processJobInternal(job);
				} catch (final Throwable e) {
					log.error(e.getMessage(), e);
					getBlackboardHandler().failed(job, e);
				}
			}
		});
	}

	@SuppressWarnings("unchecked")
	private void processJobInternal(final BlackboardJob job) throws DatabaseException {
		String action = job.getAction();

		log.info("processing database job: " + action);

		if (action.equals("IMPORT")) {
			String db = job.getParameters().get("db");
			String epr = decodeBase64(job.getParameters().get("epr"));
			String xslt = decodeBase64(job.getParameters().get("xslt"));
			String xsltParamsString = decodeBase64(job.getParameters().get("xsltParams"));

			Map<String, String> xsltParams = null;
			if ((xsltParamsString != null) && !xsltParamsString.isEmpty()) {
				xsltParams = (new Gson()).fromJson(xsltParamsString, Map.class);
			}

			if ((db == null) || db.isEmpty() || (epr == null) || epr.isEmpty())
				throw new IllegalArgumentException("Some needed params are null or empty.");

			W3CEndpointReference eprObject = (new EPRUtils()).getEpr(epr);

			W3CEndpointReference mappedEpr = null;
			if ((xslt == null) || xslt.isEmpty()) {
				mappedEpr = eprObject;
			} else if ((xsltParams == null) || xsltParams.isEmpty()) {
				mappedEpr = xsltResultsetFactory.createMappedResultSet(eprObject, xslt);
			} else {
				mappedEpr = xsltResultsetFactory.createMappedResultSet(eprObject, xslt, xsltParams);
			}

			core.importFromResultset(db, mappedEpr);
			getBlackboardHandler().done(job);
			log.info("IMPORT job set to DONE");
		} else if (action.equals("EXEC")) {
			String db = job.getParameters().get("db");
			String sql = job.getParameters().get("sql");

			if ((db == null) || db.isEmpty() || (sql == null) || sql.isEmpty())
				throw new IllegalArgumentException("Some needed params are null or empty.");

			log.info("EXECUTING SCRIPT: " + sql + " on " + db);
			core.getDbUtils().executeSql(db, sql);
			getBlackboardHandler().done(job);
			log.info("SCRIPT COMPLETED");
		} else {
			throw new IllegalArgumentException("unsupported message action: " + action);
		}
	}

	private String decodeBase64(final String s) {
		if ((s != null) && Base64.isBase64(s.getBytes())) return new String(Base64.decodeBase64(s.getBytes()));
		return s;
	}

	public DatabaseServiceCore getCore() {
		return core;
	}

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

	public ExecutorService getThreadPool() {
		return threadPool;
	}

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

	public XSLTMappedResultSetFactory getXsltResultsetFactory() {
		return xsltResultsetFactory;
	}

	@Required
	public void setXsltResultsetFactory(final XSLTMappedResultSetFactory xsltResultsetFactory) {
		this.xsltResultsetFactory = xsltResultsetFactory;
	}
}
