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

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.morilib.sh.ShFile;
import net.morilib.sh.ShFileSystem;
import net.morilib.sh.ShProcess;
import net.morilib.sh.ShSecurityPolicy;
import net.morilib.sh.ShStat;

public class ShWindowsFileSystem implements ShFileSystem {

	private static final String SEP =
		System.getProperty("file.separator");
	private static final Pattern WPN =
		Pattern.compile("([A-Za-z]):[/\\\\]+(.*)");

	private final ShFile root = new ShFile() {

		public Collection<ShFile> getFiles() {
			List<ShFile> l = new ArrayList<ShFile>();
			SortedSet<String> z;
			ShFile f;

			z = new TreeSet<String>();
			for(File g : File.listRoots()) {
				z.add(g.toString().substring(0, 1));
			}

			for(String s : z) {
				f = new ShWindowsRealFile(ShWindowsFileSystem.this,
						s.toLowerCase(),
						new File(s + ":"));
				l.add(f);
			}
			return l;
		}

		public InputStream getInputStream() {
			return null;
		}

		public String getName() {
			return "";
		}

		public PrintStream getPrintStream(boolean append) {
			return null;
		}

		public boolean isDirectory() {
			return true;
		}

		public boolean isExecutable() {
			return true;
		}

		public boolean isExist() {
			return true;
		}

		public boolean isFile() {
			return false;
		}

		public long getLastModified() {
			return Long.MIN_VALUE;
		}

		public boolean isReadable() {
			return true;
		}

		public boolean isWritable() {
			return true;
		}

		public boolean isHidden() {
			return false;
		}

		public boolean isZeroFile() {
			return false;
		}

		public ShProcess getProcess(String... args) {
			return null;
		}

		public ShFile toAbsolute() {
			return this;
		}

		public boolean mkdir() {
			return false;
		}

		public boolean renameTo(ShFile f) {
			return false;
		}

		public String toString() {
			return "/";
		}

		public void setLastModified(long time) {
			// do nothing
		}

		public boolean rmdir() {
			return false;
		}

		public boolean deleteFile() {
			return false;
		}

		public ShStat getStat() {
			return new ShStat("/", 0, 0,
					ShStat.DIRECTORY | ShStat.EXECUTABLE |
					ShStat.READABLE  | ShStat.WRITABLE);
		}

	};

	private String home;
	private ShFile realcur;
	private ShSecurityPolicy security;

	/**
	 * 
	 * @param home
	 * @param security
	 */
	public ShWindowsFileSystem(String home,
			ShSecurityPolicy security) {
		this.home = home;
		this.security = security;
	}

	/**
	 * 
	 * @param wpath
	 * @return
	 */
	public static String toJshFilename(String wpath) {
		StringBuffer b = new StringBuffer("/");
		Matcher m = WPN.matcher(wpath);
		String[] a;

		if(!m.matches())  return "";
		b.append(m.group(1).toLowerCase()).append('/');
		a = m.group(2).split("[/\\\\]+");
		for(String s : a)  b.append('/').append(s);
		return b.toString();
	}

	public ShFile getCurrentDirectory() {
		return realcur;
	}

	public void setCurrentDirectory(ShFile s) {
		realcur = s;
	}

	public ShFile getFile(String s) {
		StringBuffer b = new StringBuffer();
		String[] a;
		String t, v;

		if(s == null)  throw new NullPointerException();
		if(s.length() == 0)  return getCurrentDirectory();
		if(s.matches("/+")) {
			return root;
		} else if(s.charAt(0) == '/') {
			t = s.replaceFirst("^/+", "");
			t = t.replaceFirst("/+$", "");
			t = t.replaceAll("^(/\\.\\.)+", "/");
			do {
				v = t;
				t = t.replaceAll("/[^/]+/\\.\\.", "");
			} while(!t.equals(v));
			t = t.replaceAll("(/\\.)+/", "/");
			t = t.replaceAll("(/\\.)+$", "");
			a = t.split("/+");
			v = s.replaceAll("/+", "/");
			if(a[0].length() != 1 ||
					a[0].charAt(0) < 'a' || a[0].charAt(0) > 'z') {
				return null;
			} else {
				b.append('/').append(a[0].toUpperCase()).append(':');
				for(int i = 1; i < a.length; i++) {
					b.append(SEP).append(a[i]);
				}
				return new ShWindowsRealFile(
						this, v, new File(b.toString()));
			}
		} else {
			return getFile(getCurrentDirectory(), s);
		}
	}

	public ShFile getFile(String dir, String name) {
		return getFile(dir + "/" + name);
	}

	public ShFile getFile(ShFile dir, String name) {
		return getFile(dir + "/" + name);
	}

	public ShFile getRoot() {
		return root;
	}

	public ShSecurityPolicy getSecurityPolicy() {
		return security;
	}

	public InputStream getProfile() {
		ShFile f;

		try {
			if((f = getFile(home, "jsh_profile.sh")).isFile() ||
					(f = getFile(home, "jsh_login.sh")).isFile() ||
					(f = getFile(home, "profile.sh")).isFile()) {
				return f.getInputStream();
			} else {
				return null;
			}
		} catch (IOException e) {
			return null;
		}
	}

	public InputStream getRc() {
		ShFile f;

		try {
			if((f = getFile(home, "jshrc.sh")).isFile()) {
				return f.getInputStream();
			} else {
				return null;
			}
		} catch (IOException e) {
			return null;
		}
	}

	public ShFile getHome() {
		return getFile(home);
	}

}
