//! Example: Frame-based upload workflow using memkit-co.
//!
//! This example demonstrates how to:
//! 1. Setup the coordinator
//! 2. Allocate frame data on the CPU
//! 3. Stage it for upload to the GPU
//! 4. Execute the frame loop

use memkit_co::{MkCoordinator, CoordinatorConfig, BufferingMode, MkGpuBackend};
use memkit_gpu::{DummyBackend, MkBufferUsage};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. Setup backend and coordinator
    // In a real app, you'd use VulkanBackend::new(config)
    let backend = DummyBackend::new();
    
    let config = CoordinatorConfig {
        frame_arena_size: 1024 * 1024, // 1 MB
        staging_pool_size: 4,
        buffering: BufferingMode::Double,
        ..CoordinatorConfig::default()
    };
    
    let mut coordinator = MkCoordinator::new(backend, config);
    
    println!("Coordinator initialized!");
    
    // 2. Simulation loop
    let mut offset = 0.0f32;
    
    for frame in 0..60 {
        // Begin frame - rotates buffers/arenas
        coordinator.begin_frame();
        
        // 3. Allocate data for this frame (CPU side)
        // This specific allocation uses the frame arena - fast, no-lock
        let transform_count = 100;
        let transforms_option = coordinator.frame_alloc_slice::<[f32; 4]>(transform_count);
        let transforms = transforms_option.expect("Failed to allocate frame data");
        
        // Fill data
        for t in transforms.iter_mut() {
            *t = [offset, 0.0, 0.0, 1.0];
        }
        offset += 0.1;
        
        // 4. Create a device buffer for this frame (or reuse one)
        // For this example we create one, but normally you'd have persistent device buffers
        let device_buffer = coordinator.backend().create_buffer(
            transform_count * std::mem::size_of::<[f32; 4]>(),
            MkBufferUsage::TRANSFER_DST | MkBufferUsage::STORAGE,
            memkit_gpu::MkMemoryType::DeviceLocal,
        )?;
        
        // 5. Stage upload
        // Acquire staging buffer
        let size = std::mem::size_of_val(transforms);
        let staging = coordinator.acquire_staging(size)?;
        
        // Write data
        staging.write_data(coordinator.backend(), transforms);
        
        // Queue transfer (will be batched)
        // Note: coordinator.stage_upload() is the high-level API that handles staging for you.
        // Here we show manual control using the underlying GPU handle.
        let copy_size = staging.size().min(0 + transform_count * std::mem::size_of::<[f32; 4]>());
        coordinator.backend().copy_buffer(staging.handle(), &device_buffer, copy_size)?;
        
        println!("Frame {}: Queued upload of {} bytes", frame, size);
        
        // 6. End frame - flushes batch and submits
        coordinator.end_frame()?;
    }
    
    println!("Done!");
    Ok(())
}
