package com.stress.generated;

import java.util.ArrayList;
import java.util.List;

/**
 * Combined loader for both simple classes and mega-classes.
 * This simulates a real application with:
 * - Many simple classes (normal application classes)
 * - A few mega-classes (classes with thousands of fields)
 */
public class CombinedClassLoader {

    private static final int SIMPLE_CLASS_COUNT = 300_000;
    private static final int MEGA_CLASS_COUNT = 2_000;
    private static final int FIELD_OBJECT_COUNT = 5_000;
    private static final String PACKAGE_NAME = "com.stress.generated";

    // Keep references so classes aren't unloaded
    private static final List<Class<?>> loadedClasses = new ArrayList<>();

    static {
        System.out.println("[Combined Loader] Static initializer - loading all classes...");
        long startTime = System.currentTimeMillis();

        try {
            loadAllClasses();

            long elapsed = System.currentTimeMillis() - startTime;
            System.out.println("[Combined Loader] Loaded " + loadedClasses.size() +
                             " classes in " + elapsed + "ms");
            System.out.println("[Combined Loader] AOT archiver will process ALL these classes!");

        } catch (Exception e) {
            System.err.println("[Combined Loader] Error loading classes: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private static void loadAllClasses() throws ClassNotFoundException {
        System.out.println("[Combined Loader] Targeting ~5.5M RW objects (OOM threshold):");
        System.out.println("  - " + SIMPLE_CLASS_COUNT + " simple classes (normal app classes)");
        System.out.println("  - " + MEGA_CLASS_COUNT + " mega-classes (5K fields each)");
        System.out.println("  - " + FIELD_OBJECT_COUNT + " FieldObject classes");
        System.out.println();

        // Load simple classes
        System.out.println("[Combined Loader] Loading " + SIMPLE_CLASS_COUNT + " simple classes...");
        for (int i = 0; i < SIMPLE_CLASS_COUNT; i++) {
            String className = PACKAGE_NAME + ".GeneratedClass" + i;

            try {
                Class<?> clazz = Class.forName(className);
                loadedClasses.add(clazz);

                if ((i + 1) % 20_000 == 0) {
                    System.out.println("[Combined Loader] Loaded " + (i + 1) + " simple classes...");
                }

            } catch (ClassNotFoundException e) {
                if (i < 1000) {
                    throw e; // Error if we can't even load the first 1000
                }
                System.out.println("[Combined Loader] Loaded " + i + " simple classes (reached end)");
                break;
            }
        }

        // Load mega-classes
        System.out.println("[Combined Loader] Loading " + MEGA_CLASS_COUNT + " mega-classes...");
        for (int i = 0; i < MEGA_CLASS_COUNT; i++) {
            String className = PACKAGE_NAME + ".MegaDataClass" + i;

            try {
                Class<?> clazz = Class.forName(className);
                loadedClasses.add(clazz);

                if ((i + 1) % 200 == 0) {
                    System.out.println("[Combined Loader] Loaded " + (i + 1) + " mega-classes...");
                }

            } catch (ClassNotFoundException e) {
                System.out.println("[Combined Loader] Loaded " + i + " mega-classes (reached end)");
                break;
            }
        }

        // Load FieldObject classes
        System.out.println("[Combined Loader] Loading " + FIELD_OBJECT_COUNT + " FieldObject classes...");
        for (int i = 0; i < FIELD_OBJECT_COUNT; i++) {
            String className = PACKAGE_NAME + ".FieldObject" + i;

            try {
                Class<?> clazz = Class.forName(className);
                loadedClasses.add(clazz);

                if ((i + 1) % 1000 == 0) {
                    System.out.println("[Combined Loader] Loaded " + (i + 1) + " FieldObjects...");
                }

            } catch (ClassNotFoundException e) {
                System.out.println("[Combined Loader] Loaded " + i + " FieldObjects (reached end)");
                break;
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("\n=== Combined Class Loader - Main Execution ===");
        System.out.println("Total classes loaded: " + loadedClasses.size());
        System.out.println();

        // Print memory stats
        Runtime runtime = Runtime.getRuntime();
        long usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024);
        System.out.println("Memory Statistics:");
        System.out.println("  Max Memory: " + runtime.maxMemory() / (1024 * 1024) + "MB");
        System.out.println("  Total Memory: " + runtime.totalMemory() / (1024 * 1024) + "MB");
        System.out.println("  Used Memory: " + usedMemory + "MB");
        System.out.println();

        System.out.println("=== AOT Archiver Will Process ===");
        System.out.println("  - 300,000 simple classes (10 fields each)");
        System.out.println("  - 2,000 mega-classes (5,000 fields each)");
        System.out.println("  - 5,000 FieldObject classes");
        System.out.println("  - Total: " + loadedClasses.size() + " classes");
        System.out.println();
        System.out.println("Estimated RW/RO objects:");
        System.out.println("  Target: ~5.5M RW objects (your OOM threshold)");
        System.out.println("  Calculation: 100K classes → 3.1M RW");
        System.out.println("               300K classes → ~9.3M RW");
        System.out.println();
        System.out.println("COMPARISON TO YOUR APP:");
        System.out.println("  Your failed run:");
        System.out.println("    RW: 5,581,706 ← OOM during RO allocation");
        System.out.println("  Your successful run:");
        System.out.println("    RW: 5,064,453, RO: 10,258,666");
        System.out.println();
        System.out.println("  This test (target):");
        System.out.println("    RW: ~5,500,000 ← Should hit OOM threshold!");
        System.out.println();
        System.out.println("If we hit ~5.5-5.6M RW objects, we should reproduce the OOM!");
        System.out.println();

        // Keep alive for AOT recording
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println("Combined test completed.");
    }
}
