/*
 * 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.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.morilib.sh.ShEnvironment;
import net.morilib.sh.ShFile;
import net.morilib.sh.ShFileSystem;
import net.morilib.sh.ShProcess;
import net.morilib.sh.misc.IOs;
import net.morilib.unix.misc.OptionIterator;

public class ShCp implements ShProcess {

	private static final int FORCE = 1;
	private static final int RETRIVE_LINK_ALL = 2;
	private static final int INTERACTIVE = 4;
	private static final int RETRIVE_LINK = 8;
	private static final int PRESERVE = 16;
	private static final int RECURSIVE = 32;

	private boolean _md(PrintStream err, ShFile g) {
		if(!g.isExist() && !g.mkdir()) {
			err.print("cp: cannot make directory `");
			err.print(g.toString());
			err.println("'.");
			return false;
		}
		return true;
	}

	private int _cp1(ShFileSystem fs, BufferedReader rd,
			PrintStream err, int f, ShFile g,
			List<String> l) throws IOException {
		ShFile h;
		int x;

		for(String s : l) {
			h = fs.getFile(s);
			if((x = _cp2(fs, rd, err, f, h, g)) != 0)  return x;
		}
		return 0;
	}

	private int _cp2(ShFileSystem fs, BufferedReader rd,
			PrintStream err, int f, ShFile h,
			ShFile g) throws IOException {
		InputStream ins = null;
		PrintStream ous = null;
		ShFile j, k;
		String s;

		if(!h.isExist()) {
			err.print("cp: ");
			err.print(h.toString());
			err.println(" not found.");
			return 2;
		} else if(h.isFile() && !g.isExist()) {
			ins = h.getInputStream();
			ous = g.getPrintStream(false);
			IOs.copy(ins, ous);
			return 0;
		} else if(h.isFile() && g.isFile()) {
			if((f & INTERACTIVE) != 0) {
				err.print("cp: overwrite `");
				err.print(g.toString());
				err.print("'? ");
				if(!(s = rd.readLine()).equalsIgnoreCase("y") &&
						!s.equalsIgnoreCase("yes")) {
					return 0;
				}
			}
			ins = h.getInputStream();
			ous = g.getPrintStream(false);
			IOs.copy(ins, ous);
			return 0;
		} else if(h.isFile() && g.isDirectory()) {
			j = fs.getFile(g, h.getName());
			return _cp2(fs, rd, err, f, h, j);
		} else if(h.isDirectory() && (f & RECURSIVE) == 0) {
			err.print("cp: ");
			err.print(h.toString());
			err.println(" must be a file.");
			return 2;
		} else if(h.isDirectory() && !g.isExist()) {
			if(!_md(err, g))  return 2;
			for(ShFile x : h.getFiles()) {
				j = fs.getFile(g, x.getName());
				_cp2(fs, rd, err, f, x, j);
			}
			return 0;
		} else if(h.isDirectory() && g.isDirectory()) {
			_md(err, k = fs.getFile(g, h.getName()));
			for(ShFile x : h.getFiles()) {
				j = fs.getFile(k, x.getName());
				_cp2(fs, rd, err, f, x, j);
			}
			return 0;
		} else {
			err.print("cp: ");
			err.print(h.toString());
			err.println(" is an invalid object.");
			return 2;
		}
	}

	public int main(ShEnvironment env, ShFileSystem fs, InputStream in,
			PrintStream out, PrintStream err,
			String... args) throws IOException {
		List<String> l = new ArrayList<String>();
		String[] a = new String[args.length - 1];
		Iterator<String> t;
		BufferedReader rd;
		OptionIterator o;
		int f = 0, n;
		ShFile h, g;

		System.arraycopy(args, 1, a, 0, a.length);
		o  = new OptionIterator("fHiLpRr", a);
		while(o.hasNext()) {
			switch(o.nextChar()) {
			case 'f':  f |= FORCE;  break;
			case 'H':  f |= RETRIVE_LINK_ALL;  break;
			case 'i':  f |= INTERACTIVE;  break;
			case 'L':  f |= RETRIVE_LINK;  break;
			case 'p':  f |= PRESERVE;  break;
			case 'r':  case 'R':  f |= RECURSIVE;  break;
			default:
				err.print("cp: unrecognized option: ");
				err.println((char)o.getErrorOption());
				return 2;
			}
		}

		t = o.filenameIterator();
		while(t.hasNext())  l.add(t.next());
		if((n = l.size()) < 2) {
			err.println("cp: too few arguments");
			return 2;
		} else if(n == 2) {
			rd = new BufferedReader(
					new InputStreamReader(in, env.getCharset()));
			h = fs.getFile(l.get(0));
			g = fs.getFile(l.get(1));
			return _cp2(fs, rd, err, f, h, g);
		} else if((g = fs.getFile(l.get(n - 1))).isDirectory()) {
			rd = new BufferedReader(
					new InputStreamReader(in, env.getCharset()));
			return _cp1(fs, rd, err, f, g, l.subList(0, n - 1));
		} else {
			err.print("cp: ");
			err.print(l.get(n - 1));
			err.println(" must be a directory.");
			return 2;
		}
	}

}
