private static Operation chooseFunctionForInvocation(
      LinkedList<Operation> functions, FileScope scope) {
    Operation candidate = null;
    int candidatePriority = 0;
    int curPriority;
    for (Operation f : functions) {
      curPriority = 0;

      // Scoped private functions override global ones
      if (f.getScope() == scope) {
        curPriority = 2;
        if (f.isOverride()) {
          curPriority = 4;
        }
      } else if (f.getVisibility() != Visibility.PRIVATE) {
        curPriority = 1;
        if (f.isOverride()) {
          curPriority = 3;
        }
      }
      if (curPriority > candidatePriority) {
        candidate = f;
        candidatePriority = curPriority;
      }
    }
    return candidate;
  }
  void addMethod(Operation f) {

    // Enrichment methods may not declared override
    if (f.isOverride())
      throw Problem.ofType(ProblemId.NATIVE_ENRICHMENT_METHOD_DECLARED_OVERRIDE)
          .at(f.getDefinition())
          .raiseUnrecoverable();

    String uid = f.getUid();
    LinkedHashMap<Signature, LinkedList<Operation>> meths = methods.get(uid);
    if (meths == null) {
      meths = new LinkedHashMap<Signature, LinkedList<Operation>>();
      methods.put(uid, meths);
    }
    addMethod(f, meths);
  }
  public Operation resolveInvocation(FileScope scope, SyntaxNode where, String name, Signature s) {
    LinkedHashMap<Signature, LinkedList<Operation>> funcs = methods.get(name);
    if (funcs == null) return null;
    // No signature specified? Only one method with the name allowed
    if (s == null) {
      if (funcs.size() > 1)
        throw Problem.ofType(ProblemId.AMBIGUOUS_METHOD_ACCESS)
            .at(where)
            .details("method", name, assembleFuncList(funcs))
            .raiseUnrecoverable();
      return chooseFunctionForInvocation(funcs.entrySet().iterator().next().getValue(), scope);
    }
    LinkedList<Operation> funcList = funcs.get(s);
    if (funcList == null) {
      // No direct hit :(

      Operation globalCandidate = null;
      Operation candidate = null;
      boolean error = false;
      for (Signature sig : funcs.keySet()) {
        if (s.canImplicitCastTo(sig)) {
          funcList = funcs.get(sig);
          candidate = chooseFunctionForInvocation(funcList, scope);
          if (candidate != null && globalCandidate != null) {
            error = true;
          }
          globalCandidate = candidate;
        }
        if (error)
          throw Problem.ofType(ProblemId.AMBIGUOUS_METHOD_CALL)
              .at(where)
              .details(
                  "method", candidate.getNameAndSignature(), globalCandidate.getNameAndSignature())
              .raiseUnrecoverable();
        globalCandidate = candidate;
      }
      return globalCandidate;
    }

    // Direct signature hit!
    return chooseFunctionForInvocation(funcList, scope);
  }
  public void generateFunctionIndex() {
    for (String s : methods.keySet()) {
      int index = 0;
      LinkedHashMap<Signature, LinkedList<Operation>> funcs = methods.get(s);
      for (Signature sig : funcs.keySet()) {
        LinkedList<Operation> sameFuncs = funcs.get(sig);

        // If we have only one function, we need no function indices.
        // However, we still mus go through the loop to check for dangling forward-declarations.
        if (sameFuncs.size() == 1 && funcs.size() == 1) {
          index = 0;
        }
        for (Operation f : sameFuncs) {
          if (f.hasBody()) {
            f.setIndex(index++);
          }
        }
      }
    }
  }
  private void addMethod(Operation f, LinkedHashMap<Signature, LinkedList<Operation>> meths) {
    LinkedList<Operation> list = meths.get(f.getSignature());

    // No duplicate method? Everything fine!
    if (list == null) {
      list = new LinkedList<Operation>();
      meths.put(f.getSignature(), list);
      list.add(f);
      return;
    }

    // Function is just a forward declaration and there is already such a function? Skip!
    if (f.isForwardDeclaration()) return;

    // Test for duplicate functions
    ListIterator<Operation> iter = list.listIterator();
    while (iter.hasNext()) {
      Operation f2 = iter.next();

      // Forward declaration? Remove!
      if (f2.isForwardDeclaration()) {
        iter.remove();
        continue;
      }

      // Duplicate?
      if (f2.getScope().equals(f.getScope())
          || (f.getVisibility() != Visibility.PRIVATE
              && f2.getVisibility() != Visibility.PRIVATE)) {
        // TODO: This code was copy pasted from function set. Check if it makes sense for
        // enrichments
        throw Problem.ofType(ProblemId.DUPLICATE_METHOD)
            .at(f.getDefinition(), f2.getDefinition())
            .raiseUnrecoverable();
      }
    }
    list.add(f);
  }