// Copyright (C) 2025 Christian Mauduit <ufoot@ufoot.org>

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use shortestpath::mesh_2d::Index2D;
use shortestpath::mesh_3d::Index3D;
use shortestpath::{Gradient, Mesh};
use std::collections::HashMap;
use uuid::Uuid;

use crate::{Battlefield, GameState, Place, Place2D, Place3D, Team};

#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct World<M: Mesh, P: Place> {
    mesh: M,
    battlefield: Battlefield<P>,
    teams: HashMap<Uuid, Team>,
}

impl<M: Index2D + Mesh> World<M, Place2D> {
    pub fn new_2d(mesh: M) -> Result<Self, shortestpath::Error> {
        let battlefield = Battlefield::from_mesh_2d(&mesh)?;
        Ok(Self {
            mesh,
            battlefield,
            teams: HashMap::new(),
        })
    }
}

impl<M: Index3D + Mesh> World<M, Place3D> {
    pub fn new_3d(mesh: M) -> Result<Self, shortestpath::Error> {
        let battlefield = Battlefield::from_mesh_3d(&mesh)?;
        Ok(Self {
            mesh,
            battlefield,
            teams: HashMap::new(),
        })
    }
}

impl<M: Mesh, P: Place> World<M, P> {
    pub fn mesh(&self) -> &M {
        &self.mesh
    }

    pub fn battlefield(&self) -> &Battlefield<P> {
        &self.battlefield
    }

    pub fn add_team(&mut self, team_id: Uuid, color: u32, name: String) {
        let gradient = Gradient::from_mesh(&self.mesh);
        let team = Team::new(gradient, color, name);
        self.teams.insert(team_id, team);
    }

    pub fn remove_team(&mut self, team_id: &Uuid) -> Option<Team> {
        self.teams.remove(team_id)
    }

    pub fn get_team(&self, team_id: &Uuid) -> Option<&Team> {
        self.teams.get(team_id)
    }

    pub fn get_team_mut(&mut self, team_id: &Uuid) -> Option<&mut Team> {
        self.teams.get_mut(team_id)
    }

    pub fn place_count(&self) -> usize {
        self.mesh.len()
    }

    pub fn team_count(&self) -> usize {
        self.teams.len()
    }
}

impl<M: Mesh, P: Place> GameState for World<M, P> {
    fn do_step(&mut self, _odd_even: bool) {
        // TODO: implement game logic
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use shortestpath::mesh_2d::Compact2D;

    #[test]
    fn test_world_2d_80x50() {
        let width = 80;
        let height = 50;

        let mesh = Compact2D::new_full(width, height);
        let world = World::new_2d(mesh).expect("Failed to create 2D world");

        assert_eq!(world.place_count(), width * height);
        assert_eq!(world.team_count(), 0);
    }
}
