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

import net.morilib.diff.Change;
import net.morilib.diff.Diff;
import net.morilib.sh.ShEnvironment;
import net.morilib.sh.ShFileSystem;
import net.morilib.sh.ShProcess;

/**
 *
 *
 * @author MORIGUCHI, Yuichiro 2013/06/09
 */
public class ShDiff implements ShProcess {

	private static abstract class Ot {

		abstract void out(List<String> l1, List<String> l2,
				List<Change<String>> c, PrintStream out);

	}

	private static class OtD extends Ot {

		/* (non-Javadoc)
		 * @see net.morilib.sh.buildin.ShDiff.Ot#out(java.util.List, java.util.List, java.util.List, java.io.PrintStream)
		 */
		@Override
		void out(List<String> l1, List<String> l2,
				List<Change<String>> c, PrintStream out) {
			for(Change<String> a : c) {
				switch(a.getOperation()) {
				case Change.INSERTED:
					out.format("%da%s\n", a.getBeginIndexA(), _rb(a));
					for(String s : a.getAfter()) {
						out.format("> %s\n", s);
					}
					break;
				case Change.DELETED:
					out.format("%sd%d\n", _ra(a), a.getBeginIndexB());
					for(String s : a.getBefore()) {
						out.format("< %s\n", s);
					}
					break;
				case Change.CHANGED:
					out.format("%sc%s\n", _ra(a), _rb(a));
					for(String s : a.getBefore()) {
						out.format("< %s\n", s);
					}
					out.println("---");
					for(String s : a.getAfter()) {
						out.format("> %s\n", s);
					}
					break;
				default:  throw new RuntimeException();
				}
			}
		}

	}

	private static final Ot DFT = new OtD();

	private static String _ra(Change<?> c) {
		if(c.getBeginIndexA() == c.getEndIndexA()) {
			return Integer.toString(c.getBeginIndexA());
		} else {
			return String.format("%s,%s",
					c.getBeginIndexA(), c.getEndIndexA());
		}
	}

	private static String _rb(Change<?> c) {
		if(c.getBeginIndexB() == c.getEndIndexB()) {
			return Integer.toString(c.getBeginIndexB());
		} else {
			return String.format("%s,%s",
					c.getBeginIndexB(), c.getEndIndexB());
		}
	}

	List<String> _readall(BufferedReader rd,
			String eof) throws IOException {
		List<String> l = new ArrayList<String>();
		String s;

		while((s = rd.readLine()) != null && !s.equals(eof)) {
			l.add(s);
		}
		return l;
	}

	void _diff(List<String> l, List<String> m, PrintStream out,
			Ot o) throws IOException {
		List<Change<String>> c;

		c = Diff.diff(l, m);
		o.out(l, m, c, out);
	}

	/* (non-Javadoc)
	 * @see net.morilib.sh.ShProcess#main(net.morilib.sh.ShEnvironment, net.morilib.sh.ShFileSystem, java.io.InputStream, java.io.PrintStream, java.io.PrintStream, java.lang.String[])
	 */
	public int main(ShEnvironment env, ShFileSystem fs, InputStream in,
			PrintStream out, PrintStream err,
			String... args) throws IOException {
		BufferedReader rd = null;
		List<String> l, m;
		String s = null;
		int k = 1;

		for(; k < args.length; k++) {
			if(args[k].equals("-c") || args[k].equals("--context")) {
				err.println("diff: context mode is not supported");
				return 2;
			} else if(args[k].equals("-u") ||
					args[k].equals("--unified")) {
				err.println("diff: unified mode is not supported");
				return 2;
			} else if(args[k].equals("-e") || args[k].equals("--ed")) {
				err.println("diff: ed mode is not supported");
				return 2;
			} else if(args[k].startsWith("--haruka=") &&
					args[k].length() > 9) {
				s = args[k].substring(9);
			} else if(args[k].equals("--")) {
				k++;
				break;
			} else if(args[k].equals("-") ||
					!args[k].startsWith("-")) {
				break;
			} else {
				err.println("diff: invaild arguments");
				return 2;
			}
		}

		if(s == null) {
			rd = new BufferedReader(
					new InputStreamReader(in, env.getCharset()));
			l  = _readall(rd, s);
			m  = _readall(rd, null);
			_diff(l, m, out, DFT);
			return 0;
		} else if(args.length - k >= 1) {
			try {
				rd = new BufferedReader(new InputStreamReader(
						fs.getFile(args[k++]).getInputStream(),
						env.getCharset()));
				l  = _readall(rd, null);
				rd.close();
				rd = new BufferedReader(new InputStreamReader(
						fs.getFile(args[k]).getInputStream(),
						env.getCharset()));
				m  = _readall(rd, null);
				_diff(l, m, out, DFT);
				return 0;
			} finally {
				if(rd != null)  rd.close();
			}
		} else {
			err.println("diff: files must be specified");
			return 2;
		}
	}

}
