/*
    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 .
*/
use bincode::{
    config::{standard, Configuration, Limit, LittleEndian, Varint},
    Decode, Encode,
};
use glam::{IVec2, Vec2};
use serde::{Deserialize, Serialize};
use std::{
    collections::{HashMap, HashSet},
    fmt::Display,
};
pub use glam;
pub const VERSION: (u32, u32) = (1, 0);
pub const BINCODE_CONFIG: Configuration> =
    standard().with_limit();
#[derive(
    Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(transparent)]
pub struct PlayerID(pub i64);
#[derive(
    Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(transparent)]
pub struct ItemIndex(pub usize);
#[derive(
    Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(transparent)]
pub struct TileIndex(pub usize);
#[derive(
    Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(transparent)]
pub struct RecipeIndex(pub usize);
#[derive(
    Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(transparent)]
pub struct DemandIndex(pub usize);
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct MapMetadata {
    name: String,
    players: usize,
    difficulty: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode,  Default)]
#[rustfmt::skip]
pub struct ClientGamedata {
    pub item_names: Vec,
    pub tile_names: Vec,
    pub tile_collide: Vec,
    pub tile_interact: Vec,
    pub map_names: HashSet, // for compat with game jam version
    pub maps: HashMap,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum PacketS {
    Join {
        name: String,
        character: i32,
    },
    Leave,
    Position {
        #[bincode(with_serde)]
        pos: Vec2,
        rot: f32,
        boosting: bool,
    },
    Interact {
        #[bincode(with_serde)]
        pos: Option,
    },
    Collide {
        player: PlayerID,
        #[bincode(with_serde)]
        force: Vec2,
    },
    Communicate {
        message: Option,
        persist: bool,
    },
    #[serde(skip)]
    #[bincode(skip)]
    /// For internal use only
    ReplaceHand {
        item: Option,
    },
    /// For use in replay sessions only
    ReplayTick {
        dt: f64,
    },
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
#[serde(rename_all = "snake_case")]
pub enum Message {
    Text(String),
    Item(ItemIndex),
    Effect(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum PacketC {
    Version {
        minor: u32,
        major: u32,
        supports_bincode: bool,
    },
    Init {
        id: PlayerID,
    },
    Data {
        data: ClientGamedata,
    },
    AddPlayer {
        id: PlayerID,
        #[bincode(with_serde)]
        position: Vec2,
        character: i32,
        name: String,
    },
    RemovePlayer {
        id: PlayerID,
    },
    Position {
        player: PlayerID,
        #[bincode(with_serde)]
        pos: Vec2,
        rot: f32,
        boosting: bool,
    },
    MoveItem {
        from: ItemLocation,
        to: ItemLocation,
    },
    SetItem {
        location: ItemLocation,
        item: Option,
    },
    SetProgress {
        item: ItemLocation,
        progress: Option,
        warn: bool,
    },
    UpdateMap {
        #[bincode(with_serde)]
        tile: IVec2,
        kind: Option,
        neighbors: [Option; 4],
    },
    Collide {
        player: PlayerID,
        #[bincode(with_serde)]
        force: Vec2,
    },
    Communicate {
        player: PlayerID,
        message: Option,
        persist: bool,
    },
    ServerMessage {
        text: String,
    },
    Score {
        points: i64,
        demands_failed: usize,
        demands_completed: usize,
        time_remaining: Option,
    },
    SetIngame {
        state: bool,
        lobby: bool,
    },
    Error {
        message: String,
    },
    /// For use in replay sessions only
    ReplayStart,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Copy, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum ItemLocation {
    Tile(#[bincode(with_serde)] IVec2),
    Player(PlayerID),
}
impl Display for ItemLocation {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ItemLocation::Tile(pos) => write!(f, "tile({pos})"),
            ItemLocation::Player(PlayerID(id)) => write!(f, "player({id})"),
        }
    }
}