package eu.dnetlib.miscutils;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Map;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;

import com.google.common.base.Function;

public abstract class AbstractApplyXslt<K> implements Function<K, String> {

	private static final Log log = LogFactory.getLog(AbstractApplyXslt.class);

	private Transformer transformer;

	public AbstractApplyXslt(final Resource xslt) {
		this(xslt, null);
	}

	public AbstractApplyXslt(final String xslt) {
		this(xslt, null);
	}

	public AbstractApplyXslt(final Source xslt) {
		this(xslt, null);
	}

	public AbstractApplyXslt(final Resource xslt, final Map<String, String> parameters) {
		this(new StreamSource(getInputStream(xslt)), parameters);
	}

	public AbstractApplyXslt(final String xslt, final Map<String, String> parameters) {
		this(new StreamSource(new StringReader(xslt)), parameters);
	}

	public AbstractApplyXslt(final Source xslt, final Map<String, String> parameters) {
		try {
			final TransformerFactory factory = TransformerFactory.newInstance();
			transformer = factory.newTransformer(xslt);
			if (parameters != null) {
				for (Map.Entry<String, String> parameter : parameters.entrySet()) {
					transformer.setParameter(parameter.getKey(), parameter.getValue());
				}
			}
		} catch (final Throwable e) {
			log.error("Problems with transformer!\n" + xslt, e);
			throw new IllegalStateException(e);
		}
	}

	@Override
	public String apply(final K source) {
		try {
			final StringWriter output = new StringWriter();
			transformer.transform(toStream(source), new StreamResult(output));
			return output.toString();
		} catch (final TransformerException e) {
			log.error("cannot transform record", e);
			log.error(toString(source));
		}
		return null;
	}

	public abstract Source toStream(final K source);

	private static InputStream getInputStream(final Resource xslt) {
		try {
			return xslt.getInputStream();
		} catch (final IOException e) {
			throw new IllegalArgumentException(e);
		}
	}

	public abstract String toString(K input);
}
