//! walker: a character walking in place (v0.2 step 11, milestone 1).
//!
//! Flipbook animation is bookkeeping, not motion: the hero's transform
//! never changes, only WHICH rectangle of the sheet is showing. Left/Right
//! arrows flip the facing instantly, mid-stride, because the flip is a uv
//! transform (flip_x) and the animation clock never resets. The whole scene
//! is one instance in one draw call (watch the backend's log).
//!
//! The 6-frame walk sheet is generated procedurally at init so the example
//! needs zero asset files: a stick figure whose legs and arms swing with
//! the frame's phase.
//!
//! Run: RUST_LOG=info cargo run --example walker

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

const FRAMES: u32 = 6;
const CELL: u32 = 32;

/// Draw a 1px line onto the RGBA canvas (naive DDA; fine for 32px art).
fn line(rgba: &mut [u8], w: u32, x0: f32, y0: f32, x1: f32, y1: f32) {
    let steps = ((x1 - x0).abs().max((y1 - y0).abs()) * 2.0).ceil() as i32;
    for s in 0..=steps {
        let t = s as f32 / steps.max(1) as f32;
        let (x, y) = (x0 + (x1 - x0) * t, y0 + (y1 - y0) * t);
        if x >= 0.0 && y >= 0.0 && (x as u32) < w && (y as u32) < w {
            let i = ((y as u32 * w + x as u32) * 4) as usize;
            rgba[i..i + 4].copy_from_slice(&[240, 240, 255, 255]);
        }
    }
}

/// The 6-frame walk sheet: head, body, and limbs swinging with the phase.
fn make_walk_sheet() -> TextureData {
    let (w, h) = (CELL * FRAMES, CELL);
    let mut rgba = vec![0u8; (w * h * 4) as usize];
    for f in 0..FRAMES {
        let phase = f as f32 / FRAMES as f32 * std::f32::consts::TAU;
        let swing = phase.sin() * 6.0; // leg/arm swing, pixels
        let x0 = (f * CELL) as f32 + 16.0; // this frame's center x
        let bob = (phase * 2.0).cos().abs() * 1.5; // body bob, twice per cycle

        // one frame's figure, drawn into the shared canvas row
        let head_y = 6.0 + bob;
        for dy in -3i32..=3 {
            for dx in -3i32..=3 {
                if dx * dx + dy * dy <= 9 {
                    let (x, y) = (x0 + dx as f32, head_y + dy as f32);
                    let i = ((y as u32 * w + x as u32) * 4) as usize;
                    rgba[i..i + 4].copy_from_slice(&[240, 240, 255, 255]);
                }
            }
        }
        let hip_y = 20.0 + bob;
        line(&mut rgba, w, x0, head_y + 3.0, x0, hip_y); // body
        line(&mut rgba, w, x0, hip_y, x0 + swing, 30.0); // leg 1
        line(&mut rgba, w, x0, hip_y, x0 - swing, 30.0); // leg 2
        let sh_y = 12.0 + bob; // shoulders; arms swing opposite the legs
        line(&mut rgba, w, x0, sh_y, x0 - swing * 0.7, sh_y + 7.0);
        line(&mut rgba, w, x0, sh_y, x0 + swing * 0.7, sh_y + 7.0);
    }
    TextureData { width: w, height: h, rgba }
}

struct Walker {
    sheet: Option<SpriteSheet>,
    walk: Animation,
    hero: Sprite,
    facing_left: bool,
    camera: OrthographicCamera,
}

impl Game for Walker {
    fn init(&mut self, ctx: &mut Context) -> Result<()> {
        let data = make_walk_sheet();
        let (w, h) = (data.width, data.height);
        let tex = ctx.content_mut().insert_rgba(w, h, data.rgba);
        ctx.upload_texture(tex)?;
        self.sheet = Some(SpriteSheet {
            texture: tex,
            sheet_size: (w, h),
            frame_size: (CELL, CELL),
            columns: FRAMES,
            frames: FRAMES,
            pad: 0,
        });
        self.hero = Sprite::new(tex, 336.0, 236.0, 128.0, 128.0); // drawn at 4x
        Ok(())
    }

    fn update(&mut self, ctx: &mut Context, time: GameTime) -> Result<()> {
        self.walk.update(time.delta_seconds());
        if ctx.input().is_down(mawida::Key::Left) {
            self.facing_left = true;
        }
        if ctx.input().is_down(mawida::Key::Right) {
            self.facing_left = false;
        }
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> Result<()> {
        let Some(sheet) = &self.sheet else { return Ok(()) };
        let mut rect = sheet.frame_rect(self.walk.frame());
        if self.facing_left {
            rect = flip_x(rect); // a negative-width uv rect: facing, one negation
        }
        self.hero.set_uv_rect(rect);
        ctx.draw(&self.hero, &self.camera as &dyn Camera)
    }
}

fn main() -> Result<()> {
    run(
        Walker {
            sheet: None,
            walk: Animation::new(0..FRAMES, 10.0), // 10 fps walk cycle
            hero: Sprite::solid(Color::WHITE, 0.0, 0.0, 1.0, 1.0), // replaced at init
            facing_left: false,
            camera: OrthographicCamera::new(800.0, 600.0),
        },
        AppConfig {
            title: "Mawida - walker (flipbook animation)".to_string(),
            ..Default::default()
        },
    )
}
