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

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import net.morilib.sh.ShFile;

public final class FileTypeUtils {

	private static enum S1 { INIT, SHRP, BANG, PATH, ARGS, ARG2 }

	private FileTypeUtils() { }

	/**
	 * 
	 * @param input
	 * @return
	 * @throws IOException
	 */
	public static String[] parseShebang(
			InputStream input) throws IOException {
		StringBuffer b = null, d = null;
		String p = null, f = null;
		S1 stat = S1.INIT;
		int c;

		while((c = input.read()) >= 0) {
			switch(stat) {
			case INIT:
				if(c != '#')  return null;
				stat = S1.SHRP;
				break;
			case SHRP:
				if(c != '!')  return null;
				stat = S1.BANG;
				break;
			case BANG:
				if(c == '/') {
					b = new StringBuffer();
					d = new StringBuffer("/");
					stat = S1.PATH;
				} else if(c == '\n' || c == '\r') {
					return null;
				} else if(!Character.isWhitespace(c)) {
					return null;
				}
				break;
			case PATH:
				if(c > 127) {
					return null;
				} else if(c == '\n' || c == '\r') {
					if(b.length() == 0)  return null;
					return new String[] {
							d.toString(), b.toString(), ""
					};
				} else if(c == '/') {
					if(d.length() > 1)  d.append('/');
					d.append(b);
					b = new StringBuffer();
				} else if(Character.isWhitespace(c)) {
					p = d.toString();
					f = b.toString();
					b = new StringBuffer();
					stat = S1.ARGS;
				} else if(Character.isISOControl(c)) {
					return null;
				} else {
					b.append((char)c);
				}
				break;
			case ARGS:
				if(c > 127) {
					return null;
				} else if(c == '\n' || c == '\r') {
					return new String[] { p, f, "" };
				} else if(Character.isWhitespace(c)) {
					// do nothing
				} else if(Character.isISOControl(c)) {
					return null;
				} else {
					b.append((char)c);
					stat = S1.ARG2;
				}
				break;
			case ARG2:
				if(c > 127) {
					return null;
				} else if(c == '\n' || c == '\r') {
					return new String[] { p, f, b.toString() };
				} else if(Character.isISOControl(c)) {
					return null;
				} else {
					b.append((char)c);
				}
				break;
			}
		}
		return null;
	}

	/**
	 * 
	 * @param f
	 * @return
	 */
	public static String[] parseShebang(ShFile f) {
		InputStream ins = null;

		try {
			ins = f.getInputStream();
			return parseShebang(ins);
		} catch(IOException e) {
			return null;
		} finally {
			try {
				if(ins != null)  ins.close();
			} catch(IOException e) {
				return null;
			}
		}
	}

	/**
	 * 
	 * @param f
	 * @return
	 */
	public static String[] parseShebang(File f) {
		InputStream ins = null;

		try {
			ins = new FileInputStream(f);
			return parseShebang(ins);
		} catch(IOException e) {
			return null;
		} finally {
			try {
				if(ins != null)  ins.close();
			} catch(IOException e) {
				return null;
			}
		}
	}

	/**
	 * 
	 * @param f
	 * @return
	 */
	public static boolean guessWindowsExecutable(File f) {
		InputStream ins = null;

		try {
			ins = new FileInputStream(f);
			if(ins.read() != 'M')  return false;
			if(ins.read() != 'Z')  return false;
			return true;
		} catch(IOException e) {
			return false;
		} finally {
			try {
				if(ins != null)  ins.close();
			} catch(IOException e) {
				return false;
			}
		}
	}

	/**
	 * 
	 * @param f
	 * @return
	 */
	public static UnixExecutableType guessUnixExecutable(File f) {
		InputStream ins = null;

		try {
			ins = new FileInputStream(f);
			if(ins.read() != 127)  return UnixExecutableType.UNKNOWN;
			if(ins.read() != 'E')  return UnixExecutableType.UNKNOWN;
			if(ins.read() != 'L')  return UnixExecutableType.UNKNOWN;
			if(ins.read() != 'F')  return UnixExecutableType.UNKNOWN;
			return UnixExecutableType.ELF;
		} catch(IOException e) {
			return UnixExecutableType.ERROR;
		} finally {
			try {
				if(ins != null)  ins.close();
			} catch(IOException e) {
				return UnixExecutableType.ERROR;
			}
		}
	}

}
