/**
 * Copyright (c) 2016-2020, the Alpha Team.
 * All rights reserved.
 *
 * Additional changes made by Siemens.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1) Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2) Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package at.ac.tuwien.kr.alpha.app.config;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import at.ac.tuwien.kr.alpha.api.config.AggregateRewritingConfig;
import at.ac.tuwien.kr.alpha.api.config.AlphaConfig;
import at.ac.tuwien.kr.alpha.api.config.BinaryNoGoodPropagationEstimationStrategy;
import at.ac.tuwien.kr.alpha.api.config.Heuristic;
import at.ac.tuwien.kr.alpha.api.config.InputConfig;
import at.ac.tuwien.kr.alpha.api.config.SystemConfig;

/**
 * Parses given argument lists (as passed when Alpha is called from command line) into {@link SystemConfig}s and
 * {@link InputConfig}s.
 *
 */
public class CommandLineParser {

	private static final Logger LOGGER = LoggerFactory.getLogger(CommandLineParser.class);

	//@formatter:off
	/*
	 * Whenever a new command line option is added, perform the following steps: 
	 * 1. Add it as a constant option below. 
	 * 2. Add the constant option into the Options "CLI_OPTS" in the static initializer.
	 * 3. Add a handler method for it and add the respective map entry in initializeGlobalOptionHandlers 
	 *    or initializeInputOptionHandlers with a method reference to the handler.
	 */
	
	// "special", i.e. non-configuration options
	private static final Option OPT_HELP = Option.builder("h").longOpt("help").hasArg(false).desc("shows this help").build();

	// input-specific options
	private static final Option OPT_INPUT = Option.builder("i").longOpt("input").hasArg(true).argName("file").type(FileInputStream.class)
			.desc("read ASP program from this file").build();
	private static final Option OPT_NUM_ANSWER_SETS = Option.builder("n").longOpt("numAS").hasArg(true).argName("number").type(Integer.class)
			.desc("the number of answer sets to compute (default: compute all)").build();
	private static final Option OPT_FILTER = Option.builder("f").longOpt("filter").hasArg(true).argName("filter").valueSeparator(',')
			.desc("predicates to show when printing answer sets").build();
	private static final Option OPT_ASPSTRING = Option.builder("str").longOpt("aspstring").hasArg(true).argName("program").type(String.class)
			.desc("provide the asp program as a string").build();
	private static final Option OPT_LITERATE = Option.builder("l").longOpt("literate")
			.desc("enable literate programming mode (default: " + InputConfig.DEFAULT_LITERATE + ")").build();
	private static final Option OPT_DEBUG_PREPROCESSING = Option.builder("dbgp").longOpt("DebugPreprocessing")
			.desc("write files for normalized and preprocessed programs, also emit dependency- and component graphs as (graphviz) dot files").build();
	private static final Option OPT_WRITE_XSLX = Option.builder("wx").longOpt("write-xlsx").hasArg(true).argName("path").type(String.class)
			.desc("Write answer sets to excel files, i.e. xlsx workbooks (one workbook per answer set)").build();
	private static final Option OPT_REIFY = Option.builder("reify").longOpt("reifyProgram").hasArg(false).desc("Reifies, i.e. encodes as ASP facts, the given program.").build();
	private static final Option OPT_RUN_TESTS = Option.builder("t").longOpt("run-tests").hasArg(false).desc("Runs all unit tests of the given ASP Program").build();

	// general system-wide config
	private static final Option OPT_GROUNDER = Option.builder("g").longOpt("grounder").hasArg(true).argName("grounder")
			.desc("the grounder implementation to use (default: " + SystemConfig.DEFAULT_GROUNDER_NAME + ")").build();
	private static final Option OPT_SOLVER = Option.builder("s").longOpt("solver").hasArg(true).argName("solver")
			.desc("the solver implementation to use (default: " + SystemConfig.DEFAULT_SOLVER_NAME + ")").build();
	private static final Option OPT_NOGOOD_STORE = Option.builder("r").longOpt("store").hasArg(true).argName("store")
			.desc("the nogood store to use (default: " + SystemConfig.DEFAULT_NOGOOD_STORE_NAME + ")").build();
	private static final Option OPT_SORT = Option.builder("sort").longOpt("sort").hasArg(false)
			.desc("sort answer sets (default: " + SystemConfig.DEFAULT_SORT_ANSWER_SETS + ")").build();
	private static final Option OPT_DETERMINISTIC = Option.builder("d").longOpt("deterministic").hasArg(false)
			.desc("disables randomness (default: " + SystemConfig.DEFAULT_DETERMINISTIC + ")").build();
	private static final Option OPT_SEED = Option.builder("e").longOpt("seed").hasArg(true).argName("seed").type(Integer.class)
			.desc("set seed (default: System.nanoTime())").build();
	private static final Option OPT_DEBUG_INTERNAL_CHECKS = Option.builder("dbgs").longOpt("DebugEnableInternalChecks")
			.desc("run additional (time-consuming) safety checks (default: " + SystemConfig.DEFAULT_DEBUG_INTERNAL_CHECKS + ")").build();
	private static final Option OPT_BRANCHING_HEURISTIC = Option.builder("b").longOpt("branchingHeuristic").hasArg(true).argName("heuristic")
			.desc("the branching heuristic to use (default: " + SystemConfig.DEFAULT_BRANCHING_HEURISTIC.name() + ")").build();
	private static final Option OPT_MOMS_STRATEGY = Option.builder("ms").longOpt("momsStrategy").hasArg(true).argName("strategy")
			.desc("strategy for mom's heuristic (CountBinaryWatches or BinaryNoGoodPropagation, default: " + SystemConfig.DEFAULT_MOMS_STRATEGY.name() + ")")
			.build();
	private static final Option OPT_REPLAY_CHOICES = Option.builder("rc").longOpt("replayChoices").hasArg().argName("choices")
			.desc("comma-separated list of choices to be replayed (each choice is represented by a signed integer whose absolute value designates an atom ID and whose sign designates a truth value)")
			.build();
	private static final Option OPT_QUIET = Option.builder("q").longOpt("quiet").desc("do not print answer sets (default: " + SystemConfig.DEFAULT_QUIET)
			.build();
	private static final Option OPT_STATS = Option.builder("st").longOpt("stats").desc("print statistics (default: " + SystemConfig.DEFAULT_PRINT_STATS + ")")
			.build();
	private static final Option OPT_NO_JUSTIFICATION = Option.builder("dj").longOpt("disableJustifications")
			.desc("disable the search for justifications on must-be-true assigned atoms in the solver (default: "
					+ SystemConfig.DEFAULT_DISABLE_JUSTIFICATION_SEARCH + ")")
			.build();
	private static final Option OPT_AGGREGATES_NO_SORTING_GRID = Option.builder("dsg").longOpt("disableSortingGrid")
			.desc("use counting grid based rewriting instead of sorting circuit for #count aggregates (default: " + !AggregateRewritingConfig.DEFAULT_USE_SORTING_GRID + ")")
			.build();
	private static final Option OPT_AGGREGATES_NO_NEGATIVE_INTEGERS = Option.builder("dni").longOpt("disableNegativeIntegers")
			.desc("use simplified encodings without netagive integer support for #sum aggregates (default: " + !AggregateRewritingConfig.DEFAULT_SUPPORT_NEGATIVE_INTEGERS + ")")
			.build();
	private static final Option OPT_NO_EVAL_STRATIFIED = Option.builder("dse").longOpt("disableStratifiedEvaluation")
			.desc("Disable stratified evaluation")
			.build();
	private static final Option OPT_NO_NOGOOD_DELETION = Option.builder("dnd").longOpt("disableNoGoodDeletion")
			.desc("disable the deletion of (learned, little active) nogoods (default: " 
					+ SystemConfig.DEFAULT_DISABLE_NOGOOD_DELETION + ")")
			.build();
	private static final Option OPT_GROUNDER_TOLERANCE_CONSTRAINTS = Option.builder("gtc").longOpt("grounderToleranceConstraints")
			.desc("grounder tolerance for constraints (default: " + SystemConfig.DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS + ")")
			.hasArg().argName("tolerance")
			.build();
	private static final Option OPT_GROUNDER_TOLERANCE_RULES = Option.builder("gtr").longOpt("grounderToleranceRules")
			.desc("grounder tolerance for rules (default: " + SystemConfig.DEFAULT_GROUNDER_TOLERANCE_RULES + ")")
			.hasArg().argName("tolerance")
			.build();
	private static final Option OPT_GROUNDER_ACCUMULATOR_ENABLED = Option.builder("acc").longOpt("enableAccumulator")
			.desc("activates the accumulator grounding strategy by disabling removal of instances from grounder memory in certain cases (default: " 
					+ SystemConfig.DEFAULT_GROUNDER_ACCUMULATOR_ENABLED + ")")
			.build();
	private static final Option OPT_OUTPUT_ATOM_SEPARATOR = Option.builder("sep").longOpt("atomSeparator").hasArg(true).argName("separator")
			.desc("a character (sequence) to use as separator for atoms in printed answer sets (default: "
					+ SystemConfig.DEFAULT_ATOM_SEPARATOR + ")")
			.build();
	//@formatter:on

	private static final Options CLI_OPTS = new Options();

	static {
		/*
		 * Below code adds all options defined above to CLI_OPTS - needed for parsing
		 */
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_HELP);

		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NUM_ANSWER_SETS);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_FILTER);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_LITERATE);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_INPUT);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_ASPSTRING);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_DEBUG_PREPROCESSING);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_WRITE_XSLX);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_REIFY);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_RUN_TESTS);

		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_GROUNDER);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_SOLVER);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NOGOOD_STORE);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_SORT);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_DETERMINISTIC);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_SEED);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_DEBUG_INTERNAL_CHECKS);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_BRANCHING_HEURISTIC);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_MOMS_STRATEGY);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_REPLAY_CHOICES);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_QUIET);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_STATS);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NO_JUSTIFICATION);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_AGGREGATES_NO_SORTING_GRID);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_AGGREGATES_NO_NEGATIVE_INTEGERS);

		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NO_EVAL_STRATIFIED);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_NO_NOGOOD_DELETION);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_GROUNDER_TOLERANCE_CONSTRAINTS);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_GROUNDER_TOLERANCE_RULES);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_GROUNDER_ACCUMULATOR_ENABLED);
		CommandLineParser.CLI_OPTS.addOption(CommandLineParser.OPT_OUTPUT_ATOM_SEPARATOR);
	}

	/*
	 * Below maps map commandline options to handler methods. If a new option is added, the appropriate put into the map
	 * must be added in the constructor
	 */
	private final Map<String, CliOptionHandler<SystemConfig>> globalOptionHandlers = new HashMap<>();
	private final Map<String, CliOptionHandler<InputConfig>> inputOptionHandlers = new HashMap<>();
	private Consumer<String> abortAction;
	private String cmdSyntax;

	/**
	 * Creates a new <code>CommandLineParser</code>. The abortAction described below is passed into the constructor
	 * externally in order to avoid strongly coupling this class to any other part of the application. Especially an
	 * abortAction - which likely will include some call like System.exit({state}) - should not be specified by utility
	 * classes themselves, but rather by the application's main class.
	 * 
	 * @param cmdLineSyntax a string describing the basic call syntax for the application binary, e.g. "java -jar
	 *                      somejar.jar"
	 * @param abortAction   a <code>Consumer<String></code> that is called when option parsing is aborted, as is the case
	 *                      when the "help" option is encountered.
	 *                      It expects a string parameter, which is a message accompanying the abort
	 */
	public CommandLineParser(String cmdLineSyntax, Consumer<String> abortAction) {
		this.cmdSyntax = cmdLineSyntax;
		this.abortAction = abortAction;
		this.initializeGlobalOptionHandlers();
		this.initializeInputOptionHandlers();
	}

	private void initializeGlobalOptionHandlers() {
		/*
		 * below put invocations are used to "register" the handler methods for each commandline option
		 */
		// help is handled separately, therefore dummy handler
		this.globalOptionHandlers.put(CommandLineParser.OPT_HELP.getOpt(), (o, c) -> { });
		this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER.getOpt(), this::handleGrounder);
		this.globalOptionHandlers.put(CommandLineParser.OPT_SOLVER.getOpt(), this::handleSolver);
		this.globalOptionHandlers.put(CommandLineParser.OPT_NOGOOD_STORE.getOpt(), this::handleNogoodStore);
		this.globalOptionHandlers.put(CommandLineParser.OPT_SORT.getOpt(), this::handleSort);
		this.globalOptionHandlers.put(CommandLineParser.OPT_DETERMINISTIC.getOpt(), this::handleDeterministic);
		this.globalOptionHandlers.put(CommandLineParser.OPT_SEED.getOpt(), this::handleSeed);
		this.globalOptionHandlers.put(CommandLineParser.OPT_DEBUG_INTERNAL_CHECKS.getOpt(), this::handleInternalChecks);
		this.globalOptionHandlers.put(CommandLineParser.OPT_BRANCHING_HEURISTIC.getOpt(), this::handleBranchingHeuristic);
		this.globalOptionHandlers.put(CommandLineParser.OPT_MOMS_STRATEGY.getOpt(), this::handleMomsStrategy);
		this.globalOptionHandlers.put(CommandLineParser.OPT_REPLAY_CHOICES.getOpt(), this::handleReplayChoices);
		this.globalOptionHandlers.put(CommandLineParser.OPT_QUIET.getOpt(), this::handleQuiet);
		this.globalOptionHandlers.put(CommandLineParser.OPT_STATS.getOpt(), this::handleStats);
		this.globalOptionHandlers.put(CommandLineParser.OPT_NO_JUSTIFICATION.getOpt(), this::handleNoJustification);
		this.globalOptionHandlers.put(CommandLineParser.OPT_AGGREGATES_NO_SORTING_GRID.getOpt(), this::handleDisableSortingGrid);
		this.globalOptionHandlers.put(CommandLineParser.OPT_AGGREGATES_NO_NEGATIVE_INTEGERS.getOpt(), this::handleDisableNegativeSumElements);
		this.globalOptionHandlers.put(CommandLineParser.OPT_NO_EVAL_STRATIFIED.getOpt(), this::handleDisableStratifedEval);
		this.globalOptionHandlers.put(CommandLineParser.OPT_NO_NOGOOD_DELETION.getOpt(), this::handleNoNoGoodDeletion);
		this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER_TOLERANCE_CONSTRAINTS.getOpt(), this::handleGrounderToleranceConstraints);
		this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER_TOLERANCE_RULES.getOpt(), this::handleGrounderToleranceRules);
		this.globalOptionHandlers.put(CommandLineParser.OPT_GROUNDER_ACCUMULATOR_ENABLED.getOpt(), this::handleGrounderNoInstanceRemoval);
		this.globalOptionHandlers.put(CommandLineParser.OPT_OUTPUT_ATOM_SEPARATOR.getOpt(), this::handleAtomSeparator);
	}

	private void initializeInputOptionHandlers() {
		this.inputOptionHandlers.put(CommandLineParser.OPT_NUM_ANSWER_SETS.getOpt(), this::handleNumAnswerSets);
		this.inputOptionHandlers.put(CommandLineParser.OPT_INPUT.getOpt(), this::handleInput);
		this.inputOptionHandlers.put(CommandLineParser.OPT_FILTER.getOpt(), this::handleFilters);
		this.inputOptionHandlers.put(CommandLineParser.OPT_ASPSTRING.getOpt(), this::handleAspString);
		this.inputOptionHandlers.put(CommandLineParser.OPT_LITERATE.getOpt(), this::handleLiterate);
		this.inputOptionHandlers.put(CommandLineParser.OPT_DEBUG_PREPROCESSING.getOpt(), this::handleDebugPreprocessing);
		this.inputOptionHandlers.put(CommandLineParser.OPT_WRITE_XSLX.getOpt(), this::handleWriteXlsx);
		this.inputOptionHandlers.put(CommandLineParser.OPT_REIFY.getOpt(), this::handleReify);
		this.inputOptionHandlers.put(CommandLineParser.OPT_RUN_TESTS.getOpt(), this::handleRunTests);
	}

	public AlphaConfig parseCommandLine(String[] args) throws ParseException {
		CommandLine commandLine = new DefaultParser().parse(CommandLineParser.CLI_OPTS, args);
		if (commandLine.getArgs().length > 0) {
			throw new ParseException("Positional arguments { " + StringUtils.join(args, ' ') + " } are invalid!");
		}
		AlphaConfig retVal = new AlphaConfig();
		SystemConfig sysConf = new SystemConfig();
		InputConfig inputConf = new InputConfig();
		if (commandLine.hasOption(CommandLineParser.OPT_HELP.getOpt())) {
			LOGGER.debug("Found help option!");
			this.handleHelp();
		} else {
			this.validate(commandLine);
		}
		for (Option opt : commandLine.getOptions()) {
			this.handleOption(opt, sysConf, inputConf);
		}
		retVal.setSystemConfig(sysConf);
		retVal.setInputConfig(inputConf);
		return retVal;
	}

	private void handleOption(Option opt, SystemConfig sysConf, InputConfig inputConf) throws ParseException {
		CliOptionHandler<SystemConfig> globalOptionHandler;
		CliOptionHandler<InputConfig> inputOptionHandler;
		globalOptionHandler = this.globalOptionHandlers.get(opt.getOpt());
		if (globalOptionHandler != null) {
			globalOptionHandler.handleOption(opt, sysConf);
		} else {
			inputOptionHandler = this.inputOptionHandlers.get(opt.getOpt());
			if (inputOptionHandler != null) {
				inputOptionHandler.handleOption(opt, inputConf);
			} else {
				throw new ParseException("Cannot handle option: " + opt.getOpt());
			}
		}
	}

	public String getUsageMessage() {
		HelpFormatter formatter = new HelpFormatter();
		// Unfortunately, commons-cli does not offer a method of simply rendering a help
		// message into a string, therefore the ByteArrayOutputStream..
		ByteArrayOutputStream helpBuffer = new ByteArrayOutputStream();
		PrintWriter pw = new PrintWriter(helpBuffer);
		formatter.printHelp(pw, HelpFormatter.DEFAULT_WIDTH, this.cmdSyntax, "", CommandLineParser.CLI_OPTS, HelpFormatter.DEFAULT_LEFT_PAD,
				HelpFormatter.DEFAULT_DESC_PAD, "");
		pw.flush();
		return helpBuffer.toString();
	}

	private void validate(CommandLine commandLine) throws ParseException {
		if (!commandLine.hasOption(CommandLineParser.OPT_INPUT.getOpt()) && !commandLine.hasOption(CommandLineParser.OPT_ASPSTRING.getOpt())) {
			throw new ParseException("Missing input source - need to specifiy either a file (" + CommandLineParser.OPT_INPUT.getOpt() + ") or a string ("
					+ CommandLineParser.OPT_ASPSTRING.getOpt() + " - or both)!");
		}
	}

	private void handleNumAnswerSets(Option opt, InputConfig cfg) {
		String optVal = opt.getValue();
		int limit;
		if (optVal != null) {
			limit = Integer.valueOf(optVal);
			cfg.setNumAnswerSets(limit);
		} else {
			cfg.setNumAnswerSets(InputConfig.DEFAULT_NUM_ANSWER_SETS);
		}
	}

	private void handleHelp() {
		this.abortAction.accept(this.getUsageMessage());
	}

	private void handleInput(Option opt, InputConfig cfg) {
		String optVal = opt.getValue().trim();
		cfg.getFiles().add(optVal);
	}

	private void handleGrounder(Option opt, SystemConfig cfg) {
		cfg.setGrounderName(opt.getValue(SystemConfig.DEFAULT_GROUNDER_NAME));
	}

	private void handleSolver(Option opt, SystemConfig cfg) {
		cfg.setSolverName(opt.getValue(SystemConfig.DEFAULT_SOLVER_NAME));
	}

	private void handleNogoodStore(Option opt, SystemConfig cfg) {
		cfg.setNogoodStoreName(opt.getValue(SystemConfig.DEFAULT_NOGOOD_STORE_NAME));
	}

	private void handleFilters(Option opt, InputConfig cfg) {
		String pred = opt.getValue().trim();
		cfg.getDesiredPredicates().add(pred);
	}

	private void handleAspString(Option opt, InputConfig cfg) {
		String optVal = opt.getValue().trim();
		cfg.getAspStrings().add(optVal);
	}

	private void handleSort(Option opt, SystemConfig cfg) {
		cfg.setSortAnswerSets(true);
	}

	private void handleDeterministic(Option opt, SystemConfig cfg) {
		cfg.setDeterministic(true);
		cfg.setSeed(0);
	}

	private void handleSeed(Option opt, SystemConfig cfg) {
		cfg.setDeterministic(false);
		String optVal = opt.getValue();
		long seed;
		if (optVal != null) {
			seed = Long.valueOf(optVal);
			cfg.setSeed(seed);
		} else {
			cfg.setSeed(SystemConfig.DEFAULT_SEED);
		}
	}

	private void handleInternalChecks(Option opt, SystemConfig cfg) {
		cfg.setDebugInternalChecks(true);
	}

	private void handleBranchingHeuristic(Option opt, SystemConfig cfg) throws ParseException {
		String branchingHeuristicName = opt.getValue(SystemConfig.DEFAULT_BRANCHING_HEURISTIC.name());
		try {
			cfg.setBranchingHeuristicName(branchingHeuristicName);
		} catch (IllegalArgumentException e) {
			throw new ParseException(
					"Unknown branching heuristic: " + branchingHeuristicName + ". Please try one of the following: " + Heuristic.listAllowedValues());
		}
	}

	private void handleMomsStrategy(Option opt, SystemConfig cfg) throws ParseException {
		String momsStrategyName = opt.getValue(SystemConfig.DEFAULT_MOMS_STRATEGY.name());
		try {
			cfg.setMomsStrategyName(momsStrategyName);
		} catch (IllegalArgumentException e) {
			throw new ParseException("Unknown mom's strategy: " + momsStrategyName + ". Please try one of the following: "
					+ BinaryNoGoodPropagationEstimationStrategy.listAllowedValues());
		}
	}

	private void handleReplayChoices(Option opt, SystemConfig cfg) throws ParseException {
		String replayChoices = opt.getValue(SystemConfig.DEFAULT_REPLAY_CHOICES.toString());
		try {
			cfg.setReplayChoices(replayChoices);
		} catch (NumberFormatException e) {
			throw new ParseException("Cannot parse list of signed integers indicating choices to be replayed: " + replayChoices);
		}
	}

	private void handleQuiet(Option opt, SystemConfig cfg) {
		cfg.setQuiet(true);
	}

	private void handleLiterate(Option opt, InputConfig cfg) {
		cfg.setLiterate(true);
	}

	private void handleWriteXlsx(Option opt, InputConfig cfg) {
		cfg.setWriteAnswerSetsAsXlsx(true);
		String outputPath = opt.getValue(InputConfig.DEFAULT_XLSX_OUTFILE_PATH);
		cfg.setAnswerSetFileOutputPath(outputPath);
	}

	private void handleReify(Option opt, InputConfig cfg) {
		cfg.setReifyInput(true);
	}

	private void handleRunTests(Option opt, InputConfig cfg) {
		cfg.setRunTests(true);
	}

	private void handleStats(Option opt, SystemConfig cfg) {
		cfg.setPrintStats(true);
	}

	private void handleNoJustification(Option opt, SystemConfig cfg) {
		cfg.setDisableJustificationSearch(true);
	}

	private void handleDisableSortingGrid(Option opt, SystemConfig cfg) {
		cfg.getAggregateRewritingConfig().setUseSortingGridEncoding(false);
	}

	private void handleDisableNegativeSumElements(Option opt, SystemConfig cfg) {
		cfg.getAggregateRewritingConfig().setSupportNegativeValuesInSums(false);
	}

	private void handleDisableStratifedEval(Option opt, SystemConfig cfg) {
		cfg.setEvaluateStratifiedPart(false);
	}

	private void handleDebugPreprocessing(Option opt, InputConfig cfg) {
		cfg.setDebugPreprocessing(true);
	}

	private void handleNoNoGoodDeletion(Option opt, SystemConfig cfg) {
		cfg.setDisableNoGoodDeletion(true);
	}

	private void handleGrounderToleranceConstraints(Option opt, SystemConfig cfg) {
		String grounderToleranceConstraints = opt.getValue(SystemConfig.DEFAULT_GROUNDER_TOLERANCE_CONSTRAINTS);
		cfg.setGrounderToleranceConstraints(grounderToleranceConstraints);
	}

	private void handleGrounderToleranceRules(Option opt, SystemConfig cfg) {
		String grounderToleranceRules = opt.getValue(SystemConfig.DEFAULT_GROUNDER_TOLERANCE_RULES);
		cfg.setGrounderToleranceRules(grounderToleranceRules);
	}

	private void handleGrounderNoInstanceRemoval(Option opt, SystemConfig cfg) {
		cfg.setGrounderAccumulatorEnabled(true);
	}

	private void handleAtomSeparator(Option opt, SystemConfig cfg) {
		cfg.setAtomSeparator(StringEscapeUtils.unescapeJava(opt.getValue(SystemConfig.DEFAULT_ATOM_SEPARATOR)));
	}

}
