/*
    Hurry Curry! - a game about cooking
    Copyright (C) 2026 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/>.

*/

use crate::{
    PacketSink,
    pathfinding::{Path, find_path_to_neighbour},
};
use hurrycurry_game_core::Game;
use hurrycurry_protocol::{
    Hand, ItemIndex, ItemLocation, PacketS, PlayerID,
    glam::{IVec2, Vec2},
};

pub struct StepState {
    label: String,
    path: Path,
    interact_position: IVec2,
    item_expected: Option<ItemIndex>,
    item_current: Option<ItemIndex>,
    hand: Hand,
    wait_timer: f32,
    interact_start_pending: bool,
    interact_stop_pending: bool,
    me: PlayerID,
}

impl StepState {
    pub fn new_idle(me: PlayerID, label: String) -> Self {
        Self::new_wait(me, 0., label)
    }
    pub fn new_wait(me: PlayerID, timer: f32, label: String) -> Self {
        Self {
            me,
            path: Path::EMPTY,
            hand: Hand(0),
            wait_timer: timer,
            interact_start_pending: false,
            interact_stop_pending: false,
            interact_position: IVec2::ZERO,
            item_expected: None,
            item_current: None,
            label,
        }
    }
    pub fn new_segment(
        game: &Game,
        me: PlayerID,
        hand: Hand,
        pos: IVec2,
        interact: f32,
        label: String,
    ) -> Option<Self> {
        let own_pos = game.players.get(&me)?.movement.position.as_ivec2();
        let path = find_path_to_neighbour(game, own_pos, pos)?;
        let destination_item = game
            .tiles
            .get(&pos)
            .and_then(|t| t.item.as_ref().map(|i| i.kind));
        Some(Self {
            label,
            me,
            path,
            hand,
            wait_timer: interact,
            interact_start_pending: true,
            interact_stop_pending: true,
            interact_position: pos,
            item_expected: destination_item,
            item_current: destination_item,
        })
    }

    pub fn is_busy(&self) -> bool {
        (self.wait_timer > 0. || self.interact_stop_pending)
            && !self.path.is_stuck(3.)
            && (self.path.is_done() || self.item_current == self.item_expected)
    }

    pub fn tick(&mut self, out: &mut PacketSink, game: &Game, dt: f32) {
        #[cfg(feature = "debug_events")]
        {
            use crate::debug_player_color;
            use hurrycurry_protocol::{DebugEvent, DebugEventDisplay};
            out.push(PacketS::Debug(DebugEvent {
                key: format!("step-label-{}", self.me),
                color: debug_player_color(self.me),
                display: DebugEventDisplay::Label {
                    pos: ItemLocation::Player(self.me, Hand(0)),
                    text: self.label.to_string(),
                },
                timeout: 1.,
            }));
            out.push(PacketS::Debug(DebugEvent {
                key: format!("step-target-{}", self.me),
                color: debug_player_color(self.me),
                display: DebugEventDisplay::Label {
                    pos: ItemLocation::Tile(self.interact_position),
                    text: format!("D={} T={:.01}", self.path.is_done() as u8, self.wait_timer),
                },
                timeout: 1.,
            }));
        }
        if self.path.is_done() {
            if self.interact_start_pending {
                self.interact_start_pending = false;
                out.push(PacketS::Interact {
                    player: self.me,
                    hand: self.hand,
                    target: Some(ItemLocation::Tile(self.interact_position)),
                });
                out.push(PacketS::Movement {
                    player: self.me,
                    dir: Vec2::ZERO,
                    boost: false,
                    pos: None,
                });
            } else if self.wait_timer > 0. {
                self.wait_timer -= dt;
            } else if self.interact_stop_pending {
                self.interact_stop_pending = false;
                out.push(PacketS::Interact {
                    player: self.me,
                    hand: self.hand,
                    target: None,
                });
            }
        } else {
            self.item_current = game
                .tiles
                .get(&self.interact_position)
                .and_then(|t| t.item.as_ref().map(|i| i.kind));

            let position = game
                .players
                .get(&self.me)
                .as_ref()
                .map(|p| p.movement.position)
                .unwrap_or_default();

            #[cfg(feature = "debug_events")]
            out.push(PacketS::Debug(self.path.debug(self.me)));
            self.path.update(out, self.me, position, dt, 1., true);
        }
    }
}
