use godot::classes::{CharacterBody3D, ICharacterBody3D};
use godot::obj::WithBaseField;
use godot::prelude::*;

use crate::globals;

#[derive(GodotClass)]
#[class(base=CharacterBody3D)]
struct PlayerPlaceholder {
    base: Base<CharacterBody3D>,
    #[export]
    owner_id: i32,
    jumping: bool,
    input_direction: Vector2,
}

#[godot_api]
impl ICharacterBody3D for PlayerPlaceholder {
    fn init(base: Base<CharacterBody3D>) -> Self {
        Self {
            owner_id: 0,
            base,
            jumping: false,
            input_direction: Vector2::new(0.0, 0.0),
        }
    }

    fn ready(&mut self) {
        if let Some(mut multiplayer) = self.base().get_multiplayer() {
            self.owner_id = multiplayer.get_unique_id();
        }
    }

    fn physics_process(&mut self, delta: f64) {
        if !self.base().is_on_floor() {
            let new_gravity = self.base().get_gravity() * delta as f32;
            let new_velocity = self.base().get_velocity() + new_gravity;
            self.base_mut().set_velocity(new_velocity);
        }

        if self.base().is_on_floor() && self.jumping {
            let mut new_velocity = self.base().get_velocity();
            new_velocity.y = globals::DEFAULT_JUMP_VELOCITY;
            self.base_mut().set_velocity(new_velocity);
        }
        self.jumping = false;

        if self.base().is_on_floor() {
            let direction = self.base().get_transform().basis
                * Vector3::new(self.input_direction.x, 0.0, self.input_direction.y);
            if direction.length() > 0.0 {
                let direction = direction.normalized();
                let new_velocity = Vector3::new(
                    direction.x * globals::DEFAULT_CHARACTER_SPEED,
                    self.base().get_velocity().y,
                    direction.z * globals::DEFAULT_CHARACTER_SPEED,
                );
                self.base_mut().set_velocity(new_velocity);
            } else {
                let moved = self
                    .base()
                    .get_velocity()
                    .clone()
                    .move_toward(Vector3::new(0.0, 0.0, 0.0), globals::DEFAULT_CHARACTER_SPEED);
                let new_velocity = Vector3::new(moved.x, self.base().get_velocity().y, moved.z);
                self.base_mut().set_velocity(new_velocity);
            }
        }

        self.base_mut().move_and_slide();
    }
}

#[godot_api]
impl PlayerPlaceholder {
    #[rpc(any_peer, call_local, unreliable)]
    fn jump(&mut self) {
        self.jumping = true
    }

    #[rpc(any_peer, call_local, unreliable)]
    fn set_input_direction(&mut self, new_input_direction: Vector2) {
        self.input_direction = new_input_direction;
    }

    #[rpc(any_peer, call_local, unreliable)]
    fn set_rotation_y(&mut self, y: f32) {
        let mut new_rotation = self.base().get_rotation();
        new_rotation.y = y;
        self.base_mut().set_rotation(new_rotation);
    }

    #[rpc(any_peer, call_local, unreliable)]
    fn set_rotation_x(&mut self, x: f32) {
        let mut camera_mount = self.base().get_node_as::<Node3D>("BulletStartingPoint");
        let mut new_rotation = camera_mount.get_rotation();
        new_rotation.x = x;
        camera_mount.set_rotation(new_rotation);
    }

    #[rpc(any_peer, call_local, unreliable_ordered)]
    fn shoot(&mut self) {
        let bullet_starting_poing = match self.base().find_child("BulletStartingPoint") {
            Some(node) => node,
            None => return,
        };
        let casted_bullet_node = bullet_starting_poing.cast::<Node3D>();
        let mut map = match self.base().find_parent("Map") {
            Some(map) => map,
            None => return,
        };
        let args = &[
            casted_bullet_node.to_variant(),
            200.to_variant(),
            50.to_variant(),
        ];
        map.call("spawn_bullet", args);
    }
}
