/*
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package codetoanalyze.java.checkers;

import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.ImageView;
import com.facebook.infer.annotation.Expensive;
import com.facebook.infer.annotation.PerformanceCritical;
import javax.annotation.Nullable;

interface AnnotatedInterface {

  @PerformanceCritical
  void annotatedPerformanceCriticalInInterface();
}

class Other {

  @Expensive
  void expensive() {}

  void callsExpensive1() {
    expensive();
  }

  void inexpensiveMethod() {}
}

@Expensive
class ExpensiveClass {

  void anExpensiveMethod() {}
}

@PerformanceCritical
class PerformanceCriticalClass {

  void performanceCriticalMethod1(ExpensiveClass c) {
    c.anExpensiveMethod(); // should report
  }

  void performanceCriticalMethod2(Other o) {
    o.expensive(); // should report
  }

  void performanceCriticalMethod3(Other o) {
    o.callsExpensive1(); // should report
  }

  void performanceCriticalMethod4(Other o) {
    o.inexpensiveMethod(); // should not report
  }
}

class ExpensiveSubclass extends ExpensiveClass {

  void anotherExpensiveMethod() {}
}

class PerformanceCriticalSubclass extends PerformanceCriticalClass {

  void subclassPerformanceCriticalMethod1(ExpensiveClass c) {
    c.anExpensiveMethod(); // should report
  }

  void subclassPerformanceCriticalMethod2(ExpensiveSubclass c) {
    c.anotherExpensiveMethod(); // should report
  }

  void subclassPerformanceCriticalMethod3(Other o) {
    o.callsExpensive1(); // should report;
  }

  void subclassPerformanceCriticalMethod4(Other o) {
    o.inexpensiveMethod(); // should not report;
  }
}

public class ExpensiveCallExample implements AnnotatedInterface {

  @Nullable Other mOther;

  void nonExpensiveMethod() {}

  @Expensive
  void expensiveMethod() {
    // The checker should still report the expensive call stack despite the call cycle
    methodWrapper();
  }

  void methodWrapper() {
    expensiveMethod();
  }

  @PerformanceCritical
  void notCallingExpensiveMethod() {
    nonExpensiveMethod();
  }

  @PerformanceCritical
  void directlyCallingExpensiveMethod() {
    expensiveMethod();
  }

  @PerformanceCritical
  void indirectlyCallingExpensiveMethod() {
    methodWrapper();
  }

  @PerformanceCritical
  void callingExpensiveMethodFromInterface(ExpensiveInterfaceExample object) {
    object.m5();
  }

  void callsExpensive2() {
    mOther.callsExpensive1();
  }

  @PerformanceCritical
  void longerCallStackToExpensive() {
    callsExpensive2();
  }

  @PerformanceCritical
  View callsFindViewByIdFromView(ImageView view, int id) {
    return view.findViewById(id);
  }

  @PerformanceCritical
  View callsFindViewByIdFromActivity(FragmentActivity activity, int id) {
    return activity.findViewById(id);
  }

  @PerformanceCritical
  void callMethodOnExpensiveClass(ExpensiveClass c) {
    c.anExpensiveMethod();
  }

  public void annotatedPerformanceCriticalInInterface() {
    mOther.callsExpensive1();
  }

  @PerformanceCritical
  void callExpensiveMethodWithUnlikely() {
    if (Branch.unlikely(mOther != null)) {
      mOther.callsExpensive1();
    }
  }

  @PerformanceCritical
  void onlyOneExpensiveCallUsingUnlikely() {
    if (Branch.unlikely(mOther != null)) {
      mOther.callsExpensive1();
    }
    expensiveMethod();
  }

  @PerformanceCritical
  void callsExpensiveInTheUnlikelyElseBranch() {
    if (Branch.unlikely(mOther != null)) {
      // Do nothing
    } else {
      expensiveMethod();
    }
  }

  native boolean test();

  @PerformanceCritical
  void callsExpensiveWithDisjunctionAfterUnlikely() {
    if (Branch.unlikely(mOther != null) || test()) {
      expensiveMethod();
    }
  }

  @PerformanceCritical
  void callsExpensiveWithUnlikelyInLocalVariable() {
    boolean b = Branch.unlikely(mOther != null);
    if (b) {
      expensiveMethod();
    }
  }

  @PerformanceCritical
  void callsExpensiveWithOverriddenUnlikelyCondition() {
    boolean b = Branch.unlikely(mOther != null);
    b = test();
    if (b) {
      expensiveMethod();
    }
  }

  @PerformanceCritical
  void callsExpensiveInConditionalBranch() {
    if (test()) {
      expensiveMethod();
    }
  }
}
