package jadx.tests.integration.debuginfo;

import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.nodes.LineAttrNode;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.tests.api.IntegrationTest;

import org.junit.Test;

import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

public class TestLineNumbers extends IntegrationTest {

	public static class TestCls {
		int field;

		public void func() {
		}

		public static class Inner {
			int innerField;

			public void innerFunc() {
			}

			public void innerFunc2() {
				new Runnable() {
					@Override
					public void run() {
					}
				}.run();
			}

			public void innerFunc3() {
			}
		}
	}

	@Test
	public void test() {
		ClassNode cls = getClassNode(TestCls.class);
		String code = cls.getCode().toString();

		FieldNode field = cls.searchFieldByName("field");
		MethodNode func = cls.searchMethodByName("func()V");
		ClassNode inner = cls.getInnerClasses().get(0);
		MethodNode innerFunc = inner.searchMethodByName("innerFunc()V");
		MethodNode innerFunc2 = inner.searchMethodByName("innerFunc2()V");
		MethodNode innerFunc3 = inner.searchMethodByName("innerFunc3()V");
		FieldNode innerField = inner.searchFieldByName("innerField");

		// check source lines (available only for instructions and methods)
		int testClassLine = 18;
		assertEquals(testClassLine + 3, func.getSourceLine());
		assertEquals(testClassLine + 9, innerFunc.getSourceLine());
		assertEquals(testClassLine + 12, innerFunc2.getSourceLine());
		assertEquals(testClassLine + 20, innerFunc3.getSourceLine());

		// check decompiled lines
		String[] lines = code.split(CodeWriter.NL);
		checkLine(lines, field, "int field;");
		checkLine(lines, func, "public void func() {");
		checkLine(lines, inner, "public static class Inner {");
		checkLine(lines, innerField, "int innerField;");
		checkLine(lines, innerFunc, "public void innerFunc() {");
		checkLine(lines, innerFunc2, "public void innerFunc2() {");
		checkLine(lines, innerFunc3, "public void innerFunc3() {");
	}

	private static void checkLine(String[] lines, LineAttrNode node, String str) {
		int lineNumber = node.getDecompiledLine();
		String line = lines[lineNumber - 1];
		assertThat(line, containsString(str));
	}
}
