/*
 * Copyright 2013 Yuichiro Moriguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.morilib.awk.nano.expr;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import net.morilib.awk.nano.AwkCastException;
import net.morilib.awk.nano.AwkLocation;
import net.morilib.awk.nano.AwkUnboundException;
import net.morilib.awk.nano.io.AwkFiles;
import net.morilib.awk.nano.namespace.AwkNamespace;
import net.morilib.awk.nano.value.AwkClass;
import net.morilib.awk.nano.value.AwkFunction;
import net.morilib.awk.nano.value.AwkValue;

/**
 * 関数呼出の中間表現です。
 * 
 * @author MORIGUCHI, Yuichiro 2013/03
 */
public class AwkApplier extends AwkExpression {

	private AwkExpression function;
	private AwkExpression[] args;

	/**
	 * 中間表現を生成します。
	 * 
	 * @param function 関数
	 * @param args     引数の中間表現
	 */
	public AwkApplier(AwkExpression function,
			Collection<AwkExpression> args) {
		this.function = function;
		this.args     = args.toArray(new AwkExpression[0]);
	}

	private AwkNamespace searchns(AwkNamespace t, AwkLocation m) {
		AwkNamespace v;
		AwkValue w;

		for(int i = 0; i < m.sizeOfPath() - 1; i++) {
			v = t.referNamespace(m.getPathOf(i));
			if((w = t.find(m.getPathOf(i))) != null &&
					w.getNamespace() != null) {
				t = w.getNamespace();
			} else if(v != null) {
				t = v;
			} else {
				return null;
			}
		}
		return t;
	}

	/* (non-Javadoc)
	 * @see net.morilib.awk.expr.AwkExpression#eval(net.morilib.awk.namespace.AwkNamespace, net.morilib.awk.io.AwkFiles)
	 */
	public AwkValue eval(AwkNamespace s, AwkFiles o) {
		List<AwkValue> l = new ArrayList<AwkValue>();
		AwkNamespace t = null;
		AwkLocation m;
		AwkFunction g;
		AwkValue fn, f = null;

		for(AwkExpression e : args)  l.add(e.eval(s, o));
		if(function instanceof AwkReferVariable) {
			m = ((AwkReferVariable)function).getLocation();
//			t = m.isRoot() ? s.getRoot() : s;
			if(!m.isRoot() && (t = searchns(s, m)) != null) {
				f = t.findFunction(m.getName());
				if(f == null)  f = t.find(m.getName());
			}

			if(f == null) {
				for(t = s; !t.isRoot(); t = t.getRoot());
				if((t = searchns(t, m)) != null) {
					f = t.findFunction(m.getName());
				}
			}

			if(f == null) {
				throw new AwkUnboundException(
						"unbound function " + m.toString());
			} else if(!(f instanceof AwkFunction)) {
				throw new AwkCastException("function required");
			}

			g = (AwkFunction)f;
			if(g.getName().equals("new") && !t.isRoot()) {
				t = t.newInstance();
				g.init(t, o, l);
				return new AwkClass(t);
			} else {
				return g.apply(t, o, l);
			}
		} else if((fn = function.eval(s, o)) instanceof AwkFunction) {
			g = (AwkFunction)fn;
			return g.apply(g.getEnvironment(), o, l);
		} else {
			throw new AwkCastException("function required");
		}
	}

}
