package eu.dnetlib.msro.workflows.nodes;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.Resource;

import com.google.gson.JsonSyntaxException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;

import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.googlecode.sarasvati.Engine;
import com.googlecode.sarasvati.NodeToken;
import com.googlecode.sarasvati.mem.MemNode;

import eu.dnetlib.conf.PropertyFetcher;

public abstract class SarasvatiJobNode extends MemNode implements BeanNameAware {

	/**
	 * logger.
	 */
	private static final Log log = LogFactory.getLog(SarasvatiJobNode.class);

	private String beanName;

	/**
	 * parameter values injected as json map into subclasses instance beans.
	 */
	private String params;

	/**
	 * parameter values retrieved from the sarasvati Env.
	 */
	private String envParams;

	/**
	 * parameter values retrieved from the system properties.
	 */
	private String sysParams;

	@Resource(name = "propertyFetcher")
	private PropertyFetcher propertyFetcher;

	@Override
	public void execute(final Engine engine, final NodeToken token) {
		final String prefix = "system:node:" + getName() + ":";

		for (Field field : getClass().getDeclaredFields()) {
			try {
				field.setAccessible(true);
				token.getEnv().setAttribute(prefix + field.getName(), "" + field.get(this));
			} catch (Exception e) {
				log.warn("Error accessing value of field: " + field.getName());
			}
		}
	}

	/**
	 * builds a parameter map overriding values having the same name in the following order: sysparams < envparams < params
	 * 
	 * @param token
	 * @return
	 */
	protected Map<String, String> parseJsonParameters(final NodeToken token) {
		final Map<String, String> res = Maps.newHashMap();
		for (Entry<?, ?> e : asMapEntries(getSysParams())) {
			String propertyName = e.getValue().toString();

			if (!getPropertyFetcher().getProps().containsKey(propertyName)) {
				log.warn("unable to find system property: '" + propertyName + "'");
			}

			String propertyValue = getPropertyFetcher().getProperty(propertyName);
			res.put(e.getKey().toString(), propertyValue);
		}
		for (Entry<?, ?> e : asMapEntries(getEnvParams())) {
			final String param = e.getValue().toString();

			if (token.getEnv().hasAttribute(param)) {
				res.put(e.getKey().toString(), token.getEnv().getAttribute(param));
			} else {
				res.put(e.getKey().toString(), token.getFullEnv().getAttribute(param));
			}
		}
		for (Entry<?, ?> e : asMapEntries(getParams())) {
			res.put(e.getKey().toString(), e.getValue().toString());
		}
		return res;
	}

	@SuppressWarnings("unchecked")
	private Set<Map.Entry<?, ?>> asMapEntries(final String s) {
		try {
			return isSet(s) ? new Gson().fromJson(s, Map.class).entrySet() : new HashSet<>();
		} catch(JsonSyntaxException e) {
			log.error(String.format("invalid json: %s", s));
			throw e;
		}
	}

	private boolean isSet(final String s) {
		return (s != null) && !s.isEmpty();
	}

	// ///////////////

	public String getBeanName() {
		return beanName;
	}

	@Override
	public void setBeanName(final String beanName) {
		this.beanName = beanName;
	}

	public PropertyFetcher getPropertyFetcher() {
		return propertyFetcher;
	}

	public String getParams() {
		return params;
	}

	public void setParams(final String params) {
		this.params = params;
	}

	public String getEnvParams() {
		return envParams;
	}

	public void setEnvParams(final String envParams) {
		this.envParams = envParams;
	}

	public String getSysParams() {
		return sysParams;
	}

	public void setSysParams(final String sysParams) {
		this.sysParams = sysParams;
	}

}
