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::PlayerEventKind::Start,
            babalcore::PlayerEventKind::Jump,
            babalcore::PlayerEventKind::Land,
            babalcore::PlayerEventKind::Fall,
            babalcore::PlayerEventKind::CatchUp,
            babalcore::PlayerEventKind::ChangeColumn,
            babalcore::PlayerEventKind::Boost,
            babalcore::PlayerEventKind::Overdrive,
            babalcore::PlayerEventKind::PercentTime,
        ] {
            builder.add_signal(Signal {
                name: evt.as_str(),
                args: &[],
            });
        }
        for evt in vec![
            babalcore::PlayerEventKind::ThousandScore,
            babalcore::PlayerEventKind::HighScore,
            babalcore::PlayerEventKind::Finish,
        ] {
            builder.add_signal(Signal {
                name: evt.as_str(),
                args: &[SignalArgument {
                    name: "score",
                    default: Variant::from_i64(0),
                    export_info: ExportInfo::new(VariantType::I64),
                    usage: PropertyUsage::DEFAULT,
                }],
            });
        }
    }

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

    #[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) {
                        match evt.arg {
                            Some(v) => {
                                owner.emit_signal(evt.kind.as_str(), &[Variant::from_i64(v as i64)])
                            }
                            None => owner.emit_signal(evt.kind.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 is_boosting(&self, _owner: &Spatial) -> bool {
        self.player.is_boosting()
    }

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

    #[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 set_steer_factor(&mut self, _owner: &Spatial, steer_factor: f64) {
        self.player.set_steer_factor(steer_factor)
    }

    #[export]
    fn update_player_best_score(&mut self, _owner: &Spatial, best_score: i32) {
        self.player.set_best_score(best_score)
    }

    #[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()
    }

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

    #[export]
    fn time_progress_done(&mut self, _owner: &Spatial, tunnel: Variant) -> f64 {
        match TunnelWrapper::from_variant(&tunnel) {
            Ok(tunnel_wrapper) => self.player.time_progress_done(&tunnel_wrapper),
            Err(e) => {
                godot_print!("error building TunnelWrapper: {:?}", e);
                0.0
            }
        }
    }

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