package eu.dnetlib.functionality.cql;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.z3950.zing.cql.CQLNode;
import org.z3950.zing.cql.CQLOrNode;
import org.z3950.zing.cql.CQLParseException;
import org.z3950.zing.cql.CQLParser;
import org.z3950.zing.cql.ModifierSet;

import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class CqlUtils {

	public static final String cqlDefaultField = "cql.serverChoice";
	
	private CqlUtils() {}

	public static CQLNode expand(final String query, Set<String> fields) {
		return expand(parse(query), fields);
	}

	public static CQLNode expand(CQLNode root, Set<String> fields) {

		if (fields.isEmpty()) {
			return root;
		}

		Map<String, CQLNode> unfielded = group(filter(root, listFields(root)), Lists.newArrayList(CqlUtils.cqlDefaultField));
		CQLNode terms = unfielded.get(CqlGroup.defaultTerm);

		if (terms != null) {
			CQLNode expand = new CQLExpander().expand(terms, fields);
			return new CQLOrNode(root, expand, new ModifierSet("or"));
		}

		return root;
	}

	public static List<String> listFields(final String query) {
		return listFields(parse(query));
	}

	public static List<String> listFields(CQLNode root) {
		Set<String> fields = new CQLFieldLister().listFields(root);
		fields.remove(cqlDefaultField);
		return Lists.newArrayList(fields);
	}

	public static List<String> listTerms(final String query, final String field) {
		return listTerms(parse(query), field);
	}

	public static List<String> listTerms(final CQLNode query, final String field) {
		return new CqlTermLister().listTerms(group(query, Lists.newArrayList(field)).get(field), field);
	}

	public static Map<String, CQLNode> group(final CQLNode root, final List<String> fields) {
		Map<String, CQLNode> groups = new CqlGroup().group(root, fields);
		groups.put(CqlGroup.defaultTerm, new CqlFilter().filter(root, fields));
		return Maps.filterValues(groups, Predicates.notNull());
	}

	public static Map<String, CQLNode> group(final String query, final List<String> fields) {
		return group(parse(query), fields);
	}

	public static CQLNode filter(final CQLNode query, final List<String> fields) {
		return new CqlFilter().filter(query, fields);
	}

	public static CQLNode filter(final String query, final List<String> fields) {
		return filter(parse(query), fields);
	}

	public static CQLNode parse(final String query) {
		try {
			return query != null ? new CQLParser().parse(query) : null;
		} catch (CQLParseException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
