import java.io.*;
import java.nio.file.*;

/**
 * Generates thousands of dummy Java classes to stress the AOT archiver.
 * Each class has fields, methods, and constructors to maximize metadata.
 */
public class ClassGenerator {

    // Target: Generate enough classes to reach ~5.5M RW objects (OOM threshold)
    // From tests: 100K classes → 3.1M RW objects
    // So 180K classes → ~5.5M RW objects
    private static final int TARGET_CLASS_COUNT = 300_000;
    private static final String OUTPUT_DIR = "generated-classes/src";
    private static final String PACKAGE_NAME = "com.stress.generated";

    public static void main(String[] args) throws IOException {
        System.out.println("=== Dummy Class Generator ===");
        System.out.println("Target: " + TARGET_CLASS_COUNT + " classes");
        System.out.println("Output: " + OUTPUT_DIR);
        System.out.println();

        // Create output directory
        Path outputPath = Paths.get(OUTPUT_DIR, PACKAGE_NAME.replace('.', '/'));
        Files.createDirectories(outputPath);

        long startTime = System.currentTimeMillis();

        // Generate classes in batches
        int batchSize = 1000;
        for (int batch = 0; batch < TARGET_CLASS_COUNT / batchSize; batch++) {
            for (int i = 0; i < batchSize; i++) {
                int classNumber = batch * batchSize + i;
                generateClass(outputPath, classNumber);
            }

            if ((batch + 1) % 10 == 0) {
                int generated = (batch + 1) * batchSize;
                System.out.printf("Generated %d / %d classes (%.1f%%)%n",
                    generated, TARGET_CLASS_COUNT,
                    100.0 * generated / TARGET_CLASS_COUNT);
            }
        }

        long elapsed = System.currentTimeMillis() - startTime;
        System.out.println();
        System.out.println("=== Generation Complete ===");
        System.out.println("Total classes: " + TARGET_CLASS_COUNT);
        System.out.println("Time taken: " + elapsed + "ms");
        System.out.println();
        System.out.println("Next steps:");
        System.out.println("  1. Compile: ./compile-generated-classes.sh");
        System.out.println("  2. Run AOT: ./run-generated-classes-aot.sh");
    }

    private static void generateClass(Path outputPath, int classNumber) throws IOException {
        String className = "GeneratedClass" + classNumber;
        Path classFile = outputPath.resolve(className + ".java");

        StringBuilder code = new StringBuilder();
        code.append("package ").append(PACKAGE_NAME).append(";\n\n");
        code.append("/**\n");
        code.append(" * Auto-generated class #").append(classNumber).append("\n");
        code.append(" * This class exists to stress the AOT archiver.\n");
        code.append(" */\n");
        code.append("public class ").append(className).append(" {\n\n");

        // Add fields (creates field metadata)
        for (int i = 0; i < 5; i++) {
            code.append("    private int field").append(i).append(";\n");
            code.append("    private String strField").append(i).append(";\n");
        }
        code.append("\n");

        // Add constructor (creates method metadata)
        code.append("    public ").append(className).append("() {\n");
        for (int i = 0; i < 5; i++) {
            code.append("        this.field").append(i).append(" = ").append(i).append(";\n");
            code.append("        this.strField").append(i).append(" = \"value").append(i).append("\";\n");
        }
        code.append("    }\n\n");

        // Add methods (creates more method metadata + bytecode)
        for (int i = 0; i < 10; i++) {
            code.append("    public int method").append(i).append("() {\n");
            code.append("        return field").append(i % 5).append(" + ").append(i).append(";\n");
            code.append("    }\n\n");

            code.append("    public String getStr").append(i).append("() {\n");
            code.append("        return strField").append(i % 5).append(" + \"_\" + ").append(i).append(";\n");
            code.append("    }\n\n");
        }

        // Add a compute method with some logic
        code.append("    public int compute() {\n");
        code.append("        int sum = 0;\n");
        for (int i = 0; i < 5; i++) {
            code.append("        sum += field").append(i).append(";\n");
        }
        code.append("        return sum;\n");
        code.append("    }\n\n");

        // Add toString (creates string constants)
        code.append("    @Override\n");
        code.append("    public String toString() {\n");
        code.append("        return \"").append(className).append("{\" + \n");
        for (int i = 0; i < 5; i++) {
            code.append("            \"field").append(i).append("=\" + field").append(i);
            if (i < 4) {
                code.append(" + \", \" +\n");
            } else {
                code.append(" + \n");
            }
        }
        code.append("            \"}\";\n");
        code.append("    }\n");

        code.append("}\n");

        Files.writeString(classFile, code.toString());
    }
}
