// annotates each function with Function ID matches from any attached FidDb.
//
// idempotent. unlike the Function ID Analyzer (ApplyFidEntriesCommand), this never renames any
// functions, always updates their plate comments, and can coexist with other plate comments by
// writing its findings as a “trailer”.

import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;

public class ReverssgCorrelate extends GhidraScript {
    public void run() throws Exception {
        // FIXME: opens other program as a tab in Listing, which changes `currentProgram`
        var otherProgram = askProgram("choose another program", false);
        var otherProgramListing = otherProgram.getListing();

        var fids = new ghidra.feature.fid.service.FidService();
        // TODO: first argument will become FidProgramID in newer versions of ghidra
        try (var queryService = fids.openFidQueryService(currentProgram.getLanguage(), false)) {
            for (var entry : fids.processProgram(currentProgram, queryService, 14.6f, TaskMonitor.DUMMY)) {
                Stream.Builder<String> trailerValues = Stream.builder();
                printf("%s\t%s\n", entry.function.getEntryPoint(), entry.function /*, entry.hashQuad*/);
                for (var match : entry.matches) {
                    var source = match.getLibraryRecord();
                    var functionName = match.getFunctionRecord().getName();
                    var trailerValue = String.format("[%s %s %s] %s (%.1f)",
                        source.getLibraryFamilyName(),
                        source.getLibraryVersion(),
                        source.getLibraryVariant(),
                        functionName,
                        match.getOverallScore());
                    // TODO: support namespaces (NationalSecurityAgency/ghidra#5858)
                    for (var function : otherProgramListing.getGlobalFunctions(functionName)) {
                        trailerValue += " " + function.getEntryPoint();
                    }
                    printf("\t%s\n", trailerValue);
                    trailerValues.accept(trailerValue);
                }
                ReverssgUtil.setPlateCommentTrailer(entry.function, "FID MATCH", trailerValues.build());
            }
        }

        otherProgram.release(this);
    }
}
