package sizzle.compiler;

import java.util.Arrays;
import java.util.HashMap;

import sizzle.types.SizzleFunction;
import sizzle.types.SizzleType;
import sizzle.types.SizzleVarargs;

public class FunctionTrie {
	@SuppressWarnings("rawtypes")
	private final HashMap trie;

	@SuppressWarnings("rawtypes")
	public FunctionTrie() {
		this.trie = new HashMap();
	}

	private SizzleFunction getFunction(final Object[] ids) {
		if (this.trie.containsKey(ids[0])) {
			if (ids[0].equals(""))
				return (SizzleFunction) this.trie.get("");
			else
				return ((FunctionTrie) this.trie.get(ids[0])).getFunction(Arrays.copyOfRange(ids, 1, ids.length));
		} else {
			for (final Object o : this.trie.keySet()) {
				if (o instanceof SizzleVarargs && ((SizzleVarargs) o).accepts((SizzleType) ids[0]))
					return ((FunctionTrie) this.trie.get(o)).getFunction();

				if (o instanceof SizzleType && ((SizzleType) o).accepts((SizzleType) ids[0])) {
					final SizzleFunction function = ((FunctionTrie) this.trie.get(o)).getFunction(Arrays.copyOfRange(ids, 1, ids.length));

					if (function != null)
						return function;
				}
			}
		}

		return null;
	}

	private SizzleFunction getFunction() {
		return (SizzleFunction) this.trie.get("");
	}

	public SizzleFunction getFunction(final String name, final SizzleType[] formalParameters) {
		final Object[] ids = new Object[formalParameters.length + 2];

		ids[0] = name;

		for (int i = 0; i < formalParameters.length; i++)
			ids[i + 1] = formalParameters[i];

		ids[ids.length - 1] = "";

		final SizzleFunction function = this.getFunction(ids);

		if (function == null)
			throw new TypeException("no such function " + name + "(" + Arrays.toString(formalParameters) + ")");

		return function;
	}

	public boolean hasFunction(final String name, final SizzleType[] formalParameters) {
		try {
			this.getFunction(name, formalParameters);

			return true;
		} catch (final TypeException e) {
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	private void addFunction(final Object[] ids, final SizzleFunction sizzleFunction) {
		if (this.trie.containsKey(ids[0])) {
			if (ids[0].equals("")) {
				throw new RuntimeException("function " + sizzleFunction + " already defined");
			} else {
				((FunctionTrie) this.trie.get(ids[0])).addFunction(Arrays.copyOfRange(ids, 1, ids.length), sizzleFunction);
			}
		} else {
			if (ids[0].equals("")) {
				this.trie.put("", sizzleFunction);
			} else {
				final FunctionTrie functionTrie = new FunctionTrie();
				functionTrie.addFunction(Arrays.copyOfRange(ids, 1, ids.length), sizzleFunction);
				this.trie.put(ids[0], functionTrie);
			}
		}
	}

	public void addFunction(final String name, final SizzleFunction sizzleFunction) {
		final SizzleType[] formalParameters = sizzleFunction.getFormalParameters();

		final Object[] ids = new Object[formalParameters.length + 2];

		ids[0] = name;

		for (int i = 0; i < formalParameters.length; i++)
			ids[i + 1] = formalParameters[i];

		ids[ids.length - 1] = "";

		this.addFunction(ids, sizzleFunction);
	}
}

@@ -876,16 +876,47 @@
 eType &&
+ !(ids%5B0%5D instanceof String) &&
  ((Sizzl

fix a cast exception for when source has invalid arguments to a function call - it now gives the right error indicating no such function exists