package eu.wdaqua.qanary.web.messages;

import org.apache.jena.update.UpdateFactory;
import org.apache.jena.query.QueryParseException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class AdditionalInsertQuery {
	private String insertQuery;

	private static final Logger logger = LoggerFactory.getLogger(AdditionalTriples.class);

	public AdditionalInsertQuery(String insertQuery) {
		if (insertQuery != null) {
			try {
				this.insertQuery = parseInsertQuery(addBindsToInsertQuery(insertQuery));
				logger.info("initializing AdditionalTriples with query \n{}", this.insertQuery);
			} catch (Exception e) {
				logger.warn("initializing no additonal query: \n{}", e.getMessage());
			}
		} else {
			logger.info("initializing no additonal query");
		}
	}

	public String getInsertQuery() {
		return this.insertQuery;
	}

	private String addBindsToInsertQuery(String insertQuery) {
		String sparqlBinds = getSparqlBindsForVariables(insertQuery);
		if (sparqlBinds.length()>0) {
			return insertQuery+""// 
				+ " WHERE { " //
				+ sparqlBinds
				+"} "; //
		} else return insertQuery;
	}

	private String getSparqlBindsForVariables(String insertQuery) {
		String sparqlBinds = "";

		List<String> unnamed = findAdditionalVaraibles(insertQuery);

		for (String i : unnamed) {
			logger.info("adding URI bind for var {}", i);
			sparqlBinds += "BIND (IRI(str(RAND())) AS "+i+") . ";
		}
		return sparqlBinds;
	}


	private String parseInsertQuery(String expectedQuery) {
		logger.info("parsing query: \n{}", expectedQuery);
		Pattern pattern = Pattern.compile(".*(delete|clear|load)+.*");
		Matcher matcher = pattern.matcher(expectedQuery.toLowerCase());
		if (expectedQuery.length() == 0) {
			logger.info("no additional SPARQL INSERT query was supplied");
			return null;
		} else if (matcher.find()) {
			throw new QueryParseException(
					"additional SPARQL queries must not contain either of DELETE | CLEAR | LOAD",
					0, matcher.start() // column is not computed
					);
		} else if (!expectedQuery.toLowerCase().contains("insert")) {
			throw new QueryParseException(
					"additional SPARQL queries are expected to contain an insert statement", 0, 0);
		} else {
			try {
				UpdateFactory.create(expectedQuery);
				logger.info("successfully parsed additional SPARQL INSERT query");
				return expectedQuery;
			} catch (Exception e) {
				logger.info("parsing additional SPARQL query failed with exception: \n{}", e.getLocalizedMessage());
				return null;
			}
		}
	}

	/**
	 * return a list of all variables used in a SPARQL query
	 *
	 * @param additionalInsertQuery
	 * @return variables
	 */
	private List<String> findAdditionalVaraibles(String insertQuery) {
		int lastIdx = 0;
		String varString;
		List<String> variables = new ArrayList<String>();

		while (lastIdx != -1) {
			lastIdx = insertQuery.indexOf("?", lastIdx);
			int end = insertQuery.indexOf(" ", lastIdx);

			if (lastIdx != -1) {
				varString = insertQuery.substring(lastIdx, end);
				if (!variables.contains(varString)) {
					logger.info("found new var {} at ({},{})", varString, lastIdx, end);
					variables.add(varString);
				}
				lastIdx += varString.length();
			}
		}
		return variables;
	}

}
