use nalgebra_glm::Vec2;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TileData {
    pub tile_id: u32,
    pub flip_x: bool,
    pub flip_y: bool,
}

impl TileData {
    pub fn new(tile_id: u32) -> Self {
        Self {
            tile_id,
            flip_x: false,
            flip_y: false,
        }
    }

    pub fn with_flip_x(mut self, flip_x: bool) -> Self {
        self.flip_x = flip_x;
        self
    }

    pub fn with_flip_y(mut self, flip_y: bool) -> Self {
        self.flip_y = flip_y;
        self
    }
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Tilemap {
    pub position: Vec2,
    pub tile_size: Vec2,
    pub grid_width: u32,
    pub grid_height: u32,
    pub tiles: Vec<Option<TileData>>,
    pub texture_index: u32,
    pub sheet_columns: u32,
    pub sheet_rows: u32,
    pub depth: f32,
    pub color: [f32; 4],
    pub uv_max: Vec2,
}

impl Default for Tilemap {
    fn default() -> Self {
        Self {
            position: Vec2::new(0.0, 0.0),
            tile_size: Vec2::new(16.0, 16.0),
            grid_width: 0,
            grid_height: 0,
            tiles: Vec::new(),
            texture_index: 0,
            sheet_columns: 1,
            sheet_rows: 1,
            depth: 0.0,
            color: [1.0, 1.0, 1.0, 1.0],
            uv_max: Vec2::new(1.0, 1.0),
        }
    }
}

impl Tilemap {
    pub fn new(position: Vec2, tile_size: Vec2, grid_width: u32, grid_height: u32) -> Self {
        let tile_count = (grid_width * grid_height) as usize;
        Self {
            position,
            tile_size,
            grid_width,
            grid_height,
            tiles: vec![None; tile_count],
            ..Default::default()
        }
    }

    pub fn with_texture(mut self, texture_index: u32) -> Self {
        self.texture_index = texture_index;
        self
    }

    pub fn with_sheet_layout(mut self, columns: u32, rows: u32) -> Self {
        self.sheet_columns = columns;
        self.sheet_rows = rows;
        self
    }

    pub fn with_depth(mut self, depth: f32) -> Self {
        self.depth = depth;
        self
    }

    pub fn with_color(mut self, color: [f32; 4]) -> Self {
        self.color = color;
        self
    }

    pub fn with_uv_max(mut self, uv_max: Vec2) -> Self {
        self.uv_max = uv_max;
        self
    }

    pub fn set_tile(&mut self, col: u32, row: u32, tile_data: Option<TileData>) {
        if col < self.grid_width && row < self.grid_height {
            let index = (row * self.grid_width + col) as usize;
            self.tiles[index] = tile_data;
        }
    }

    pub fn get_tile(&self, col: u32, row: u32) -> Option<&TileData> {
        if col < self.grid_width && row < self.grid_height {
            let index = (row * self.grid_width + col) as usize;
            self.tiles[index].as_ref()
        } else {
            None
        }
    }

    pub fn fill(&mut self, tile_data: TileData) {
        for tile in &mut self.tiles {
            *tile = Some(tile_data.clone());
        }
    }
}
