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 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;

import com.google.gson.Gson;

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;

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 Exception e) {
					log.error("Error in thread when importing from epr", e);
				}
			}
		});
	}
	
	@SuppressWarnings("unchecked")
	private void processJobInternal(BlackboardJob job) {
		String action = job.getAction();
		
		log.info("processing database job: " + action);
		
		try {
			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 {
				job.setError("unsupported Action: " + action);
				log.warn("unsupported message action: " + action);
				throw new IllegalArgumentException("unsupported message action: " + action);				
			}
		} catch (final Exception e) {
			log.error(e.getMessage(), e);
			getBlackboardHandler().failed(job, e);
		}
	}
	
	private String decodeBase64(final String s) {
		if (s != null && Base64.isArrayByteBase64(s.getBytes())) {
			return new String(Base64.decodeBase64(s.getBytes()));
		}
		return s;
	}


	public DatabaseServiceCore getCore() {
		return core;
	}

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

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

	public ExecutorService getThreadPool() {
		return threadPool;
	}

	public XSLTMappedResultSetFactory getXsltResultsetFactory() {
		return xsltResultsetFactory;
	}

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