package org.extendj;

import org.extendj.ast.*;
import org.junit.jupiter.api.Assertions;

import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Supplier;

public abstract class ScopeAnalysisTest {
  private static void assertShadow(Set<AbstractFinding> findings,
                            BiFunction<JavaDeclaration, JavaDeclaration, Boolean> test,
                            Supplier<String> failMessage) {
    for (AbstractFinding finding : findings) {
      if (finding instanceof VariableShadowFinding) {
        JavaDeclaration shadower = ((VariableShadowFinding)finding).getShadower().asJavaDeclaration();
        JavaDeclaration shadowed = ((VariableShadowFinding)finding).getShadowed().asJavaDeclaration();
        if (test.apply(shadower, shadowed)) {
          return;
        }
      }
    }
    Assertions.fail(failMessage.get());
  }

  static void assertShadow(Set<AbstractFinding> findings, String name, String shadowerSourceFile, int shadowerLine, String shadowedSourceFile, int shadowedLine) {
    assertShadow(findings,
        (shadower, shadowed) ->
            shadowed.getName().equals(name) &&
                shadowed.sourceFile().contains(shadowedSourceFile) &&
                shadowed.lineNumber() == shadowedLine &&
                shadower.sourceFile().contains(shadowerSourceFile) &&
                shadower.lineNumber() == shadowerLine,
        () -> "No shadow finding found for name '" + name + "' in " +shadowerSourceFile + ":" + shadowerLine + " > " + shadowedSourceFile + ":" + shadowedLine);
  }

  static void assertRedefinition(Set<AbstractFinding> findings, String name, int declLine) {
    for (AbstractFinding finding : findings) {
      if (finding instanceof MultipleDeclarationFinding) {
        Declaration declaration = ((MultipleDeclarationFinding)finding).getDeclaration();
        if (declaration.getName().equals(name) && declaration.lineNumber() == declLine) {
          return;
        }
      }
    }
    Assertions.fail("No multi-decl finding found for name '" + name + "' in line " + declLine);
  }

  static void assertNotShadow(Set<AbstractFinding> findings, String name, String shadowerSourceFile, int shadowerLine, String shadowedSourceFile, int shadowedLine) {
    try {
      assertShadow(findings, name, shadowerSourceFile, shadowerLine, shadowedSourceFile, shadowedLine);
    } catch (AssertionError e) {
      return;
    }
    Assertions.fail("There should not be a shadow finding for name '" + name + "' in " + shadowerSourceFile + ":" + shadowerLine + " > " + shadowedSourceFile + ":" + shadowedLine);
  }
}