/*
    Hurry Curry! - a game about cooking
    Copyright (C) 2025 Hurry Curry! Contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

*/
pub mod book;
pub mod bot;
pub mod campaign;
pub mod conveyor;
pub mod customers;
pub mod environment_effect;
pub mod item_portal;
pub mod pedestrians;
pub mod player_portal;
pub mod tram;
pub mod tutorial;

use crate::{entity::pedestrians::Pedestrians, scoreboard::ScoreboardStore};
use anyhow::Result;
use book::Book;
use campaign::{Gate, Map};
use conveyor::Conveyor;
use customers::Customers;
use environment_effect::{EnvironmentController, EnvironmentEffectController};
use hurrycurry_client_lib::Game;
use hurrycurry_data::{Serverdata, entities::EntityDecl};
use hurrycurry_locale::TrError;
use hurrycurry_protocol::{Character, PacketC, PacketS, PlayerID, glam::IVec2};
use item_portal::ItemPortal;
use player_portal::PlayerPortal;
use std::{
    any::Any,
    collections::{HashMap, VecDeque},
};
use tram::Tram;

pub type DynEntity = Box<dyn Entity + Send + Sync + 'static>;
pub type Entities = Vec<DynEntity>;

pub struct EntityContext<'a> {
    pub game: &'a mut Game,
    pub serverdata: &'a Serverdata,
    pub packet_out: &'a mut VecDeque<PacketC>,
    pub packet_in: &'a mut VecDeque<PacketS>,
    pub score_changed: &'a mut bool,
    pub load_map: &'a mut Option<String>,
    pub scoreboard: &'a ScoreboardStore,
    pub replies: Option<&'a mut Vec<PacketC>>,
    pub dt: f32,
}

pub trait Entity: Any {
    fn tick(&mut self, _c: EntityContext<'_>) -> Result<()> {
        Ok(())
    }
    fn finished(&self) -> bool {
        false
    }
    fn constructor(&mut self, _c: EntityContext<'_>) {}
    fn destructor(&mut self, _c: EntityContext<'_>) {}
    fn interact(
        &mut self,
        _c: EntityContext<'_>,
        _pos: Option<IVec2>,
        _player: PlayerID,
    ) -> Result<bool, TrError> {
        Ok(false)
    }
}

pub fn construct_entity(decl: &EntityDecl) -> DynEntity {
    match decl.to_owned() {
        EntityDecl::Book { pos } => Box::new(Book(pos)),
        EntityDecl::ItemPortal { from, to } => Box::new(ItemPortal { from, to }),
        EntityDecl::PlayerPortal { from, to } => Box::new(PlayerPortal { from, to }),
        EntityDecl::Conveyor { from, to, speed } => Box::new(Conveyor {
            from,
            to,
            max_cooldown: 1. / speed.unwrap_or(2.),
            cooldown: 0.,
        }),
        EntityDecl::Map { name, pos } => Box::new(Map { pos, name }),
        EntityDecl::Gate {
            condition,
            pos,
            blocker_tile,
        } => Box::new(Gate {
            condition,
            unlocked: false,
            pos,
            blocker_tile,
            active: true,
        }),
        EntityDecl::Customers { scaling_factor, .. } => {
            Box::new(Customers::new(scaling_factor.unwrap_or(0.5)))
        }
        EntityDecl::EnvironmentEffect(config) => Box::new(EnvironmentEffectController::new(config)),
        EntityDecl::Environment(names) => Box::new(EnvironmentController(names)),
        EntityDecl::Tram {
            length,
            color,
            points,
            smoothing,
            spacing,
        } => Box::new(Tram {
            length,
            character: Character {
                color: color.unwrap_or_default(),
                hairstyle: 0,
                headwear: 0,
            },
            ids: Vec::new(),
            points,
            progress: 0.,
            spacing,
            smoothing,
        }),
        EntityDecl::Pedestrians {
            spawn_delay,
            spawn_delay_stdev,
            points,
            speed,
        } => Box::new(Pedestrians {
            players: HashMap::new(),
            points,
            spawn_delay,
            spawn_delay_stdev: spawn_delay_stdev.unwrap_or(0.),
            cooldown: 0.,
            speed: speed.unwrap_or(0.6),
        }),
    }
}
