/*
 * 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.PrintStream;
import java.util.ArrayList;
import java.util.List;

import net.morilib.sh.misc.XtraceStream;

public final class ShTrees {

	private static enum S1 { INIT, ESC1, SQUT, DQUT, ESC2 }

	private ShTrees() { }

	//
	public static String removeQuote(String s) {
		StringBuffer b = new StringBuffer();
		S1 stat = S1.INIT;
		int c;

		for(int i = 0; i < s.length(); i++) {
			c = s.charAt(i);
			switch(stat) {
			case INIT:
				if(c == '\'') {
					stat = S1.SQUT;
				} else if(c == '\"') {
					stat = S1.DQUT;
				} else if(c == '\\') {
					stat = S1.ESC1;
				} else {
					b.append((char)c);
				}
				break;
			case ESC1:
				b.append((char)c);
				stat = S1.INIT;
				break;
			case SQUT:
				if(c == '\'') {
					stat = S1.INIT;
				} else {
					b.append((char)c);
				}
				break;
			case DQUT:
				if(c == '\"') {
					stat = S1.INIT;
				} else if(c == '\\') {
					stat = S1.ESC2;
				} else {
					b.append((char)c);
				}
				break;
			case ESC2:
				b.append((char)c);
				stat = S1.DQUT;
				break;
			}
		}

		if(stat.equals(S1.ESC1))  b.append('\\');
		return b.toString();
	}

	/**
	 * 
	 * @param env
	 * @param run
	 * @param fs
	 * @param line
	 * @return
	 * @throws IOException
	 * @throws ShSyntaxException
	 */
	public static String substituteCase(
			ShEnvironment env, ShRuntime run,
			ShFileSystem fs, PrintStream err, XtraceStream p,
			ShToken line) throws IOException, ShSyntaxException {
		StringBuffer b = new StringBuffer();
		List<ShToken> r, a;
		String d = "";

		r = new ArrayList<ShToken>();
		r.add(line);

		// replace tilde
		for(int i = 0; i < r.size(); i++) {
			r.set(i, r.get(i).replaceTilde(env));
		}

		// replace parameter
		a = new ArrayList<ShToken>();
		for(int i = 0; i < r.size(); i++) {
			a.addAll(r.get(i).replaceParameter(env, err));
		}
		r = a;

		// replace commands
		for(int i = 0; i < r.size(); i++) {
			r.set(i, r.get(i).replaceCommand(env, fs, run, p));
		}

		// replace arithmetic
		for(int i = 0; i < r.size(); i++) {
			r.set(i, r.get(i).replaceArithmetic(env));
		}

		for(ShToken x : r) {
			b.append(d).append(x.toString());
			d = " ";
		}
		return b.toString();
	}

	//
	static List<String> substitute1(
			ShEnvironment env, ShRuntime run,
			ShFileSystem fs, PrintStream err, XtraceStream p,
			ShToken line) throws IOException, ShSyntaxException {
		List<ShToken> r, a;
		List<String> s;
		List<String> t;
		List<ShFile> f; 

		// replace bracket
		r = new ArrayList<ShToken>(line.replaceBracket());

		// replace tilde
		for(int i = 0; i < r.size(); i++) {
			r.set(i, r.get(i).replaceTilde(env));
		}

		// replace parameter
		a = new ArrayList<ShToken>();
		for(int i = 0; i < r.size(); i++) {
			a.addAll(r.get(i).replaceParameter(env, err));
		}
		r = a;

		// replace commands
		for(int i = 0; i < r.size(); i++) {
			r.set(i, r.get(i).replaceCommand(env, fs, run, p));
		}

		// replace arithmetic
		for(int i = 0; i < r.size(); i++) {
			r.set(i, r.get(i).replaceArithmetic(env));
		}

		// replace words
		s = new ArrayList<String>();
		for(int i = 0; i < r.size(); i++) {
			if(r.get(i).toString().equals("")) {
				s.add("");
			} else {
				s.addAll(r.get(i).splitWord(env.find("IFS")));
			}
		}

		// replace paths
		t = new ArrayList<String>();
		for(int i = 0; i < s.size(); i++) {
			if(!env.isSet("noglob") && ShToken.isWildcard(s.get(i))) {
				f = ShFiles.glob(s.get(i), fs);
				if(f.isEmpty()) {
					t.add(removeQuote(s.get(i)));
				} else {
					for(ShFile x : f)  t.add(x.getName());
				}
			} else {
				t.add(removeQuote(s.get(i)));
			}
		}
		return t;
	}

	/**
	 * 
	 * @param env
	 * @param run
	 * @param fs
	 * @param token
	 * @return
	 * @throws IOException
	 * @throws ShSyntaxException
	 */
	public static String substitute(ShEnvironment env,
			ShRuntime run, ShFileSystem fs, PrintStream err,
			XtraceStream p,
			ShToken token) throws IOException, ShSyntaxException {
		List<String> l = substitute1(env, run, fs, err, p, token);
		StringBuffer b = new StringBuffer();
		String d = "";

		for(String s : l) {
			b.append(d).append(s);
			d = " ";
		}
		return b.toString();
	}

	/**
	 * 
	 * @param env
	 * @param run
	 * @param fs
	 * @param line
	 * @return
	 * @throws IOException
	 * @throws ShSyntaxException
	 */
	public static List<String> substitute(ShEnvironment env,
			ShRuntime run, ShFileSystem fs,
			PrintStream err, XtraceStream p,
			List<ShToken> line) throws IOException, ShSyntaxException {
		List<String> f = new ArrayList<String>();

		// replace a command line
		for(ShToken t : line) {
			f.addAll(substitute1(env, run, fs, err, p, t));
		}
		return f;
	}

	/**
	 * 
	 * @param env
	 * @param cmd
	 * @param run
	 * @param fs
	 * @param line
	 * @return
	 * @throws IOException
	 * @throws ShSyntaxException
	 */
	public static List<String> substituteCommand(ShEnvironment env,
			ShBuiltInCommands cmd, ShRuntime run, ShFileSystem fs,
			PrintStream err, XtraceStream p,
			List<ShToken> line) throws IOException, ShSyntaxException {
		List<String> f = new ArrayList<String>();
//		ShFile x;

		// replace a command line
		for(ShToken t : line) {
			f.addAll(substitute1(env, run, fs, err, p, t));
		}

//		if(f.size() > 0 && cmd.find(f.get(0)) == null) {
//			x = ShFiles.searchPath(env, fs, f.get(0));
//			if(x != null)  f.set(0, x.toString());
//		}
		return f;
	}

	/**
	 * 
	 * @param t
	 * @return
	 */
	public static ShTree compile(ShTree t) {
		ShTreeExpressionMachine.Builder b;

		b = new ShTreeExpressionMachine.Builder();
		t.compileInternally(b, null, null);
		return b.get();
	}

}
