use bevy::prelude::*;
use client::ComponentSyncMode;
use lightyear::prelude::*;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct ChatMessage(pub String);

/// Identifies which player something belongs to.
#[derive(Component, Serialize, Deserialize, Clone, Debug, PartialEq, Reflect)]
pub struct PlayerId(ClientId);

/// A component that will store the transform data of the player.
#[derive(Component, Serialize, Deserialize, Clone, Debug, PartialEq, Reflect)]
pub struct PlayerTransformData {
    pos: Vec3,
    rot: Quat,
}
impl Linear for PlayerTransformData {
    fn lerp(start: &Self, other: &Self, t: f32) -> Self {
        Self {
            pos: start.pos.lerp(other.pos, t),
            rot: start.rot.lerp(other.rot, t),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Direction {
    pub(crate) up: bool,
    pub(crate) down: bool,
    pub(crate) left: bool,
    pub(crate) right: bool,
}

/// The `InputProtocol` needs to be an enum of the various inputs that the client can send to the server.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Inputs {
    Direction(Direction),
    None,
}

/// A channel for sending chat messages.
#[derive(Channel)]
pub struct ChatChannel;

pub struct ProtocolPlugin;
impl Plugin for ProtocolPlugin {
    fn build(&self, app: &mut bevy::prelude::App) {
        app.register_message::<ChatMessage>(ChannelDirection::Bidirectional);

        app.add_plugins(InputPlugin::<Inputs>::default());

        app.register_component::<PlayerId>(ChannelDirection::ServerToClient)
            .add_prediction(ComponentSyncMode::Once)
            .add_interpolation(ComponentSyncMode::Once);

        app.register_component::<PlayerTransformData>(ChannelDirection::ServerToClient)
            .add_prediction(ComponentSyncMode::Full)
            .add_interpolation(ComponentSyncMode::Full)
            .add_linear_interpolation_fn();

        app.add_channel::<ChatChannel>(ChannelSettings {
            mode: ChannelMode::OrderedReliable(ReliableSettings::default()),
            ..default()
        })
    }
}
