use std::f64::consts::PI;

use godot::classes::{Marker2D, Node, PathFollow2D, Timer};
use godot::global::{randf, randf_range};
use godot::prelude::*;

use crate::mob::Mob;
use crate::player::Player;

#[derive(GodotClass)]
#[class(init, base=Node)]
pub struct MainScene {
  #[export]
  mob_scene: OnEditor<Gd<PackedScene>>,
  #[var]
  score: i64,

  // required
  base: Base<Node>,
}

#[godot_api]
impl INode for MainScene {
  fn ready(&mut self) {
    let player = self.base().get_node_as::<Player>("Player");
    player.signals().hit().connect_other(self, Self::game_over);

    let start_timer = self.base().get_node_as::<Timer>("StartTimer");
    let score_timer = self.base().get_node_as::<Timer>("ScoreTimer");
    let mob_timer = self.base().get_node_as::<Timer>("MobTimer");

    start_timer
      .signals()
      .timeout()
      .connect_other(self, Self::on_start_timer_timeout);
    score_timer
      .signals()
      .timeout()
      .connect_other(self, Self::on_score_timer_timeout);
    mob_timer
      .signals()
      .timeout()
      .connect_other(self, Self::on_mob_timer_timeout);

    self.new_game();
  }
}

#[godot_api]
impl MainScene {
  fn game_over(&mut self) {
    self.base().get_node_as::<Timer>("ScoreTimer").stop();
    self.base().get_node_as::<Timer>("MobTimer").stop();
  }

  fn new_game(&mut self) {
    let mut player = self.base().get_node_as::<Player>("Player");
    let start_position = self.base().get_node_as::<Marker2D>("StartPosition");
    player.bind_mut().start(start_position.get_position());

    self.score = 0;
    self.base().get_node_as::<Timer>("ScoreTimer").start();
    self.base().get_node_as::<Timer>("MobTimer").start();
  }

  fn on_start_timer_timeout(&mut self) {
    self.new_game();
  }

  fn on_score_timer_timeout(&mut self) {
    self.score += 1;
  }

  fn on_mob_timer_timeout(&mut self) {
    let mut mob = self.mob_scene.instantiate_as::<Mob>();

    let mut mob_spawn_location = self.base().get_node_as::<PathFollow2D>("MobPath/MobSpawnLocation");
    mob_spawn_location.set_progress_ratio(randf() as f32);

    let mut direction = mob_spawn_location.get_rotation() as f64 + (PI / 2.0);
    direction += randf_range(-PI / 4.0, PI / 4.0);

    mob.set_position(mob_spawn_location.get_position());
    mob.set_rotation(direction as f32);

    let velocity = Vector2::new(randf_range(150.0, 250.0) as f32, 0.0);
    mob.set_linear_velocity(velocity.rotated(direction as f32));

    self.base_mut().add_child(&mob);
  }
}

#[derive(GodotClass)]
#[class(init, base=Node)]
struct CrashFucker {
  // required
  base: Base<Node>,
}
