/*
 * 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.sh;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.morilib.sh.misc.FileTypeUtils;
import net.morilib.sh.misc.XtraceStream;

public class ShTreeSimpleCommand implements ShTree {

	/**
	 * 
	 */
	public static final int ALL = 65535;

	/**
	 * 
	 */
	public static final int FUNCTION = 1;

	/**
	 * 
	 */
	public static final int BUILTIN = 2;

	/**
	 * 
	 */
	public static final int EXTERNAL = 4;

	/**
	 * 
	 */
	public static final int COMMAND = BUILTIN | EXTERNAL;

	private List<ShRedirector> types;
	private List<ShToken> files;
	private List<ShToken> line;
	private List<ShToken> binds;
	private int flags;

	public ShTreeSimpleCommand(List<ShToken> line,
			List<ShRedirector> types, List<ShToken> files,
			List<ShToken> binds, int flags) {
		this.types = new ArrayList<ShRedirector>(types);
		this.files = new ArrayList<ShToken>(files);
		this.line  = new ArrayList<ShToken>(line);
		this.binds = new ArrayList<ShToken>(binds);
		this.flags = flags;
	}

	public ShTreeSimpleCommand(List<ShToken> line,
			List<ShRedirector> types, List<ShToken> files, int flags) {
		this.types = new ArrayList<ShRedirector>(types);
		this.files = new ArrayList<ShToken>(files);
		this.line  = new ArrayList<ShToken>(line);
		this.binds = Collections.emptyList();
		this.flags = flags;
	}

	public List<String> substitute(ShEnvironment env,
			ShBuiltInCommands cmd, ShRuntime run,
			ShFileSystem fs, PrintStream err,
			XtraceStream p) throws IOException, ShSyntaxException {
		return ShTrees.substituteCommand(env, cmd, run, fs, err, p,
				line);
	}

	public int eval(ShEnvironment env,
			ShFileSystem fs,
			ShBuiltInCommands cmd,
			ShRuntime run,
			InputStream stdin,
			PrintStream out,
			PrintStream err,
			XtraceStream pr) throws IOException, ShSyntaxException {
		InputStream in  = stdin, sin;
		ShEnvironment e2;
		PrintStream[] a;
		List<String> l;
		ShProcess p;
		String v, n;
		String[] y;
		ShTree f;
		int z;

		// bind
		e2 = new ShFunctionEnvironment(env);
		for(ShToken t : binds) {
			t.bindVariable(e2, fs, run, err, pr);
		}

		// not execute
		if(env.isSet("noexec"))  return 0;

		// redirect
		a = new PrintStream[] { null, out, err };
		for(int i = 0; i < types.size(); i++) {
			in = ShFiles.redirect(env, fs, run, types.get(i),
					files.get(i), stdin, a, pr);
		}

		// substitute
		l = ShTrees.substituteCommand(env, cmd, run, fs, err, pr,
				line);
		if(env.isSet("xtrace") && pr != null) {
			v = ShTrees.substituteCase(env, run, fs, err, pr,
					new ShString(env.find("PS4")));
			pr.printPrompt(v);
			for(String s : l) {
				pr.print(s);
				pr.print(' ');
			}
			pr.println();
		}

		if(l.size() == 0) {
			return 0;
		} else if((f = env.findFunction(n = l.get(0))) != null &&
				(flags & FUNCTION) != 0) {
			e2.put("#", l.size() + "");
			for(int i = 0; i < l.size(); i++) {
				e2.put(i + "", l.get(i));
			}
			z = f.eval(e2, fs, cmd, run, in, a[1], a[2], pr);
		} else if((flags & BUILTIN) != 0 &&
				(p = cmd.find(n)) != null) {
			l.set(0, e2.find("0"));
			z = p.main(e2, fs, in, a[1], a[2], l.toArray(
					new String[0]));
		} else if((flags & EXTERNAL) != 0 &&
				(p = ShFiles.findCommand(env, fs, cmd, n)) != null) {
			z = p.main(e2, fs, in, a[1], a[2], l.toArray(
					new String[0]));
		} else if((y = FileTypeUtils.parseShebang(fs.getFile(n))) != null &&
				y[1] != null && y[1].equals("jsh")) {
			// bind
			e2 = new ShSubEnvironment(env.getEnvironment());
			for(ShToken t : binds) {
				t.bindVariable(e2, fs, run, err, pr);
			}

			// arguments
			e2  = new ShRootShellEnvironment(e2);
			e2.put("#", l.size() + "");
			for(int i = 1; i < l.size(); i++) {
				e2.put(i + "", l.get(i));
			}

			sin = fs.getFile(n).getInputStream();
			f   = ShParser.parse(sin, env.getCharset());
			z   = f.eval(e2, fs, cmd, run, in, out, err, pr);
		} else {
			throw new ShCommandNotFoundException(n);
		}

		if(a[1] != out && a[1] != err)  a[1].close();
		if(a[2] != out && a[2] != err)  a[2].close();
		env.put("?", z + "");
		return z;
	}

	public void compileInternally(ShTreeExpressionMachine.Builder b,
			Object brk, Object cnt) {
		b.add(this);
	}

	public String toString() {
		StringBuffer b = new StringBuffer();
		String d = "";

		for(ShToken l : line) {
			b.append(d).append(l);
			d = " ";
		}

		for(int i = 0; i < types.size(); i++) {
			b.append(' ').append(types.get(i));
			if(files.get(i) != null) {
				b.append(' ').append(files.get(i));
			}
		}
		return b.toString();
	}

}
