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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.PrintWriter;
import java.io.StringReader;

import net.morilib.awk.builtin.AwkBuiltInLoader;
import net.morilib.awk.code.AwkExecutable;
import net.morilib.awk.io.AwkFiles;
import net.morilib.awk.namespace.AwkNamespace;
import net.morilib.awk.namespace.AwkRootNamespace;
import net.morilib.awk.parser.AwkLexer;
import net.morilib.awk.parser.AwkParser;
import net.morilib.awk.value.AwkUndefined;
import net.morilib.awk.value.AwkValue;
import net.morilib.sh.file.ShFileSystemFactory;
import junit.framework.TestCase;

public class AwkTestCase extends TestCase {

	AwkNamespace ns0;
	AwkFiles fs0;
	PipedWriter pow, pew;
	PipedInputStream pir;
	PrintWriter piw;
	BufferedReader por, per;

	static AwkLexer lex(String s) {
		try {
			return new AwkLexer(new StringReader(s));
		} catch (IOException e) {
			throw new RuntimeException(s);
		}
	}

	static AwkNamespace ns0() {
		AwkRootNamespace r = new AwkRootNamespace();

		AwkBuiltInLoader.load(r);
		return r;
	}

	protected void setUp() {
		try {
			ns0 = ns0();
			pir = new PipedInputStream();
			pow = new PipedWriter();
			pew = new PipedWriter();
			fs0 = new AwkFiles(ShFileSystemFactory.getInstance(),
					pir, pow, pew);
			piw = new PrintWriter(new PipedOutputStream(pir));
			por = new BufferedReader(new PipedReader(pow));
			per = new BufferedReader(new PipedReader(pew));
		} catch(IOException e) {
			fail();
		}
	}

	void exe(String l) {
		try {
			AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqs(String l, String s) {
		try {
			assertEquals(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toString(ns0), s);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqi(String l, long s) {
		try {
			assertEquals(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toInteger().longValue(), s);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void neg(String l) {
		try {
			assertTrue(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toInteger().longValue() < 0);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void pos(String l) {
		try {
			assertTrue(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toInteger().longValue() > 0);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void tru(String l) {
		try {
			assertTrue(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toInteger().longValue() != 0);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void fal(String l) {
		try {
			assertTrue(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toInteger().longValue() == 0);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqb(String l, boolean b) {
		try {
			assertEquals(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toInteger().longValue() != 0, b);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqf(String l, double s) {
		try {
			assertEquals(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toFloat(), s);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqr(String l, String s) {
		try {
			assertEquals(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0).toRegex(ns0).pattern(), s);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqa(String l, double... s) {
		AwkValue a;

		try {
			a = AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0);
			assertTrue(a.isArray());
			assertEquals(a.keys().size(), s.length);
			for(int i = 1; i <= s.length; i++) {
				assertEquals(a.referArray(i + "").toFloat(), s[i - 1]);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqa(String l, String... s) {
		AwkValue a;

		try {
			a = AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0);
			assertTrue(a.isArray());
			assertEquals(a.keys().size(), s.length);
			for(int i = 1; i <= s.length; i++) {
				assertEquals(a.referArray(i + "").toString(), s[i - 1]);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void und(String l) {
		try {
			assertEquals(AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0), AwkUndefined.UNDEF);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void err(String l) {
		try {
			AwkParser.parseStatement(lex(l));  fail();
		} catch(AwkRuntimeException e) {
			// ignore
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void erz(String l) {
		try {
			AwkParser.parseStatement(lex(l)).compileInternally().eval(ns0, fs0);
			fail();
		} catch(AwkRuntimeException e) {
			// ignore
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	void eqz(String nm, long v) {
		assertEquals(ns0.find(nm).toInteger().longValue(), v);
	}

	void eqz(String nm, double v) {
		assertEquals(ns0.find(nm).toFloat(), v);
	}

	void eqz(String nm, String v) {
		assertEquals(ns0.find(nm).toString(ns0), v);
	}

	void eot(String s) {
		try {
			if(!por.ready())  fail();
			assertEquals(por.readLine(), s);
		} catch (IOException e) {
			fail();
		}
	}

	void eet(String s) {
		try {
			if(!per.ready())  fail();
			assertEquals(per.readLine(), s);
		} catch (IOException e) {
			fail();
		}
	}

	void eot() {
		try {
			if(por.ready())  assertNull(por.readLine());
		} catch (IOException e) {
			fail();
		}
	}

	void eet() {
		try {
			if(per.ready())  assertNull(per.readLine());
		} catch (IOException e) {
			fail();
		}
	}

	AwkExecutable pre(String l) {
		try {
			return AwkParser.parse(ns0, lex(l));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
