/*
 * 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 ShMv implements ShProcess {

	private static final int BACKUP = 1;
	private static final int FORCE = 2;
	private static final int INTERACTIVE = 4;
//	private static final int UPDATE = 8;
//	private static final int VERBOSE = 16;

	private int _mv1(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 = _mv2(fs, rd, err, f, h, g)) != 0)  return x;
		}
		return 0;
	}

	private boolean _ren(ShFileSystem fs, int f, ShFile h,
			ShFile g) throws IOException {
		InputStream ins = null;
		PrintStream ous = null;
		ShFile j;

		j = fs.getFile(g + "~");
		if(g.isExist() && g.isFile() && !j.isExist() &&
				(f & BACKUP) != 0) {
			try {
				ins = h.getInputStream();
				ous = j.getPrintStream(false);
				IOs.copy(ins, ous);
			} finally {
				if(ins != null)  ins.close();
				if(ous != null)  ous.close();
			}
		}
		return h.renameTo(g);
	}

	private void _nrv(PrintStream err, ShFile h, ShFile g) {
		err.print("mv: cannot remove `");
		err.print(h.toString());
		err.print("' to `");
		err.print(g.toString());
		err.println("'");
	}

	private int _mv2(ShFileSystem fs, BufferedReader rd,
			PrintStream err, int f, ShFile h,
			ShFile g) throws IOException {
		String s;
		ShFile j;

		if(!h.isExist()) {
			err.print("mv: ");
			err.print(h.toString());
			err.println(" not found.");
			return 2;
		} else if(!g.isExist()) {
			if(!_ren(fs, f, h, g)) {
				_nrv(err, h, g);
				return 2;
			}
		} else if(g.isFile()) {
			if((f & INTERACTIVE) != 0) {
				err.print("mv: overwrite `");
				err.print(g.toString());
				err.print("'? ");
				if(!(s = rd.readLine()).equalsIgnoreCase("y") &&
						!s.equalsIgnoreCase("yes")) {
					return 0;
				}
			}

			if(!_ren(fs, f, h, g)) {
				_nrv(err, h, g);
				return 2;
			}
		} else if(g.isDirectory()) {
			j = fs.getFile(g, h.getName());
			if(!_ren(fs, f, h, j)) {
				_nrv(err, h, j);
				return 2;
			}
		} else {
			err.print("mv: ");
			err.print(h.toString());
			err.println(" is an invalid object.");
			return 2;
		}
		return 0;
	}

	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("bfiuv", a);
		rd = new BufferedReader(
				new InputStreamReader(in, env.getCharset()));
		while(o.hasNext()) {
			switch(o.nextChar()) {
			case 'b':  f |= BACKUP;  break;
			case 'f':  f |= FORCE;  break;
			case 'i':  f |= INTERACTIVE;  break;
			case 'u':  break;
			case 'v':  break;
			default:
				err.print("mv: 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("mv: too few arguments");
			return 2;
		} else if(n == 2) {
			h = fs.getFile(l.get(0));
			g = fs.getFile(l.get(1));
			return _mv2(fs, rd, err, f, h, g);
		} else if((g = fs.getFile(l.get(n - 1))).isDirectory()) {
			return _mv1(fs, rd, err, f, g, l.subList(0, n - 1));
		} else {
			err.print("mv: ");
			err.print(l.get(n - 1));
			err.println(" must be a directory.");
			return 2;
		}
	}

}
