package eu.dnetlib.functionality.lightui.utils;

import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import org.springframework.beans.factory.annotation.Required;
import org.z3950.zing.cql.CQLAndNode;
import org.z3950.zing.cql.CQLNode;
import org.z3950.zing.cql.CQLParseException;
import org.z3950.zing.cql.CQLParser;
import org.z3950.zing.cql.CQLRelation;
import org.z3950.zing.cql.CQLTermNode;
import org.z3950.zing.cql.ModifierSet;

public class QueryBuilderImpl extends AbstractQueryBuilder implements QueryBuilder {

	/**
	 * use bbqs.
	 */
	private boolean useBbqs;

	final CQLParser parser = new CQLParser();

	public CQLNode getQuery(final Map<String, String[]> params) throws CQLParseException, IOException {
		String bbqName = null;

		final Collection<CQLNode> nodes = new ArrayList<CQLNode>();

		for (Map.Entry<String, String[]> entry : params.entrySet()) {
			final String key = entry.getKey();
			final String value = entry.getValue()[0];

			if (key.startsWith("_")) continue;
			if (value.equals("")) continue;

			if (key.equalsIgnoreCase("allField")) {
				for (final String token : value.split(" ")) {
					nodes.add(parser.parse("\"" + token + "\""));
				}
			} else if (key.equalsIgnoreCase("language")) {
				nodes.add(new CQLTermNode(key, new CQLRelation("="), value));
			} else if (key.equalsIgnoreCase("repositoryName")) {
				final String conditionSource = Base64Coder.decodeString(value);

				if (isUseBbqs() && conditionSource.length() > 100) {
					MessageDigest m;
					try {
						m = MessageDigest.getInstance("MD5");
					} catch (NoSuchAlgorithmException e) {
						throw new IllegalStateException(e);
					}
					m.update(conditionSource.getBytes(), 0, conditionSource.length());
					bbqName = new BigInteger(1, m.digest()).toString(16);
				}
				nodes.add(parser.parse(conditionSource));
			} else {
				nodes.add(new CQLTermNode(key, new CQLRelation("any"), value));
			}
		}

		final ModifierSet plain = new ModifierSet("and");
		final ModifierSet qualified = new ModifierSet("and");
		if (bbqName != null)
			qualified.addModifier("driver.bbq", "=", bbqName);
		int i = nodes.size() - 1;

		CQLNode query = null;
		for (final CQLNode node : nodes)
			if (query == null)
				query = node;
			else
				query = new CQLAndNode(node, query, (i-- > 1 ? plain : qualified));

		// forces the usage of BBQ
		if (nodes.size() == 1 && bbqName != null)
			query = new CQLAndNode(query, parser.parse("\"" + this.getDefaultQuery() + "\""), qualified);

		return query;
	}

	@Override
	protected void handleField(Collection<CQLNode> nodes, String key, String value) throws CQLParseException, IOException {
		// NOP
	}

	public boolean isUseBbqs() {
		return useBbqs;
	}

	@Required
	public void setUseBbqs(final boolean useBbqs) {
		this.useBbqs = useBbqs;
	}

}
