//! sprite_storm: 10,000 moving quads, with numbers (v0.2 step 10 milestone).
//!
//! The benchmark was written BEFORE the batcher, per the guide: run it
//! against the naive v0.1 renderer, keep the number, land the batcher, run
//! the identical example, watch the number collapse. The measurement goes
//! through the public API, which proves the API did not change.
//!
//! Run: RUST_LOG=info cargo run --release --example sprite_storm
//! (info logging prints the avg frame time once a second)

use mawida::content::{AtlasBuilder, TextureData};
use mawida::{
    run, AppConfig, Camera, Color, Context, Game, GameTime, OrthographicCamera, Result, Sprite,
};

const N: usize = 10_000;

/// A 16x16 procedural test image (no asset files needed): four distinct
/// patterns so the atlas milestone is visible on screen.
fn pattern(kind: usize) -> TextureData {
    let mut rgba = Vec::with_capacity(16 * 16 * 4);
    for y in 0..16u32 {
        for x in 0..16u32 {
            let px: [u8; 4] = match kind {
                0 => {
                    // checkerboard, orange/white
                    if (x / 4 + y / 4) % 2 == 0 {
                        [255, 160, 40, 255]
                    } else {
                        [255, 255, 255, 255]
                    }
                }
                1 => {
                    // vertical stripes, teal
                    if x % 4 < 2 {
                        [40, 200, 190, 255]
                    } else {
                        [10, 60, 70, 255]
                    }
                }
                2 => {
                    // filled circle, magenta on transparent
                    let (dx, dy) = (x as f32 - 7.5, y as f32 - 7.5);
                    if dx * dx + dy * dy <= 42.0 {
                        [220, 70, 200, 255]
                    } else {
                        [0, 0, 0, 0]
                    }
                }
                _ => {
                    // diagonal gradient, green
                    let g = ((x + y) * 8).min(255) as u8;
                    [40, g.max(60), 40, 255]
                }
            };
            rgba.extend_from_slice(&px);
        }
    }
    TextureData { width: 16, height: 16, rgba }
}

struct Storm {
    sprites: Vec<Sprite>,
    camera: OrthographicCamera,
    acc: f32,
    frames: u32,
}

impl Game for Storm {
    /// The atlas milestone: four different images, ONE texture, so the
    /// backend's run-walk executes exactly once: one draw call per frame
    /// for the whole storm. Watch the backend's once-a-second log.
    fn init(&mut self, ctx: &mut Context) -> Result<()> {
        let mut atlas = AtlasBuilder::new(64, 64, 2); // 2 px gutter
        let rects = [
            atlas.add(&pattern(0))?,
            atlas.add(&pattern(1))?,
            atlas.add(&pattern(2))?,
            atlas.add(&pattern(3))?,
        ];
        let atlas_tex = atlas.build(ctx.content_mut()); // ONE id for all four
        ctx.upload_texture(atlas_tex)?;

        for (i, s) in self.sprites.iter_mut().enumerate() {
            // round-robin the four atlas regions across the storm
            *s = Sprite::new(
                atlas_tex,
                s.transform.translation.x,
                s.transform.translation.y,
                12.0,
                12.0,
            )
            .with_uv_rect(rects[i % 4]);
        }
        Ok(())
    }
    fn update(&mut self, _ctx: &mut Context, time: GameTime) -> Result<()> {
        let t = time.total_seconds() as f32;
        for (i, s) in self.sprites.iter_mut().enumerate() {
            // A slow sine swarm: every sprite moves every frame, so the
            // full upload cost is paid honestly each frame.
            let phase = i as f32 * 0.618;
            s.transform.translation.x = 640.0 + (t + phase).sin() * 600.0;
            s.transform.translation.y = 360.0 + (t * 1.3 + phase).cos() * 340.0;
        }
        // Numbers, not vibes: average frame time, once a second.
        self.acc += time.delta_seconds();
        self.frames += 1;
        if self.acc >= 1.0 {
            log::info!(
                "avg frame: {:.2} ms over {} frames",
                self.acc * 1000.0 / self.frames as f32,
                self.frames
            );
            self.acc = 0.0;
            self.frames = 0;
        }
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> Result<()> {
        for s in &self.sprites {
            ctx.draw(s, &self.camera as &dyn Camera)?;
        }
        Ok(())
    }
}

fn main() -> Result<()> {
    let sprites = (0..N)
        .map(|i| {
            let hue = (i % 7) as f32 / 7.0;
            Sprite::solid(
                Color::rgb(0.4 + 0.6 * hue, 0.9 - 0.5 * hue, 0.6),
                0.0,
                0.0,
                8.0,
                8.0,
            )
        })
        .collect();

    run(
        Storm {
            sprites,
            camera: OrthographicCamera::new(1280.0, 720.0),
            acc: 0.0,
            frames: 0,
        },
        AppConfig {
            title: "Mawida - sprite storm (10,000 quads)".to_string(),
            width: 1280,
            height: 720,
            ..Default::default()
        },
    )
}
