/*
    Hurry Curry! - a game about cooking
    Copyright 2024 metamuffin
    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 .
*/
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 player_portal;
pub mod tutorial;
use crate::{data::ItemTileRegistry, scoreboard::ScoreboardStore, TrError};
use anyhow::{anyhow, Result};
use book::Book;
use campaign::{Gate, GateCondition, Map};
use conveyor::Conveyor;
use customers::Customers;
use environment_effect::{EnvironmentController, EnvironmentEffect, EnvironmentEffectController};
use hurrycurry_client_lib::Game;
use hurrycurry_protocol::{
    glam::{IVec2, Vec2},
    PacketC, PacketS, PlayerID,
};
use item_portal::ItemPortal;
use player_portal::PlayerPortal;
use serde::{Deserialize, Serialize};
use std::{any::Any, collections::VecDeque};
pub type DynEntity = Box;
pub type Entities = Vec;
pub struct EntityContext<'a> {
    pub game: &'a mut Game,
    pub packet_out: &'a mut VecDeque,
    pub packet_in: &'a mut VecDeque,
    pub score_changed: &'a mut bool,
    pub load_map: &'a mut Option,
    pub scoreboard: &'a ScoreboardStore,
    pub dt: f32,
}
pub trait Entity: Any {
    fn tick(&mut self, _c: EntityContext<'_>) -> Result<()> {
        Ok(())
    }
    fn finished(&self) -> bool {
        false
    }
    fn destructor(&mut self, _c: EntityContext<'_>) {}
    fn interact(
        &mut self,
        _c: EntityContext<'_>,
        _pos: Option,
        _player: PlayerID,
    ) -> Result {
        Ok(false)
    }
}
// macro_rules! entities {
//     ($($e:ident),*) => {
//         pub enum DynEntity { $($e($e)),* }
//         impl Entity for DynEntity {
//             fn tick(&mut self, c: EntityContext<'_>) -> Result<()> {
//                 match self { $(DynEntity::$e(x) => x.tick(c)),*, }
//             }
//             fn destructor(&mut self, c: EntityContext<'_>) {
//                 match self { $(DynEntity::$e(x) => x.destructor(c)),*, }
//             }
//         }
//     };
// }
// entities!(
//     Conveyor,
//     ItemPortal,
//     PlayerPortal,
//     Customers,
//     EnvironmentEffectController,
//     EnvironmentController
// );
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum EntityDecl {
    Conveyor {
        from: Option,
        to: Option,
        filter_dir: Option,
        filter: Option,
        dir: Option,
        speed: Option,
    },
    ItemPortal {
        from: Option,
        to: IVec2,
    },
    PlayerPortal {
        from: Option,
        to: Vec2,
    },
    Customers {},
    Map {
        name: String,
        location: Option,
    },
    EnvironmentEffect(EnvironmentEffect),
    Environment(Vec),
    Gate {
        location: Option,
        condition: GateCondition,
    },
    Book,
}
pub fn construct_entity(
    pos: Option,
    decl: &EntityDecl,
    reg: &ItemTileRegistry,
) -> Result {
    Ok(match decl.to_owned() {
        EntityDecl::Book => Box::new(Book(pos.ok_or(anyhow!("book is tile entity"))?)),
        EntityDecl::ItemPortal { from, to } => Box::new(ItemPortal {
            from: from
                .or(pos)
                .ok_or(anyhow!("Item portal start without start"))?,
            to,
        }),
        EntityDecl::PlayerPortal { from, to } => Box::new(PlayerPortal {
            from: from
                .or(pos.map(|v| v.as_vec2()))
                .ok_or(anyhow!("Player portal without start"))?,
            to,
        }),
        EntityDecl::Conveyor {
            from,
            to,
            speed,
            dir,
            filter,
            filter_dir,
        } => {
            let from = from.or(pos).ok_or(anyhow!("Conveyor has no start"))?;
            let to = to
                .or(dir.map(|s| s + from))
                .ok_or(anyhow!("Conveyor has no destination"))?;
            Box::new(Conveyor {
                from,
                to,
                max_cooldown: 1. / speed.unwrap_or(2.),
                filter_tile: filter_dir.map(|o| to + o),
                filter_item: filter.map(|name| reg.register_item(name)),
                cooldown: 0.,
            })
        }
        EntityDecl::Map { name, location } => Box::new(Map {
            location: location
                .or(pos.map(|p| p.as_vec2() + 0.5))
                .ok_or(anyhow!("no location"))?,
            name,
        }),
        EntityDecl::Gate {
            condition,
            location,
        } => Box::new(Gate {
            condition,
            unlocked: false,
            location: location.or(pos).ok_or(anyhow!("no location"))?,
            blocker_tile: reg.register_tile("fence".to_string()),
            active: true,
        }),
        EntityDecl::Customers {} => Box::new(Customers::new()?),
        EntityDecl::EnvironmentEffect(config) => Box::new(EnvironmentEffectController::new(config)),
        EntityDecl::Environment(names) => Box::new(EnvironmentController(names)),
    })
}