package jadx.tests.integration.inline;

import java.util.List;
import java.util.Map;
import java.util.function.Function;

import org.junit.jupiter.api.Test;

import jadx.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.visitors.ProcessAnonymous;
import jadx.core.utils.ListUtils;
import jadx.tests.api.SmaliTest;

import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;

public class TestInstanceLambda extends SmaliTest {

	@SuppressWarnings({ "unchecked", "rawtypes", "SameParameterValue" })
	public static class TestCls {

		public <T> Map<T, T> test(List<? extends T> list) {
			return toMap(list, Lambda$1.INSTANCE);
		}

		/**
		 * Smali test missing 'T' definition in 'Lambda<T>'
		 * Note: use '$1' so class looks like generated by compiler and pass check in
		 * {@link ProcessAnonymous#canBeAnonymous(ClassNode)}
		 */
		@SuppressWarnings({ "CheckStyle", "checkstyle:TypeName" })
		private static class Lambda$1<T> implements Function<T, T> {
			public static final Lambda$1 INSTANCE = new Lambda$1();

			@Override
			public T apply(T t) {
				return t;
			}
		}

		private static <T> Map<T, T> toMap(List<? extends T> list, Function<T, T> valueMap) {
			return null;
		}
	}

	@Test
	public void test() {
		useJavaInput();
		noDebugInfo();
		assertThat(getClassNode(TestCls.class))
				.code();
	}

	@Test
	public void testSmaliDisableInline() {
		args.setInlineAnonymousClasses(false);
		List<ClassNode> classNodes = loadFromSmaliFiles();
		assertThat(searchTestCls(classNodes, "Lambda$1"))
				.code()
				.containsOne("class Lambda$1<T> implements Function<T, T> {");
		assertThat(searchTestCls(classNodes, "TestCls"))
				.code()
				.containsOne("Lambda$1.INSTANCE");
	}

	@Test
	public void testSmali() {
		List<ClassNode> classNodes = loadFromSmaliFiles();
		assertThat(ListUtils.filter(classNodes, c -> !c.contains(AFlag.DONT_GENERATE)))
				.describedAs("Expect lambda to be inlined")
				.hasSize(1);
		assertThat(searchTestCls(classNodes, "TestCls"))
				.code()
				.doesNotContain("Lambda$1.INSTANCE")
				.containsOne("toMap(list, new Function<T, T>() {");
	}
}
