//@category Vegvisir
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.*;
import java.util.*;

public class VegvisirCallGraph extends GhidraScript {
  private static String q(String s){ if(s==null)return"null"; StringBuilder b=new StringBuilder("\""); for(int i=0;i<s.length();i++){char c=s.charAt(i); switch(c){case '\\':b.append("\\\\");break;case '"':b.append("\\\"");break;case '\n':b.append("\\n");break;case '\r':b.append("\\r");break;case '\t':b.append("\\t");break;default: if(c<0x20)b.append(String.format("\\u%04x",(int)c)); else b.append(c);}} return b.append('"').toString(); }
  private Function findFunctionTarget(String s) throws Exception { if(s==null||s.length()==0)return null; FunctionManager fm=currentProgram.getFunctionManager(); try { ghidra.program.model.address.Address a=currentProgram.getAddressFactory().getAddress(s); if(a!=null){Function f=fm.getFunctionAt(a); if(f==null)f=fm.getFunctionContaining(a); if(f!=null)return f;}} catch(Exception e){} FunctionIterator it=fm.getFunctions(true); while(it.hasNext()){Function f=it.next(); if(f.getName().equals(s)||f.getName(true).equals(s)) return f;} return null; }
  public void run() throws Exception { String[] a=getScriptArgs(); String mode=a.length>0?a[0]:"edges"; String target=a.length>1?a[1]:""; int limit=a.length>2?Integer.parseInt(a[2]):500; StringBuilder out=new StringBuilder(); out.append("{\n  \"ok\": true,\n  \"program\": ").append(q(currentProgram.getName())).append(",\n"); int emitted=0,total=0; if(mode.equals("callers")||mode.equals("callees")){ Function f=findFunctionTarget(target); if(f==null){println("VEGVISIR_JSON:{\"ok\":false,\"error\":\"function not found\"}");return;} Set<Function> fs=mode.equals("callers")?f.getCallingFunctions(monitor):f.getCalledFunctions(monitor); out.append("  \"mode\": ").append(q(mode)).append(",\n  \"target\": ").append(q(f.getEntryPoint().toString())).append(",\n  \"functions\": [\n"); for(Function x: fs){ total++; if(emitted++>0)out.append(",\n"); if(emitted<=limit) out.append("    {\"name\": ").append(q(x.getName())).append(", \"entry\": ").append(q(x.getEntryPoint().toString())).append("}"); } out.append("\n  ],\n  \"emitted\": ").append(Math.min(emitted,limit)).append(",\n  \"total\": ").append(total).append("\n}"); println("VEGVISIR_JSON:"+out); return; }
    out.append("  \"mode\": \"edges\",\n  \"edges\": [\n"); FunctionIterator it=currentProgram.getFunctionManager().getFunctions(true); while(it.hasNext() && emitted<limit){Function f=it.next(); for(Function c: f.getCalledFunctions(monitor)){ total++; if(emitted++>0)out.append(",\n"); out.append("    {\"from\": ").append(q(f.getEntryPoint().toString())).append(", \"fromName\": ").append(q(f.getName())).append(", \"to\": ").append(q(c.getEntryPoint().toString())).append(", \"toName\": ").append(q(c.getName())).append("}"); if(emitted>=limit)break; }} out.append("\n  ],\n  \"emitted\": ").append(emitted).append(",\n  \"truncated\": ").append(emitted>=limit).append("\n}"); println("VEGVISIR_JSON:"+out); }
}
