001/************************* PROJECT RON *************************/
002/* Copyright (c) 2026 StuyPulse Robotics. All rights reserved. */
003/* Use of this source code is governed by an MIT-style license */
004/* that can be found in the repository LICENSE file.           */
005/***************************************************************/
006package tools.DogLogASTParser;
007
008import com.github.javaparser.ast.CompilationUnit;
009import com.github.javaparser.ast.expr.MethodCallExpr;
010import com.github.javaparser.ast.expr.NameExpr;
011import com.github.javaparser.ast.visitor.ModifierVisitor;
012
013/**
014 *
015 *
016 * <h2>DogLogRewriter | Actual functionality</h2>
017 *
018 * <p>
019 * The actual class that performs the AST parsing and replacement of
020 * <code>SmartDashboard</code>
021 * calls with <code>DogLog</code> calls, invoked by the {@link Main} class.
022 *
023 * <p>
024 * Extends {@link ModifierVisitor} to "visit" and modify the calls.
025 */
026public class DogLogRewriter extends ModifierVisitor<Void> {
027
028    private boolean fileChanged = false;
029
030    /**
031     *
032     *
033     * <h4>Visit Method</h4>
034     *
035     * <p>
036     * Overrides the {@link visit} method for method calls.
037     *
038     * <p>
039     * It loops through every {@link MethodCallExpr} to check for a
040     * <code>SmartDashboard</code>
041     * call with the methods <code>putNumber</code>, <code>putBoolean</code>, or
042     * <code>putString
043     * </code>. If so, it replaces the scope with <code>DogLog</code> and changes
044     * the method name to
045     * <code>log</code>.
046     *
047     * <p>
048     * Sets variable <code>fileChanged</code> to check if any changes were made to
049     * the file to
050     * determine whether to import <code>DogLog</code> at the end of the file.
051     *
052     * @param methodCall the method call expression being visited
053     * @param arg        additional argument (not used in this case)
054     * @return the modified method call expression
055     */
056    @Override
057    public MethodCallExpr visit(MethodCallExpr methodCall, Void arg) {
058        super.visit(methodCall, arg);
059
060        if (!methodCall.getScope().isPresent()) {
061            return methodCall;
062        }
063
064        boolean isSmartDashboardCall = methodCall
065                .getScope()
066                .filter(s -> s instanceof NameExpr)
067                .map(s -> ((NameExpr) s).getNameAsString())
068                .filter(name -> name.equals("SmartDashboard"))
069                .isPresent();
070
071        if (isSmartDashboardCall) {
072            String methodName = methodCall.getNameAsString();
073            boolean shouldBeReplaced = switch (methodName) {
074                case "putNumber", "putBoolean", "putString" -> true;
075                default -> false;
076            };
077
078            if (!shouldBeReplaced) {
079                return methodCall;
080            }
081
082            methodCall.setScope(new NameExpr("DogLog"));
083            methodCall.setName("log");
084            fileChanged = true;
085        }
086
087        return methodCall;
088    }
089
090    /**
091     *
092     *
093     * <h4>Applies the replacement</h4>
094     *
095     * <p>
096     * Applies the <code>SmartDashboard</code> to <code>DogLog</code> replacement to
097     * the given
098     * {@link CompilationUnit}.
099     *
100     * <p>
101     * If the <code>fileChanged</code> flag is true, it checks if the files already
102     * has an import
103     * for <code>DogLog</code>. If not, it adds the import statement. If the flag is
104     * false, it does
105     * nothing to avoid unnecessary changes and imports.
106     *
107     * @param parsed the {@link CompilationUnit} to process
108     */
109    public static void applyReplacement(CompilationUnit parsed) {
110        DogLogRewriter doglogRewriter = new DogLogRewriter();
111        parsed.accept(doglogRewriter, null);
112
113        if (!doglogRewriter.fileChanged) {
114            return;
115        }
116
117        String doglogImport = "dev.doglog.DogLog";
118        boolean hasDogLogImport = parsed.getImports().stream().anyMatch(i -> i.getNameAsString().equals(doglogImport));
119
120        if (!hasDogLogImport) {
121            parsed.addImport(doglogImport);
122        }
123    }
124}