@Nullable
    public static Type apply(@NotNull FunType func, @Nullable List<Type> aTypes, Map<String, Type> kTypes, Type kwargsType, Type starargsType, @Nullable Node call, int tag) {

        Indexer.idx.removeUncalled(func);

        if (func.func != null && !func.func.called) {
//            System.out.println("== func call ==");
//            System.out.println(func.func.toString());
//            System.out.println("   from    ");
//
//            System.out.println();



            Indexer.idx.nCalled++;
            func.func.called = true;
        }

        if (func.getFunc() == null) {           // func without definition (possibly builtins)
            return Indexer.idx.builtins.unknown;
        } else if (call != null && Indexer.idx.inStack(call)) {
            func.setSelfType(null);
            return Indexer.idx.builtins.unknown;
        }



        if (call != null) {
            Indexer.idx.pushStack(call);


            FunctionDef caller = (FunctionDef)((Call)call).getCallingFunction();
            //func.func is the callee
            if(caller != null) {
                Indexer.idx.addFunctionCall(caller, func.func);
            } else {
                System.out.println("Caller for " + func.func + " is null");
            }


        }

        List<Type> argTypeList = new ArrayList<Type>();
        if (func.getSelfType() != null) {
            argTypeList.add(func.getSelfType());
        } else if (func.getCls() != null) {
            argTypeList.add(func.getCls().getCanon());
        }


        if (aTypes != null) {
            argTypeList.addAll(aTypes);
        }

        bindMethodAttrs(func, tag);

        Scope funcTable = new Scope(func.getEnv(), Scope.ScopeType.FUNCTION);

        if (func.getTable().getParent() != null) {
            funcTable.setPath(func.getTable().getParent().extendPath(func.func.name.id));
        } else {
            funcTable.setPath(func.func.name.id);
        }

        Type fromType = bindParams(call, funcTable, func.func.args,
                func.func.vararg, func.func.kwarg,
                argTypeList, func.defaultTypes, kTypes, kwargsType, starargsType, tag);

        Type cachedTo = func.getMapping(fromType);
        if (cachedTo != null) {
            func.setSelfType(null);
            return cachedTo;
        } else {
            Type toType = resolveExpr(func.func.body, funcTable, tag);
            if (missingReturn(toType)) {
                Indexer.idx.putProblem(func.func.name, "Function not always return a value");

                if (call != null) {
                    Indexer.idx.putProblem(call, "Call not always return a value");
                }
            }

            func.setMapping(fromType, toType);
            func.setSelfType(null);
            return toType;
        }
    }