const std = @import("std");
const Network = @import("modular_network.zig").Network;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.deinit();

    var net = Network.init(allocator);
    defer net.deinit();

    // --- ARCHITETTURA ---
    // Input(2) -> Hidden(8) -> Hidden(8) -> Hidden(4) -> Output(1)
    try net.addLayer(2, 8, 123);
    try net.addLayer(8, 8, 456);
    try net.addLayer(8, 4, 789);
    try net.addLayer(4, 1, 101);

    net.printTopology();

    // Dati XOR
    const inputs = [_][]const f32{ &.{ 0.0, 0.0 }, &.{ 0.0, 1.0 }, &.{ 1.0, 0.0 }, &.{ 1.0, 1.0 } };
    const targets = [_][]const f32{ &.{0.0}, &.{1.0}, &.{1.0}, &.{0.0} };

    std.debug.print("--- TRAINING DEEP CON VISUALIZER E DEBUG --- \n", .{});

    // --- CONFIGURAZIONE ---
    const lr: f32 = 0.2;
    const max_epochs = 50000;

    const slow_mode = true; // Attiva il rallentatore
    const export_step = 100; // Ogni quante epoche aggiorniamo
    const delay_ms = 25; // Ritardo in millisecondi

    var epoch: usize = 0;
    while (epoch <= max_epochs) : (epoch += 1) {
        var total_loss: f32 = 0.0;

        // Training step
        for (0..4) |i| {
            total_loss += try net.train(inputs[i], targets[i], lr);
        }

        // --- ZONA OUTPUT E EXPORT ---
        if (epoch % export_step == 0) {

            // 1. Stampiamo HEADER con Epoca e Loss
            std.debug.print("\n=== EPOCA {d} | Loss: {d:.6} ===\n", .{ epoch, total_loss });

            // 2. Stampiamo le PREVISIONI attuali per i 4 casi
            for (inputs) |inp| {
                const out = net.forward(inp);
                // Stampa formattata: Input -> Output
                std.debug.print("In: [{d:.0}, {d:.0}] -> Out: {d:.4}\n", .{ inp[0], inp[1], out[0] });
            }

            // 3. Esportiamo il JSON per il browser
            try net.exportJSON("network_state.json", epoch, total_loss);

            // 4. Delay per l'animazione
            if (slow_mode) {
                // Ricorda: std.Thread.sleep vuole nanosecondi
                std.Thread.sleep(delay_ms * 1_000_000);
            }
        }
    }
}
