import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.BookmarkManager;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

public class ImportMarkersAsBookmarks extends GhidraScript {

  @Override
  public void run() throws Exception {
    // Can't do this in headless mode
    // File input = askFile("Select CSV file", "Import");

    String[] args = getScriptArgs();
    if (args.length < 1) {
      throw new IllegalArgumentException(
          "Missing CSV file path.\nUsage: ImportMarkersAsBookmarks <markers.csv>");
    }

    File input = new File(args[0]);
    if (!input.isFile()) {
      throw new IllegalArgumentException(
          "CSV file does not exist or is not a regular file: " + input.getAbsolutePath());
    }

    println("Importing bookmarks from: " + input.getAbsolutePath());

    // https://ghidra.re/ghidra_docs/api/ghidra/program/model/listing/BookmarkManager.html
    BookmarkManager bm = currentProgram.getBookmarkManager();

    int imported = 0;
    int skipped = 0;
    int lineNo = 0;

    // CSV columns: (benchmark, resulttype, faults, fault_address, mnemonic)
    // Example:     (ip       , OK_MARKER , 4311  , 0x10001B     , mov)
    try (BufferedReader br = new BufferedReader(new FileReader(input))) {
      String line;
      while ((line = br.readLine()) != null) {
        lineNo++;

        line = line.trim();
        if (line.isEmpty() || line.startsWith("#") || line.startsWith("benchmark")) {
          continue;
        }

        // Make sure to always use "," delimiter and never quotations
        String[] parts = line.split(",");
        if (parts.length != 5) {
          skipped++;
          printerr("Line " + lineNo + ": malformed");
          continue;
        }

        String benchText = parts[0].trim();
        String typeText = parts[1].trim();
        String countText = parts[2].trim();
        String addrText = parts[3].trim();
        String mnemonicText = parts[4].trim();

        if (benchText.isEmpty()
            || typeText.isEmpty()
            || countText.isEmpty()
            || addrText.isEmpty()
            || mnemonicText.isEmpty()) {
          skipped++;
          printerr("Line " + lineNo + ": malformed");
          continue;
        }

        Address addr = parseAddress(addrText);
        if (addr == null) {
          skipped++;
          printerr("Line " + lineNo + ": invalid address: " + addrText);
          continue;
        }

        println(
            "Adding bookmark at "
                + addr
                + " with type "
                + benchText
                + " with category "
                + benchText // ip, mem or regs
                + " - "
                + typeText
                + " description "
                + countText
                + "x");

        //                   TYPE       CATEGORY                      DESCRIPTION
        bm.setBookmark(addr, benchText, benchText + " - " + typeText, countText + "x");
        imported++;
      }
    }

    println("Imported " + imported + " bookmarks.");
    if (skipped > 0) {
      println("Skipped " + skipped + " lines.");
    }
  }

  public Address parseAddress(String text) {
    text = text.trim();

    if (text.startsWith("0x") || text.startsWith("0X")) {
      text = text.substring(2);
    }

    try {
      return toAddr(text);
    } catch (Exception e) {
      return null;
    }
  }
}
