package hc.adl;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * An if block: condition -and- statements. Whoa!
 * 
 * @author hc
 * 
 */
public class IfBlock implements IPreInput {
	/**
	 * IF sCondition is TRUE,
	 */
	protected String sCondition;

	/**
	 * do sStatements
	 */
	protected String sStatements;

	/**
	 * Sub ifs
	 */
	protected List<IfBlock> vSubIfs;

	/**
	 * Are we a while loop?
	 */
	protected boolean bWhile;

	/**
	 * Local variables
	 */
	protected Map<String, Object> localVars;

	/**
	 * Elif blocks
	 */
	private List<IfBlock> elifBlocks;

	/**
	 * What to execute if condition ain't met
	 */
	private IfBlock elseIfBlock;

	/**
	 * List of parameter names
	 */
	private String[] paramNames;

	/**
	 * initialize us
	 * 
	 */
	IfBlock() {
		this.sCondition = null;
		this.sStatements = "";
		this.bWhile = false;
		this.vSubIfs = new LinkedList<IfBlock>();
		this.elifBlocks = new LinkedList<IfBlock>();
		this.localVars = new HashMap<String, Object>();
	}

	/**
	 * initialize with params
	 * 
	 * @param sCondition
	 *            When to execute sStatements or null if always
	 * @param sStatements
	 *            Statements to execute when sCondition is met or is null
	 */
	IfBlock(String sCondition, String sStatements) {
		this.sCondition = sCondition;
		this.sStatements = sStatements;
		this.bWhile = false;
		this.vSubIfs = new LinkedList<IfBlock>();
		this.elifBlocks = new LinkedList<IfBlock>();
		this.localVars = new HashMap<String, Object>();
	}

	/**
	 * Set local variables
	 * 
	 * @param locals
	 *            local variables
	 */
	void SetLocals(Map<String, Object> locals) {
		this.localVars = locals;
	}

	/**
	 * set the if-condition
	 * 
	 * @param s
	 *            if-condition or null if no condition
	 */
	void SetCondition(String s) {
		this.sCondition = s;
	}

	/**
	 * set the conditional statements
	 * 
	 * @param s
	 *            statements, separated by a newline ('\n')
	 */
	void SetStatements(String s) {
		this.sStatements = s;
	}

	/**
	 * get the if-condition
	 * 
	 * @return condition string or null if none
	 */
	String GetCondition() {
		return this.sCondition;
	}

	/**
	 * get the conditional statements
	 * 
	 * @return Statement block
	 */
	String GetStatements() {
		return this.sStatements;
	}

	public void AddPreInput(IfBlock ib) {
		vSubIfs.add(ib);
	}

	/**
	 * Execute if-block.
	 * 
	 * @param eParse
	 *            Parser to use for execution
	 * @param parameters
	 *            Parameters to function
	 * @return return value of script function
	 * @throws ADLException
	 * @throws ADLException
	 *             On parsing errors
	 */
	public Object Handle(EquationParser eParse, Object[] parameters)
			throws ADLException {
		localVars.clear();
		List<Object> retvals = new LinkedList<Object>();

		if ((this.paramNames == null) && (parameters != null)
				&& (parameters.length != 0)) {
			throw new ADLException("Function expects 0 parameters. "
					+ parameters.length + " given!");
		}
		if ((parameters != null)
				&& (parameters.length != this.paramNames.length)) {
			throw new ADLException("Function expects " + this.paramNames.length
					+ " parameters. " + parameters.length + " given!");
		}

		if (this.paramNames != null) {
			if (parameters == null) {
				throw new ADLException("Function expects "
						+ this.paramNames.length + " parameters; 0 given!");
			}
			this.localVars = new HashMap<String, Object>();
			for (int i = 0; i < this.paramNames.length; i++) {
				this.localVars.put(this.paramNames[i], parameters[i]);
			}
		}

		Handle(eParse, retvals);

		if (retvals.size() != 0) {
			return retvals.get(0);
		} else {
			return null;
		}
	}

	/**
	 * Execute if-block.
	 * 
	 * @param eParse
	 *            Parser to use for execution
	 * @param returnVals
	 *            Reference to _empty_ LinkedList to fill with return value or
	 *            NULL if return values can be ignored
	 * @return true if if-block was executed, false if an elif or else block was
	 *         executed
	 * @throws ADLException
	 *             On parsing errors
	 */
	protected boolean Handle(EquationParser eParse, List<Object> returnVals)
			throws ADLException {
		String sCondition;
		Boolean bRes;
		String sStatements[];
		int i;
		Iterator<IfBlock> iIFs;
		IfBlock subIf;

		if (returnVals == null) {
			returnVals = new LinkedList<Object>();
		}

		do {
			sCondition = GetCondition();
			// do we have conditions?
			if (sCondition == null) {
				// nope, always execute the block
				bRes = true;
			} else {
				// yep, check them
				try {
					bRes = (Boolean) eParse.Parse(sCondition, localVars);
				} catch (ClassCastException e) {
					bRes = false;
				}
			}
			// conditions met?
			if (bRes) {
				// yep, execute statements
				sStatements = GetStatements().split("\n");
				for (i = 0; i < sStatements.length; i++) {
					// hack
					if (sStatements[i].startsWith("return ")) {
						Object retval = eParse.Parse(sStatements[i]
								.substring(7), localVars);
						if (returnVals != null) {
							returnVals.add(retval);
						}
						return true;
					} else {
						eParse.Parse(sStatements[i], localVars);
					}
				}

				iIFs = this.vSubIfs.iterator();
				while (iIFs.hasNext()) {
					subIf = iIFs.next();
					subIf.SetLocals(localVars);
					subIf.Handle(eParse, returnVals);
					if (returnVals.size() != 0) {
						return true;
					}
				}
			}
		} while (this.bWhile && bRes);

		if ((!this.bWhile) && (!bRes)) {
			Iterator<IfBlock> elifBlocks = this.elifBlocks.iterator();
			bRes = false;
			while (elifBlocks.hasNext() && (!bRes)) {
				IfBlock elifBlock = elifBlocks.next();
				elifBlock.SetLocals(localVars);
				bRes = elifBlock.Handle(eParse, returnVals);
				if (returnVals.size() != 0) {
					return true;
				}
			}
			if (!bRes) {
				if (this.elseIfBlock != null) {
					this.elseIfBlock.SetLocals(localVars);
					this.elseIfBlock.Handle(eParse, returnVals);
					if (returnVals.size() != 0) {
						return true;
					}
				}
			}
			return false;
		} else {
			return true;
		}
	}

	void SetWhile(boolean b) {
		this.bWhile = b;
	}

	void SetElseIfBlock(IfBlock myElseBlock) {
		this.elseIfBlock = myElseBlock;
	}

	void AddElifBlock(IfBlock myElifBlock) {
		this.elifBlocks.add(myElifBlock);
	}

	void setParamerets(String[] funcParams) {
		this.paramNames = funcParams;
	}
}
