/*
 * 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.PushbackReader;
import java.io.StringReader;

import net.morilib.sed.SedCommand;
import net.morilib.sed.SedCommandLine;
import net.morilib.sed.SedLineMatcher;
import net.morilib.sed.SedLineOption;
import net.morilib.sed.SedParser;
import net.morilib.sed.SedSubstituteFlags;
import net.morilib.sed.SedSyntaxException;
import net.morilib.sed.cmd.SedACommand;
import net.morilib.sed.cmd.SedBCommand;
import net.morilib.sed.cmd.SedCCommand;
import net.morilib.sed.cmd.SedCommands;
import net.morilib.sed.cmd.SedCompositeCommand;
import net.morilib.sed.cmd.SedICommand;
import net.morilib.sed.cmd.SedLabel;
import net.morilib.sed.cmd.SedQCommand;
import net.morilib.sed.cmd.SedRCommand;
import net.morilib.sed.cmd.SedSCommand;
import net.morilib.sed.cmd.SedTCommand;
import net.morilib.sed.cmd.SedWCommand;
import net.morilib.sed.cmd.SedYCommand;
import net.morilib.sed.line.SedLastLineMatcher;
import net.morilib.sed.line.SedNumberLineMatcher;
import net.morilib.sed.line.SedPatternLineMatcher;


import junit.framework.TestCase;

public class SedParserTest extends TestCase {

	static PushbackReader gpr(String s) {
		return new PushbackReader(new StringReader(s));
	}

	static void eqgetstr(String s, int d, String t) throws Exception {
		assertEquals(SedParser.getstr(gpr(s), d), t);
	}

	static void eqgetstre(String s, int d) throws Exception {
		try {
			SedParser.getstr(gpr(s), d);  fail();
		} catch(SedSyntaxException e) {}
	}

	static void eqnums(String s, long t) throws Exception {
		assertEquals(SedParser.nums(gpr(s)), t);
	}

	static void eqnumse(String s) throws Exception {
		try {
			SedParser.nums(gpr(s));  fail();
		} catch(SedSyntaxException e) {}
	}

	static void eqline(String s, SedLineMatcher t) throws Exception {
		assertEquals(SedParser.line(gpr(s)), t);
	}

	static void nlline(String s) throws Exception {
		assertNull(SedParser.line(gpr(s)));
	}

	static void eqlinee(String s) throws Exception {
		try {
			SedParser.line(gpr(s));  fail();
		} catch(SedSyntaxException e) {}
	}

	static void eqlines(String s, SedLineOption t) throws Exception {
		assertTrue("<" + t.toString() + "> but <" + s.toString() + ">",
				SedParser.parseLines(gpr(s)).isEquivalent(t));
	}

	static void eqlinese(String s) throws Exception {
		try {
			SedParser.parseLines(gpr(s));  fail();
		} catch(SedSyntaxException e) {}
	}

	static void eqplin(String s, SedCommand t) {
		assertEquals(SedParser.parseLine(SedCommandBundle.newInstance(), s).getCommand(), t);
	}

	static void eqplin2(String s, SedCommand... ts) {
		SedCommand l = SedParser.parseLine(SedCommandBundle.newInstance(), s).getCommand();
		SedCommandLine[] ll;

		if(l instanceof SedCompositeCommand) {
			ll = ((SedCompositeCommand)l).getCommandLines();
			if(ll.length != ts.length)  fail();
			for(int i = 0; i < ts.length; i++) {
				assertEquals(ll[i].command, ts[i]);
			}
		} else {
			fail();
		}
	}

	static void nlplin(String s) {
		assertNull(SedParser.parseLine(SedCommandBundle.newInstance(), s));
	}

	static void eqpline(String s) {
		try {
			SedParser.parseLine(SedCommandBundle.newInstance(), s);
			fail();
		} catch(SedSyntaxException e) {}
	}

	public void testErrors1() {
		eqpline("+1p");
		eqpline("+1,2p");
		eqpline("1~2,3p");
	}

	public void testCompoundLine() {
		eqplin2("1{n;p}", SedCommands.N, SedCommands.P);
		eqplin2("1,3{n;1p}", SedCommands.N, SedCommands.P);
		eqplin2("1{\n  n\n  p\n}", SedCommands.N, SedCommands.P);
		eqplin2("1{n;s/a/b/}", SedCommands.N, new SedSCommand("a", "b", new SedSubstituteFlags("")));
		eqplin2("1{n;s/a/b/;p}", SedCommands.N, new SedSCommand("a", "b", new SedSubstituteFlags("")), SedCommands.P);
		eqplin2("1{\n  a\\\naaaaa\n  p\n}", new SedACommand("aaaaa"), SedCommands.P);
		eqplin2("1{\n  c\\\naaaaa\n  p\n}", new SedCCommand("aaaaa"), SedCommands.P);
		eqplin2("1{\n  i\\\naaaaa\n  p\n}", new SedICommand("aaaaa"), SedCommands.P);
		eqplin2("1{:aaaa;p}", SedLabel.getInstance("aaaa"), SedCommands.P);
		eqplin2("1{p;:aaaa}", SedCommands.P, SedLabel.getInstance("aaaa"));
		eqplin2("1{}");

		eqpline("1{n;p");  eqpline("1{np}");
	}

	public void testGetstr() throws Exception {
		eqgetstr("ab*cd/g", '/', "ab*cd");
		eqgetstr("/g", '/', "");
		eqgetstre("abc", '/');
	}

	public void testNums() throws Exception {
		eqnums("1234aa", 1234);
		eqnums("00123", 123);
		eqnums("9223372036854775807", 9223372036854775807l);
		eqnumse("abc");
		eqnumse("000abc");
		eqnumse("9223372036854775808");
	}

	public void testLine() throws Exception {
		eqline("/aaaa/p", new SedPatternLineMatcher("aaaa"));
		eqline("1p", new SedNumberLineMatcher(1));
		eqline("1,2p", new SedNumberLineMatcher(1));
		eqline("$p", SedLastLineMatcher.INSTANCE);
		nlline("p");
	}

	public void testLines() throws Exception {
		eqlines("p", new SedLineOption());
		eqlines("1p", new SedLineOption(1));
		eqlines("1,3p", new SedLineOption(1, 3));
//		eqlines("/a/p", new SedLineOption("a", null));
		eqlines("/a/,/b/p", new SedLineOption("a", "b"));
		eqlines("$p", new SedLineOption(SedLastLineMatcher.INSTANCE));
		eqlines("/a/,10p", new SedLineOption("a", 10));
		eqlines("10,/a/p", new SedLineOption(10, "a"));
		eqlines("/a/,$p", new SedLineOption("a", false, 1));
		eqlines("10,$p", new SedLineOption(10, false, false));
		eqlines("$,$p", new SedLineOption(SedLastLineMatcher.INSTANCE));
		eqlines("g/re/p", new SedLineOption("re", false, false));
		eqlines("1!p", new SedLineOption(1, 1, true));
		eqlines("1,3!p", new SedLineOption(1, 3, true));
		eqlines("g/re/!p", new SedLineOption("re", true));

		eqlinese("$,1p");
		eqlinese("$,/a/p");
		eqlinese("2,1p");
	}

	public void testParseLine() {
		eqplin("1p", SedCommands.P);
		eqplin("1afilename", new SedACommand("filename"));
		eqplin("1a\\\nfilename", new SedACommand("filename"));
		nlplin("# comment");
		eqplin("2q", new SedQCommand(0));
		eqplin("2q1", new SedQCommand(1));
		eqplin("1rfilename", new SedRCommand("filename"));
		eqplin("1wfilename", new SedWCommand("filename"));
		eqplin(":label", SedLabel.getInstance("label"));
		eqplin("blabel", new SedBCommand("label"));
		eqplin("tlabel", new SedTCommand("label"));
		eqplin("y/ab/cd/", new SedYCommand("ab", "cd"));
		eqplin("s/ab/cd/", new SedSCommand("ab", "cd", new SedSubstituteFlags("")));
		eqplin("s/ab/cd/i", new SedSCommand("ab", "cd", new SedSubstituteFlags("i")));

		eqpline("1#");   eqpline("1,2#");
		eqpline("1,2q");  eqpline("qaaa");
		eqpline("1r");   eqpline("rfilename");  eqpline("1,2rfilename");
		eqpline("1w");
		eqpline(":");  eqpline("1:label");  eqpline("1,2:label");
		eqpline("b");  eqpline("t");
		eqpline("y");  eqpline("y/a");  eqpline("y/a/");  eqpline("y/a/a");
		eqpline("s");  eqpline("s/a");  eqpline("s/a/");  eqpline("s/a/a");
	}

}
