use super::input_wrapper::*;
use super::tunnel_wrapper::*;
use babalcore::*;
use gdnative::prelude::*;

#[derive(NativeClass)]
#[inherit(Spatial)]
#[register_with(Self::register_signals)]
pub struct Ball {
    player: Player,
    virtual_pos: Vector3,
}

#[methods]
impl Ball {
    fn new(_owner: &Spatial) -> Self {
        Ball {
            player: Player::new(),
            virtual_pos: Vector3::new(0.0, 0.0, 0.0),
        }
    }

    fn register_signals(builder: &ClassBuilder<Self>) {
        for evt in vec![
            babalcore::PlayerEvent::Start,
            babalcore::PlayerEvent::Jump,
            babalcore::PlayerEvent::Land,
            babalcore::PlayerEvent::Fall,
            babalcore::PlayerEvent::CatchUp,
            babalcore::PlayerEvent::ChangeColumn,
            babalcore::PlayerEvent::Boost,
        ] {
            builder.add_signal(Signal {
                name: evt.as_str(),
                args: &[],
            });
        }
    }

    #[export]
    fn _ready(&self, owner: &Spatial) {
        godot_print!("Ball is ready, virtual_pos=\"{:?}\".", self.virtual_pos);
        owner.emit_signal(PlayerEvent::Start.as_str(), &[]);
    }

    #[export]
    fn tick(&mut self, owner: &Spatial, delta: f64, tunnel: Variant, input: Variant) {
        match TunnelWrapper::from_variant(&tunnel) {
            Ok(tunnel_wrapper) => match InputWrapper::from_variant(&input) {
                Ok(mut input_wrapper) => {
                    for evt in self.player.tick(delta, &tunnel_wrapper, &mut input_wrapper) {
                        owner.emit_signal(evt.as_str(), &[]);
                    }
                }
                Err(e) => godot_print!("error building InputWrapper: {:?}", e),
            },
            Err(e) => godot_print!("error building TunnelWrapper: {:?}", e),
        }
    }

    #[export]
    fn position(&self, _owner: &Spatial) -> Vector3 {
        self.player.position()
    }

    #[export]
    fn speed(&self, _owner: &Spatial) -> Vector3 {
        self.player.speed()
    }

    #[export]
    fn update_player_position(&mut self, owner: &Spatial, pos: Vector3) {
        let mut translation = owner.translation();
        translation.y = pos.y;
        owner.set_translation(translation);
    }

    #[export]
    fn level_col(&mut self, _owner: &Spatial, width: usize) -> isize {
        self.player.level_col(width)
    }

    #[export]
    fn level_row(&mut self, _owner: &Spatial) -> isize {
        self.player.level_row()
    }

    #[export]
    fn score(&mut self, _owner: &Spatial) -> i32 {
        self.player.score()
    }

    #[export]
    fn best_row(&mut self, _owner: &Spatial) -> isize {
        self.player.best_row()
    }
}
