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

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.StringReader;

import net.morilib.sed.Sed;
import net.morilib.sed.SedPatternEngine;
import net.morilib.sh.ShFileSystem;
import net.morilib.sh.ShFileSystemStub;

import junit.framework.TestCase;

public class SedTest extends TestCase {

	static final SedPatternEngine E = SedBasicRegexEngine.getInstance();

	public static void eqs(String cmd, String s, String t,
			int cd) throws Exception {
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		StringReader r  = new StringReader(s);
		PrintStream  w  = new PrintStream(os);
		ShFileSystem fs = new ShFileSystemStub();

		assertEquals(cd, Sed.execute(fs, cmd, r, w, E, true));
		assertEquals(t, new String(os.toByteArray()).replaceAll("\r\n", "\n"));
	}

	public static void eqn(String cmd, String s, String t,
			int cd) throws Exception {
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		StringReader r  = new StringReader(s);
		PrintStream  w  = new PrintStream(os);
		ShFileSystem fs = new ShFileSystemStub();

		assertEquals(cd, Sed.execute(fs, cmd, r, w, E, false));
		assertEquals(t, new String(os.toByteArray()).replaceAll("\r\n", "\n"));
	}

	public void testL() throws Exception {
		eqn("1{N;l}", "\u0010\u0007\u0008\u0009\u000b\n\\\n", "\\020\\a\\b\\t\\v\\n\\$\n", 0);
	}

	public void testRange2() throws Exception {
		eqn("/b/Ip", "a\nB\nb\nd\ne\nf\n", "B\n", 0);
		eqn("/b/I,/d/Ip", "a\nB\nC\nD\ne\nf\n", "B\nC\nD\n", 0);
		eqn("g/b/Ip", "a\nB\nb\nD\neb\nf\n", "B\nb\neb\n", 0);
		eqn("g/b/Mp", "a\nB\nb\nD\neb\nf\n", "b\n", 0);
		eqn("g/b/IMp", "a\nB\nb\nD\neb\nf\n", "B\nb\n", 0);
		eqn("/b/,+2p", "a\nb\nc\nd\ne\nf\n", "b\nc\nd\n", 0);
		eqn("2,+2p", "a\nb\nc\nd\ne\nf\n", "b\nc\nd\n", 0);
		eqn("2~2p", "a\nb\nc\nd\ne\nf\n", "b\nd\nf\n", 0);
		eqn("1~2p", "a\nb\nc\nd\ne\nf\n", "a\nc\ne\n", 0);
		eqn("1~3p", "a\nb\nc\nd\ne\nf\n", "a\nd\n", 0);
		eqn("/b/,+2!p", "a\nb\nc\nd\ne\nf\n", "a\ne\nf\n", 0);
		eqn("1~2!p", "a\nb\nc\nd\ne\nf\n", "b\nd\nf\n", 0);
		eqn("1~3!p", "a\nb\nc\nd\ne\nf\n", "b\nc\ne\nf\n", 0);
	}

	public void testRange() throws Exception {
		eqs("2q", "a\nb\nc\nd\n", "a\nb\n", 0);
		eqn("2,4p", "a\nb\nc\nd\ne\nf\n", "b\nc\nd\n", 0);
		eqn("/b/p", "a\nb\nc\nbx\nby\nf\n", "b\n", 0);
		eqn("/b/,/d/p", "a\nb\nc\nd\nbx\nby\ndd\nf\n", "b\nc\nd\n", 0);
		eqn("/b/,/d/!p", "a\nb\nc\nd\nf\n", "a\nf\n", 0);
		eqn("g/b/p", "a\nb\nc\nbx\nby\nf\n", "b\nbx\nby\n", 0);
		eqn("$p", "a\nb\nc\nbx\nby\nf\n", "f\n", 0);
		eqn("3,$p", "a\nb\nc\nbx\nby\nf\n", "c\nbx\nby\nf\n", 0);
		eqn("/b/,/f/{/c/,/e/p}", "a\nb\nc\nd\ne\nf\ncx\ndx\nex\n", "c\nd\ne\n", 0);
		eqn("/b/,/f/{/c/,/e/p}", "cx\ndx\nex\na\nb\nc\nd\ne\nf\n", "c\nd\ne\n", 0);
		eqn("2,4{3p}", "a\nb\nc\nd\ne\nf\n", "c\n", 0);
		eqn("2,4{1p}", "a\nb\nc\nd\ne\nf\n", "", 0);
	}

	public void testSubst1() throws Exception {
		eqs("s/a/b/", "aaa\n", "baa\n", 0);
		eqs("s/a/b/g", "aaa\n", "bbb\n", 0);
		eqs("s/a/b/2", "aaa\n", "aba\n", 0);
		eqs("s/a/b/3", "aaa\n", "aab\n", 0);
		eqs("s/aa*/b/", "caaac\n", "cbc\n", 0);
		eqs("s/a/b/i", "AAA\n", "bAA\n", 0);
		eqs("s/a/b/", "AAA\n", "AAA\n", 0);
	}

	public void testDelete1() throws Exception {
		eqs("1d", "a\nb\nc\n", "b\nc\n", 0);
		eqs("1{d;s/a/b/}", "a\nb\nc\n", "b\nc\n", 0);
	}

	public void testPrint() throws Exception {
		eqn("2p", "a\nb\nc\n", "b\n", 0);
		eqn("2{s/b/B/;p}", "a\nb\nc\n", "B\n", 0);
		eqs("2{p;s/b/B/}", "a\nb\nc\n", "a\nb\nB\nc\n", 0);
	}

	public void testNext() throws Exception {
		eqn("2n;p", "a\nb\nc\n", "a\nc\n", 0);
		eqs("2n", "a\nb\nc\n", "a\nb\nc\n", 0);
		eqs("2n;s//--/", "a\nb\nc\n", "--a\nb\n--c\n", 0);
		eqs("2{n;n};s//--/", "a\nb\nc\nd\n", "--a\nb\nc\n--d\n", 0);
		eqs("/a/,/b/{n;s//--/}", "0\na\n1\nb\n2\n", "0\na\n--1\nb\n--2\n", 0);
	}

	public void testY() throws Exception {
		eqs("y/abc/def/", "a\nb\nc\nz\n", "d\ne\nf\nz\n", 0);
		eqs("y/a-z/A-Z/", "a\nb\nc\n0\n", "A\nB\nC\n0\n", 0);
	}

	public void testAppend() throws Exception {
		eqs("2a\\\nc", "a\nb\nd\n", "a\nb\nc\nd\n", 0);
		eqs("2,3a\\\nc", "a\nb\nd\n", "a\nb\nc\nd\nc\n", 0);
		eqs("2a\\\nc\ns/c/C/", "a\nb\nd\n", "a\nb\nc\nd\n", 0);
	}

	public void testInsert() throws Exception {
		eqs("2i\\\nc", "a\nb\nd\n", "a\nc\nb\nd\n", 0);
		eqs("2,3i\\\nc", "a\nb\nd\n", "a\nc\nb\nc\nd\n", 0);
		eqs("2i\\\nc\ns/c/C/", "a\nb\nd\n", "a\nc\nb\nd\n", 0);
	}

	public void testChange() throws Exception {
		eqs("2c\\\nc", "a\nb\nd\n", "a\nc\nd\n", 0);
		eqs("2,4c\\\nf", "a\nb\nc\nd\ne\n", "a\nf\ne\n", 0);
		eqs("2,4c\\\nf\ns/f/F/", "a\nb\nc\nd\ne\n", "a\nf\ne\n", 0);
		eqs("2,4{c\\\nf\n}", "a\nb\nc\nd\ne\n", "a\nf\nf\nf\ne\n", 0);
		eqs("2!c\\\nf", "a\nb\nd\n", "f\nb\nf\n", 0);
		eqs("2,$c\\\nf", "a\nb\nc\nd\ne\n", "a\nf\n", 0);
		eqs("/b/c\\\nf", "a\nb\nc\nd\ne\n", "a\nf\nc\nd\ne\n", 0);
		eqs("/b/,/d/c\\\nf", "a\nb\nc\nd\ne\n", "a\nf\ne\n", 0);
		eqs("1,/d/c\\\nf", "a\nb\nc\nd\ne\n", "f\ne\n", 0);
		eqs("/b/,$c\\\nf", "a\nb\nc\nd\ne\n", "a\nf\n", 0);
	}

	public void testPrineLineNo() throws Exception {
		eqn("=", "a\nb\nc\n", "1\n2\n3\n", 0);
	}

	public void testNextS() throws Exception {
		eqs("2N", "a\nb\nc\nd\n", "a\nb\nc\nd\n", 0);
		eqn("2N;p", "a\nb\nc\nd\n", "a\nb\nc\nd\n", 0);
		eqs("2N;s/\\(.*\\)/-\\1-/", "a\nb\nc\nd\n", "-a-\n-b\nc-\n-d-\n", 0);
		eqs("2N;s/\n//", "a\nb\nc\nd\n", "a\nbc\nd\n", 0);
		eqs("N;N;s/\n/,/g", "a\nb\nc\nd\ne\nf\ng\n", "a,b,c\nd,e,f\ng\n", 0);
		eqs("/b/{N;N;s/\n/,/g}", "a\nb\nc\nd\ne\nf\ng\n", "a\nb,c,d\ne\nf\ng\n", 0);
	}

	public void testPrintS() throws Exception {
		eqn("2N;P", "a\nb\nc\nd\n", "a\nb\nd\n", 0);
		eqn("N;2p", "a\n\n", "a\n\n", 0);
	}

	public void testPrintD() throws Exception {
		eqs("2N;D", "a\nb\nc\nd\n", "c\n", 0);
		eqs("2N;D;s/c/C/", "a\nb\nc\nd\n", "C\n", 0);
	}

	public void testHold() throws Exception {
		eqs("2h;3g", "a\nb\nc\nd\n", "a\nb\nb\nd\n", 0);
	}

	public void testHoldS() throws Exception {
		eqn("1,3H;3g;3p", "a\nb\nc\nd\n", "a\nb\nc\n", 0);
		eqn("1,3H;3g;3P", "a\nb\nc\nd\n", "a\n", 0);
		eqn("1,3H;4G;4p", "a\nb\nc\nd\n", "d\na\nb\nc\n", 0);
		eqn("1,3H;3g;3p", "a\n\nc\nd\n", "a\n\nc\n", 0);
	}

	public void testExchange() throws Exception {
		eqs("x", "a\nb\nc\nd\n", "\na\nb\nc\n", 0);
	}

	public void testBranch() throws Exception {
		eqn("bl;y/a-z/A-Z/;:l;p", "a\nb\nc\n", "a\nb\nc\n", 0);
		eqn("bl;y/a-z/A-Z/;:m;p;bend;=;:l;bm", "a\nb\nc\n", "a\nb\nc\n", 0);
		eqn("s/[a-z]/A/;tl;=;bm;:l;p", "a\nb\n1\n", "A\nA\n3\n", 0);
	}

	public void testSample1() throws Exception {
		eqn("1!G;$p;h", "a\nb\nc\nd\n", "d\nc\nb\na\n", 0);
		eqn("1!G;$p;h", "a\n", "a\n", 0);
		eqn("1!G;$p;h", "", "", 0);
	}

}
