Graph.js

const CLASS = require("./CoreClass.js");
const AnalysisHelper = require("./AnalysisHelper.js");


function findSuper(method){
    if(method.enclosingClass != null){

    }
}

function isLooping(loop,method){
    for(let i=0; i<loop.length; i++){
        if(loop[i]==method.__signature__){
            return true;
        }
    }
    loop.push(method.__signature__);
    return false;
}

/**
 * To get the call graph from a method. 
 * 
 * Lazy : Only the signature of the method is kept in the node
 *  
 * @param {*} method 
 * @param {*} depth 
 * @param {*} root 
 */
function findCallerLazy(method, depth=0, root=null, loop=null){
    if(root==null){
        root=method.__signature__;
        loop=[];
        loop.push(method.__signature__);  
    } 

    let cfg = { method:method.__signature__, depth:depth, callers:[] };

    if(method._callers != null){
        for(let i=0; i<method._callers.length; i++){
            if(isLooping(loop,method._callers[i])){
                //if(method._callers[i].__signature__ !== root){
                console.log(method._callers[i].__signature__);
                cfg.callers.push(findCallerLazy(method._callers[i], depth+1, root, loop));
            }
            else    {
                console.log(method._callers[i].__signature__);
                cfg.callers.push({ method:null, loop:true, depth:depth+1, callers:null });
            }
        }
    }
    return cfg;
}

/**
 * To get the call graph from a method. 
 *
 * @param {*} method 
 * @param {*} depth 
 * @param {*} root 
 */
function findCaller(method, depth=0, root=null){
    if(root==null) root=method.__signature__;

    let cfg = { method:method, depth:depth, callers:[] };

    if(method.callers != null){
        for(let i=0; i<method.callers.length; i++){
            if(method.callers[i].__signature__ !== root)
                cfg.callers.push(findCaller(method.callers[i], depth+1, root));
            else    
                cfg.callers.push({ method:null, loop:true, depth:depth+1, callers:null });
        }
    }
    return cfg;
}


function GraphMaker(ctx){
    this.context = ctx;
    return this;
}

/**
 * 
 * @param {Method} method  
 * @param {Boolean} lazy 
 * @param {Instruction} instruction

 */
GraphMaker.prototype.cfg = function(method, lazy=false, instruction=null){
    if(lazy)
        return findCallerLazy(method);
    else
        return findCaller(method);
}

/**
 * To get the call-graph of a method and return a ready to 
 * serializable lite result
 * 
 * @param {Method} method 
 * @param {Instruction} instruction 
 */
GraphMaker.prototype.cfgLazy = function(method, instruction=null){
    return this.cfg(method,true,instruction);
}
GraphMaker.prototype.method_htg = function(method, instruction=null){
    
}
GraphMaker.prototype.callgraph_from = function(obj, n=1, m=2){
    let tree={ 
        fqcn:obj.signature(), 
        tags: obj.tags,
        //internal:obj.hasTag(AnalysisHelper.TAG.Discover.Internal), 
        class:obj.enclosingClass.name, 
        classname: obj.enclosingClass.simpleName,
        name:obj.name, 
        callsignature: (obj.getAlias()!= null)? obj.__aliasedCallSignature__ : obj.__callSignature__  };

    //{ name:null, children:null };
    let meth = null;

    // call graph
    if(obj instanceof CLASS.Method){
        //tree.name = obj.signature();
        if(obj._useMethodCtr>0){
            tree.children = [];
            for(let i in obj._useMethod){
                meth = this.context.find.get.method(i);

                if(n<m)
                    tree.children.push(this.callgraph_from(this.context.find.get.method(i),n+1));
                else    
                    tree.children.push({ 
                        fqcn:i, 
                        tags: meth.getTags(),
                        //internal:meth.hasTag(AnalysisHelper.TAG.Discover.Internal), 
                        class:meth.enclosingClass.name, 
                        classname: meth.enclosingClass.simpleName,
                        name:meth.name, 
                        callsignature: (meth.getAlias()!= null)? meth.__aliasedCallSignature__ : meth.__callSignature__  });
            }
        }
    }

    console.log(tree);

    return tree;
}

GraphMaker.prototype.callgraph_to = function(obj, n=1, m=2){
    let tree={ name:null, children:null };

    // call graph
    if(obj instanceof CLASS.Method){
        tree.name = obj.signature();
        if(obj._useMethodCtr>0){
            tree.children = [];
            for(let i in obj._useMethod){
                if(n<m)
                    tree.children.push(this.callgraph_to(this.context.find.get.method(i),n+1));
                else    
                    tree.children.push({ name:i });
            }
        }
    }

    console.log(tree);

    return tree;
}
GraphMaker.prototype.htg = function(obj){
    /*if(obj instanceof CLASS.Class){
        return this.class_htg(obj);
    }
    else*/ if(obj instanceof CLASS.Method){
        return this.method_htg(method);
    }
    return null;
}


module.exports = GraphMaker;