/*
 * SmodelsWrapper.java
 *
 * Copyright (C) 2006 - 2007 Martin Slota
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * History:
 * v0.1 (2007-01-14): initial version
 * v0.1.1 (2007-01-28):
 * - checked exceptions eliminated (see
 *   http://www.mindview.net/Etc/Discussions/CheckedExceptions)
 * v0.1.2 (2007-02-12):
 * - initial implementation
 * v0.1.3 (2007-02-17):
 * - setSmodelsPath added
 * v0.2 (2007-03-06):
 * - tests added
 * - documentation finished
 * v1.0.0 (2007-05-05):
 * - changed to make parallel pipelining possible
 */

package lp.wrap;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * A class responsible for executing smodels with the given command line
 * arguments.
 *
 * Smodels can be executed by calling the {@link #exec()} method. This method
 * can be called multiple times and each time the current model limit is passed
 * to the smodels binary.
 *
 * @author Martin Slota
 * @version 1.0.0
 */
public class SmodelsWrapper {
	/**
	 * The singleton {@link WrapperUtils} instance used in the implementation.
	 */
	private static final WrapperUtils WU = WrapperUtils.getInstance();
	
	/**
	 * The path to the smodels binary.
	 */
	private String smodelsPath;
	
	/**
	 * Maximum number of stable models that should be computed by smodels. If it
	 * is zero, all stable models will be computed. This is also the default.
	 */
	private int modelLimit;
	
	/**
	 * The smodels process.
	 */
	private Process process;
	
	/**
	 * Creates a new instance of {@code SmodelsWrapper}.
	 *
	 * Initially the path to the smodels binary is set to "smodels", i.e. it is
	 * assumed that smodels can be executed by issuing the "smodels" command on
	 * the command line of the native operating system. This setting can be
	 * changed by calling the {@link #setSmodelsPath(String)} method.
	 *
	 * The model limit is set to 0, i.e. all stable models will be computed.
	 * This setting can be changed by calling the {@link #setModelLimit(int)}
	 * method.
	 */
	public SmodelsWrapper() {
		smodelsPath = "smodels";
		modelLimit = 0;
	}
	
	/**
	 * Sets the path to smodels binary that is used to invoke smodels.
	 *
	 * @param smodelsPath path to the smodels binary
	 * @throws IllegalArgumentException if {@code smodelsPath} is {@code null}
	 * or an empty string
	 */
	public void setSmodelsPath(String smodelsPath) {
		if (smodelsPath == null || "".equals(smodelsPath)) {
			throw new IllegalArgumentException(
					"The path to smodels must not be null or empty!");
		}
		this.smodelsPath = smodelsPath;
	}
	
	/**
	 * Sets the maximum number of stable models that should be computed by
	 * smodels. If set to zero, all models will be computed.
	 *
	 * @param modelLimit the maximum number of stable models that should be
	 * computed by smodels.
	 * @return a reference to this {@code SmodelsWrapper}
	 * @throws IllegalArgumentException if {@code modelLimit} is negative
	 */
	public SmodelsWrapper setModelLimit(int modelLimit) {
		if (modelLimit < 0) {
			throw new IllegalArgumentException(
					"The number of models must be non-negative!");
		}
		this.modelLimit = modelLimit;
		return this;
	}
	
	/**
	 * Executes smodels and passes the current command line options (see
	 * {@link #setModelLimit(int)}) to it.
	 *
	 * @throws WrapperException if an {@link java.io.IOException} occurs while
	 * creating the process
	 */
	public void exec() {
		process = WU.exec(smodelsPath + " " + modelLimit);
	}
	
	/**
	 * Returns the standard input stream of the currently executed smodels
	 * process.
	 *
	 * @return as specified above
	 */
	public OutputStream getStdin() {
		return process.getOutputStream();
	}
	
	/**
	 * Closes the standard output stream of the currently executed smodels
	 * process.
	 *
	 * @throws WrapperException if an I/O error occurs while closing the
	 * standard input stream of the smodels process
	 */
	public void closeStdin() {
		try {
			process.getOutputStream().close();
		} catch (IOException e) {
			throw new WrapperException("Error while closing the standard " +
					"input stream of the lparse process", e);
		}
	}
	
	/**
	 * Returns the standard output stream of the currently executed smodels
	 * process.
	 *
	 * @return as specified above
	 */
	public InputStream getStdout() {
		return process.getInputStream();
	}
	
	/**
	 * Waits for the currently executed smodels process to finish computation
	 * and returns its exit value (see {@link Process#waitFor()}).
	 *
	 * @return the return value of the smodels process
	 * @throws WrapperException if an {@link InterruptedException} occurs while
	 * waiting for the error stream thread or smodels process to finish their
	 * tasks
	 */
	public int waitFor() {
		try {
			return process.waitFor();
		} catch (InterruptedException e) {
			throw new WrapperException("The process was interrupted!", e);
		}
	}
}