package eu.dnetlib.functionality.cql.lucene;

import java.util.List;
import java.util.Map;
import java.util.Vector;

import com.google.common.collect.BiMap;
import eu.dnetlib.functionality.cql.CqlValueTransformerMap;
import eu.dnetlib.functionality.cql.lucene.SortOperation.Mode;
import eu.dnetlib.functionality.cql.parse.*;
import eu.dnetlib.miscutils.functional.UnaryFunction;
import org.z3950.zing.cql.*;

/**
 * Created by claudio on 12/09/16.
 */
public class LuceneCqlTranslator {

	/**
	 * Internal recursive translator method.
	 *
	 * @param node
	 * @param valueTranformerMap
	 * @param cqlOptions
	 * @param aliases
	 * @param weights
	 * @return
	 * @throws CQLParseException
	 */
	public static TranslatedQuery translate(
			CQLNode node,
			CqlValueTransformerMap valueTranformerMap,
			Map<String, List<String>> cqlOptions,
			BiMap<String, String> aliases,
			Map<String, String> weights) throws CQLParseException {

		if (node instanceof CQLBooleanNode) {
			return doTranslate((CQLBooleanNode) node, valueTranformerMap, cqlOptions, aliases, weights);
		}

		if (node instanceof CQLTermNode) {
			return doTranslate((CQLTermNode) node, valueTranformerMap, cqlOptions, aliases, weights);
		}

		if (node instanceof CQLSortNode) {
			return doTranslate((CQLSortNode) node, valueTranformerMap, cqlOptions, aliases, weights);
		}

		throw new RuntimeException("error choice");

	}

	private static TranslatedQuery doTranslate(
			CQLSortNode node,
			CqlValueTransformerMap valueTranformerMap,
			Map<String, List<String>> cqlOptions,
			BiMap<String, String> aliases,
			Map<String, String> weights) throws CQLParseException {

		TranslatedQuery subQuery = translate(node.subtree, valueTranformerMap, cqlOptions, aliases, weights);
		Node query = subQuery.getQuery();

		final String sortField = node.getSortIndexes().get(0).getBase();
		final Mode sortMode = SortOperation.modifiersToMode(node.getSortIndexes().get(0).getModifiers());

		QueryOptions options = new QueryOptions(new SortOperation(sortField, sortMode));

		return new TranslatedQuery(query, options);
	}

	private static TranslatedQuery doTranslate(
			CQLBooleanNode node,
			CqlValueTransformerMap valueTranformerMap,
			Map<String, List<String>> cqlOptions,
			BiMap<String, String> aliases,
			Map<String, String> weights) throws CQLParseException {

		TranslatedQuery left = translate(node.left, valueTranformerMap, cqlOptions, aliases, weights);
		TranslatedQuery right = translate(node.right, valueTranformerMap, cqlOptions, aliases, weights);

		QueryOptions options = right.getOptions();
		if (options != null) {
			options.merge(left.getOptions());
		}

		if (node instanceof CQLAndNode) {
			return new TranslatedQuery(new AndNode(left.getQuery(), right.getQuery()), options);
		}

		if (node instanceof CQLOrNode) {
			return new TranslatedQuery(new OrNode(left.getQuery(), right.getQuery()), options);
		}

		if (node instanceof CQLNotNode) {
			return new TranslatedQuery(new NotNode(left.getQuery(), right.getQuery()), options);
		}

		throw new RuntimeException("unknow boolean node");
	}

	private static TranslatedQuery doTranslate(
			CQLTermNode node,
			CqlValueTransformerMap valueTranformerMap,
			Map<String, List<String>> cqlOptions,
			BiMap<String, String> aliases,
			Map<String, String> weights) {

		String rel = node.getRelation().getBase();
		final String index = node.getIndex().toLowerCase();
		final Vector<Modifier> modifiers = node.getRelation().getModifiers();

		UnaryFunction<String, String> valueTransformer = valueTranformerMap.transformerFor(index);

		String term = valueTransformer.evaluate(node.getTerm());

		if (!index.equals("")) {

			if (modifiers.size() > 0) {
				rel = modifiers.firstElement().getType();
			}

			if (Relations.contains(rel)) {
				return buildTerm(cqlOptions, rel, index, term, aliases, weights);
			}

			//anything else is unsupported
			throw new RuntimeException("unknow relation: " + rel);
		}

		throw new RuntimeException("invalid field: " + index);
	}

	private static TranslatedQuery buildTerm(
			Map<String, List<String>> cqlOptions,
			String rel,
			String index,
			String term,
			BiMap<String, String> aliases,
			Map<String, String> weights) {

		//		if (weights.containsKey(index)) {
		//			term += "^" + weights.get(index);
		//		}
		return new TranslatedQuery(new TermNode(index, Relations.get(rel), term, cqlOptions, aliases, weights), new QueryOptions());
	}

}
