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

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

import net.morilib.sh.ShEnvironment;
import net.morilib.sh.ShFileSystem;
import net.morilib.sh.ShProcess;

public class ShEcho implements ShProcess {

	private static enum S1 { INI, HEX, UNI, OCT }

	private boolean ishex(char c) {
		return ((c >= '0' && c <= '9') ||
				(c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
	}

	private boolean _print(PrintStream out, String s) {
		boolean x = true, n = false;
		S1 stat = S1.INI;
		int k = 0, c;

		outer: for(int i = 0; true; i++) {
			c = i < s.length() ? s.charAt(i) : -1;
			switch(stat) {
			case INI:
				if(c < 0) {
					break outer;
				} else if(x) {
					if(x = c != '\\')  out.print((char)c);
					break;
				} else if(c == 'a') {
					out.print('\u0007');
				} else if(c == 'b') {
					out.print('\u0008');
				} else if(c == 'c') {
					n = true;
				} else if(c == 'e' || c == 'E') {
					out.print('\u001b');
				} else if(c == 'f') {
					out.print('\u000c');
				} else if(c == 'n') {
					out.print('\n');
				} else if(c == 'r') {
					out.print('\r');
				} else if(c == 't') {
					out.print('\t');
				} else if(c == 'v') {
					out.print('\u000b');
				} else if(c == 'x') {
					stat = S1.HEX;  k = i;
				} else if(c == 'u') {
					stat = S1.UNI;  k = i;
				} else if(c == '0') {
					stat = S1.OCT;  k = ++i;
				} else if(c >= '1' && c <= '9') {
					stat = S1.OCT;  k = i;
				} else {
					if(c != '\\')  out.print('\\');
					out.print((char)c);
				}
				x = true;
				break;
			case HEX:
				if(!ishex((char)c)) {
					stat = S1.INI;  i--;
					out.print((char)Integer.parseInt(
							s.substring(k + 1, i + 1), 16));
				} else if(i >= k + 2) {
					stat = S1.INI;
					out.print((char)Integer.parseInt(
							s.substring(k + 1, i + 1), 16));
				}
				break;
			case UNI:
				if(!ishex((char)c)) {
					stat = S1.INI;  i--;
					out.print((char)Integer.parseInt(
							s.substring(k + 1, i + 1), 16));
				} else if(i >= k + 4) {
					stat = S1.INI;
					out.print((char)Integer.parseInt(
							s.substring(k + 1, i + 1), 16));
				}
				break;
			case OCT:
				if(c < '0' || c > '7') {
					stat = S1.INI;  i--;
					out.print((char)Integer.parseInt(
							s.substring(k, i + 1), 8));
				} else if(i >= k + 3) {
					stat = S1.INI;
					out.print((char)Integer.parseInt(
							s.substring(k, i + 1), 8));
				}
				break;
			}
		}
		return n;
	}

	public int main(ShEnvironment env,
			ShFileSystem fs,
			InputStream stdin,
			PrintStream stdout,
			PrintStream stderr,
			String... args) throws IOException {
		boolean n = false, e = false;
		String d = "";
		int k = 1;

		for(; k < args.length; k++) {
			if(args[k].equals("-n")) {
				n = true;
			} else if(args[k].equals("-e")) {
				e = true;
			} else if(args[k].equals("-E")) {
				e = false;
			} else {
				break;
			}
		}

		for(; k < args.length; k++) {
			stdout.print(d);
			if(e) {
				n = _print(stdout, args[k]) | n;
			} else {
				stdout.print(args[k]);
			}
			d = " ";
		}
		if(!n)  stdout.println();
		return 0;
	}

}
