package org.stringtemplate.v4.test;

import org.junit.Assert;
import org.junit.Test;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.STGroupFile;
import org.stringtemplate.v4.gui.STViz;

import java.awt.*;
import java.util.HashMap;

public class TestEarlyEvaluation extends BaseTest {
	/**
	 * @return true if at least one Window is visible
	 */
	public static boolean isAnyWindowVisible() {
		for (Window w : Window.getWindows()) {
			if (w.isVisible())
				return true;
		}
		return false;
	}

	public static void waitUntilAnyWindowIsVisible(long maxWaitMillis) {
		long startMillis = System.currentTimeMillis();
		while (!isAnyWindowVisible()) {
			if (System.currentTimeMillis() - startMillis > maxWaitMillis) {
				throw new RuntimeException("Timeout");
			}

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// Ignore
			}
		}
	}

	public static void waitUntilAllWindowsAreClosed() {
		while (isAnyWindowVisible()) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// ignore
			}
		}
	}

	/**
	 * see
	 * http://www.antlr3.org/pipermail/stringtemplate-interest/2011-May/003476.html
	 *
	 * @throws Exception
	 */
	@Test
	public void testEarlyEval() throws Exception {
		String templates = "main() ::= <<\n<f(p=\"x\")>*<f(p=\"y\")>\n>>\n\n"
				+ "f(p,q={<({a<p>})>}) ::= <<\n-<q>-\n>>";
		writeFile(tmpdir, "t.stg", templates);

		STGroup group = new STGroupFile(tmpdir + "/t.stg");

		ST st = group.getInstanceOf("main");

		String s = st.render();
		Assert.assertEquals("-ax-*-ay-", s);

		// Calling inspect led to an java.lang.ArrayIndexOutOfBoundsException in
		// 4.0.2
		STViz viz = st.inspect();
		if (interactive) {
			viz.waitForClose();
		}
		else {
			waitUntilAnyWindowIsVisible(4000);
			viz.viewFrame.dispose();
			waitUntilAllWindowsAreClosed();
		}
	}

	/**
	 * see
	 * http://www.antlr.org/pipermail/stringtemplate-interest/2011-May/003476.html
	 *
	 * @throws Exception
	 */
	@Test
	public void testEarlyEval2() throws Exception {
		String templates = "main() ::= <<\n<f(p=\"x\")>*\n>>\n\n"
				+ "f(p,q={<({a<p>})>}) ::= <<\n-<q>-\n>>";
		writeFile(tmpdir, "t.stg", templates);

		STGroup group = new STGroupFile(tmpdir + "/t.stg");

		ST st = group.getInstanceOf("main");

		String s = st.render();
		Assert.assertEquals("-ax-*", s);

		// When <f(...)> is invoked only once inspect throws no Exception in
		// 4.0.2
		STViz viz = st.inspect();
		if (interactive) {
			viz.waitForClose();
		}
		else {
			waitUntilAnyWindowIsVisible(4000);
			viz.viewFrame.dispose();
			waitUntilAllWindowsAreClosed();
		}
	}


	/**
	 * see http://www.antlr3.org/pipermail/stringtemplate-interest/2011-August/003758.html
	 * @throws Exception
	 */
	@Test
	public void testBugArrayIndexOutOfBoundsExceptionInSTRuntimeMessage_getSourceLocation()
			throws Exception {
		String templates = "main(doit = true) ::= "
				+ "\"<if(doit || other)><t(...)><endif>\"\n"
				+ "t2() ::= \"Hello\"\n" //
				+ "t(x={<(t2())>}) ::= \"<x>\"";

		writeFile(tmpdir, "t.stg", templates);

		STGroup group = new STGroupFile(tmpdir + "/t.stg");

		ST st = group.getInstanceOf("main");

		String s = st.render();
		Assert.assertEquals("Hello", s);

		// Inspecting this template threw an ArrayIndexOutOfBoundsException
		// in 4.0.2.
		// With the default for x changed to {<t2()>} (i.e. lazy eval) inspect
		// works fine. Also removing the " || other" and keeping the early eval
		// works fine with inspect.

		STViz viz = st.inspect();
		if (interactive) {
			viz.waitForClose();
		}
		else {
			waitUntilAnyWindowIsVisible(4000);
			viz.viewFrame.dispose();
			waitUntilAllWindowsAreClosed();
		}
	}

	@Test
	public void testEarlyEvalInIfExpr() throws Exception {
		String templates = "main(x) ::= << <if((x))>foo<else>bar<endif> >>";
		writeFile(tmpdir, "t.stg", templates);

		STGroup group = new STGroupFile(tmpdir + "/t.stg");

		ST st = group.getInstanceOf("main");

		String s = st.render();
		Assert.assertEquals(" bar ", s);

		st.add("x", "true");
		s = st.render();
		Assert.assertEquals(" foo ", s);
	}

	@Test
	public void testEarlyEvalOfSubtemplateInIfExpr() throws Exception {
		String templates = "main(x) ::= << <if(({a<x>b}))>foo<else>bar<endif> >>";
		writeFile(tmpdir, "t.stg", templates);

		STGroup group = new STGroupFile(tmpdir + "/t.stg");

		ST st = group.getInstanceOf("main");

		String s = st.render();
		Assert.assertEquals(" foo ", s);
	}

	@Test
	public void testEarlyEvalOfMapInIfExpr() throws Exception {
		String templates =
			"m ::= [\n"+
			"	\"parrt\": \"value\",\n"+
			"	default: \"other\"\n"+
			"]\n" +
			"main(x) ::= << p<x>t: <m.({p<x>t})>, <if(m.({p<x>t}))>if<else>else<endif> >>\n";
		writeFile(tmpdir, "t.stg", templates);

		STGroup group = new STGroupFile(tmpdir + "/t.stg");

		ST st = group.getInstanceOf("main");

		st.add("x", null);
		String s = st.render();
		Assert.assertEquals(" pt: other, if ", s);

		st.add("x", "arr");
		s = st.render();
		Assert.assertEquals(" parrt: value, if ", s);
	}

	@Test
	public void testEarlyEvalOfMapInIfExprPassInHashMap() throws Exception {
		String templates =
			"main(m,x) ::= << p<x>t: <m.({p<x>t})>, <if(m.({p<x>t}))>if<else>else<endif> >>\n";
		writeFile(tmpdir, "t.stg", templates);

		STGroup group = new STGroupFile(tmpdir + "/t.stg");

		ST st = group.getInstanceOf("main");
		st.add("m", new HashMap<String, String>() {{put("parrt","value");}});

		st.add("x", null);
		String s = st.render();
		Assert.assertEquals(" pt: , else ", s); // m[null] has no default value so else clause

		st.add("x", "arr");
		s = st.render();
		Assert.assertEquals(" parrt: value, if ", s);
	}

}
