use crate::common::{CDEG_RAD, Direction, Rect};
use crate::framework::error::GameResult;
use crate::game::caret::CaretType;
use crate::game::npc::boss::BossNPC;
use crate::game::npc::list::NPCList;
use crate::game::npc::NPC;
use crate::game::player::Player;
use crate::game::shared_game_state::SharedGameState;
use crate::util::rng::RNG;

impl NPC {
    pub(crate) fn tick_n108_balfrog_projectile(&mut self, state: &mut SharedGameState) -> GameResult {
        if self.action_counter > 300 || (self.flags.0 & 0xff) != 0 {
            self.cond.set_alive(false);
            state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
        }

        self.x += self.vel_x;
        self.y += self.vel_y;

        self.action_counter += 1;
        self.animate(1, 0, 2);

        self.anim_rect = state.constants.npc.n108_balfrog_projectile[self.anim_num as usize];

        Ok(())
    }
}

impl BossNPC {
    pub(crate) fn tick_b02_balfrog(
        &mut self,
        state: &mut SharedGameState,
        players: [&mut Player; 2],
        npc_list: &NPCList,
    ) {
        match self.parts[0].action_num {
            0 => {
                self.hurt_sound[0] = 52;
                self.parts[0].x = 6 * 0x2000 + state.constants.game.tile_offset_x * 0x2000;
                self.parts[0].y = 12 * 0x2000 + 0x1000;
                self.parts[0].direction = Direction::Right;
                self.parts[0].display_bounds =
                    Rect { left: 48 * 0x200, top: 48 * 0x200, right: 32 * 0x200, bottom: 0x2000 };
                self.parts[0].hit_bounds = Rect { left: 24 * 0x200, top: 0x2000, right: 24 * 0x200, bottom: 0x2000 };
                self.parts[0].size = 3;
                self.parts[0].exp = 1;
                self.parts[0].event_num = 1000;
                self.parts[0].npc_flags.set_event_when_killed(true);
                self.parts[0].npc_flags.set_show_damage(true);
                self.parts[0].life = 300;
            }
            10 => {
                self.parts[0].action_num = 11;
                self.parts[0].anim_num = 3;
                self.parts[0].cond.set_alive(true);
                self.parts[0].anim_rect = state.constants.npc.b02_balfrog[9];

                self.parts[1].cond.set_alive(true);
                self.parts[1].cond.set_damage_boss(true);
                self.parts[1].damage = 5;

                self.parts[2].cond.set_alive(true);
                self.parts[2].damage = 5;

                let mut npc = NPC::create(4, &state.npc_table);

                for _ in 0..8 {
                    npc.cond.set_alive(true);
                    npc.direction = Direction::Left;
                    npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                    npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                    npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
                    npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;

                    let _ = npc_list.spawn(0x100, npc.clone());
                }
            }
            20 | 21 => {
                if self.parts[0].action_num == 20 {
                    self.parts[0].action_num = 21;
                    self.parts[0].action_counter = 0
                }

                self.parts[0].action_counter += 1;
                if (self.parts[0].action_counter / 2 % 2) != 0 {
                    self.parts[0].anim_num = 3;
                } else {
                    self.parts[0].anim_num = 0;
                }
            }
            100 | 101 => {
                if self.parts[0].action_num == 100 {
                    self.parts[0].action_num = 101;
                    self.parts[0].action_counter = 0;
                    self.parts[0].anim_num = 1;
                    self.parts[0].vel_x = 0;
                }

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter > 50 {
                    self.parts[0].action_num = 102;
                    self.parts[0].anim_counter = 0;
                    self.parts[0].anim_num = 2;
                }
            }
            102 => {
                self.parts[0].anim_counter += 1;

                if self.parts[0].anim_counter > 10 {
                    self.parts[0].action_num = 103;
                    self.parts[0].anim_counter = 0;
                    self.parts[0].anim_num = 1;
                }
            }
            103 => {
                self.parts[0].anim_counter += 1;
                if self.parts[0].anim_counter > 4 {
                    self.parts[0].action_num = 104;
                    self.parts[0].anim_num = 5;
                    self.parts[0].vel_x = self.parts[0].direction.vector_x() * 0x200;
                    self.parts[0].vel_y = -0x400;
                    self.parts[0].display_bounds.top = 64 * 0x200;
                    self.parts[0].display_bounds.bottom = 24 * 0x200;

                    state.sound_manager.play_sfx(25);
                }
            }
            104 => {
                if self.parts[0].direction == Direction::Left && self.parts[0].flags.hit_left_wall() {
                    self.parts[0].direction = Direction::Right;
                    self.parts[0].vel_x = 0x200;
                }

                if self.parts[0].direction == Direction::Right && self.parts[0].flags.hit_right_wall() {
                    self.parts[0].direction = Direction::Left;
                    self.parts[0].vel_x = -0x200;
                }

                if self.parts[0].flags.hit_bottom_wall() {
                    self.parts[0].action_num = 100;
                    self.parts[0].anim_num = 1;
                    self.parts[0].display_bounds.top = 48 * 0x200;
                    self.parts[0].display_bounds.bottom = 0x2000;

                    let player = self.parts[0].get_closest_player_mut(players);
                    if self.parts[0].direction == Direction::Left && self.parts[0].x < player.x {
                        self.parts[0].direction = Direction::Right;
                        self.parts[0].action_num = 110;
                    }

                    if self.parts[0].direction == Direction::Right && self.parts[0].x > player.x {
                        self.parts[0].direction = Direction::Left;
                        self.parts[0].action_num = 110;
                    }

                    let mut npc = NPC::create(110, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = (self.parts[0].rng.range(4..16) + state.constants.game.tile_offset_x) as i32 * 0x2000;
                    npc.y = self.parts[0].rng.range(0..4) as i32 * 0x2000;
                    npc.direction = Direction::FacingPlayer;

                    let _ = npc_list.spawn(0x80, npc);

                    let mut npc = NPC::create(4, &state.npc_table);

                    for _ in 0..4 {
                        npc.cond.set_alive(true);
                        npc.direction = Direction::Left;
                        npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                        npc.y = self.parts[0].y + self.parts[0].hit_bounds.bottom as i32;
                        npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
                        npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;

                        let _ = npc_list.spawn(0x100, npc.clone());
                    }

                    state.quake_counter = 30;
                    state.quake_rumble_counter = 30;
                    state.sound_manager.play_sfx(26);
                }
            }
            110 | 111 => {
                if self.parts[0].action_num == 110 {
                    self.parts[0].anim_num = 1;
                    self.parts[0].action_num = 111;
                    self.parts[0].action_counter = 0;
                }

                self.parts[0].action_counter += 1;

                self.parts[0].vel_x = self.parts[0].vel_x * 8 / 9;

                if self.parts[0].action_counter > 50 {
                    self.parts[0].anim_num = 2;
                    self.parts[0].anim_counter = 0;
                    self.parts[0].action_num = 112;
                }
            }
            112 => {
                self.parts[0].anim_counter += 1;

                if self.parts[0].anim_counter > 4 {
                    self.parts[0].action_num = 113;
                    self.parts[0].action_counter = 0;
                    self.parts[0].vel_x2 = 16;
                    self.parts[0].anim_num = 3;
                    self.parts[0].target_x = self.parts[0].life as i32;
                    self.parts[1].npc_flags.set_shootable(true);
                }
            }
            113 => {
                if self.parts[0].shock != 0 {
                    if (self.parts[0].action_counter2 / 2) & 1 != 0 {
                        self.parts[0].anim_num = 4;
                    } else {
                        self.parts[0].anim_num = 3;
                    }
                    self.parts[0].action_counter2 += 1;
                } else {
                    self.parts[0].action_counter2 = 0;
                    self.parts[0].anim_num = 3;
                }

                self.parts[0].vel_x = self.parts[0].vel_x * 10 / 11;

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter > 16 {
                    self.parts[0].action_counter = 0;
                    self.parts[0].vel_x2 = self.parts[0].vel_x2.saturating_sub(1);

                    let player = self.parts[0].get_closest_player_mut(players);
                    let px = self.parts[0].x + self.parts[0].direction.vector_x() * 2 * 0x2000 - player.x;
                    let py = self.parts[0].y - 0x1000 - player.y;

                    let deg = f64::atan2(py as f64, px as f64) + self.parts[0].rng.range(-16..16) as f64 * CDEG_RAD;

                    let mut npc = NPC::create(108, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = self.parts[0].x + self.parts[0].direction.vector_x() * 2 * 0x2000;
                    npc.y = self.parts[0].y - 0x1000;
                    npc.vel_x = (deg.cos() * -512.0) as i32;
                    npc.vel_y = (deg.sin() * -512.0) as i32;

                    let _ = npc_list.spawn(0x100, npc);

                    state.sound_manager.play_sfx(39);

                    if self.parts[0].vel_x2 == 0 || (self.parts[0].life as i32) < self.parts[0].target_x - 90 {
                        self.parts[0].action_num = 114;
                        self.parts[0].action_counter = 0;
                        self.parts[0].anim_num = 2;
                        self.parts[0].anim_counter = 0;
                        self.parts[1].npc_flags.set_shootable(false);
                    }
                }
            }
            114 => {
                self.parts[0].anim_counter += 1;
                if self.parts[0].anim_counter > 10 {
                    self.parts[0].anim_num = 1;
                    self.parts[0].anim_counter = 0;

                    self.parts[1].action_counter2 += 1;
                    if self.parts[1].action_counter2 > 2 {
                        self.parts[1].action_counter2 = 0;
                        self.parts[0].action_num = 120;
                    } else {
                        self.parts[0].action_num = 100;
                    }
                }
            }
            120 | 121 => {
                if self.parts[0].action_num == 120 {
                    self.parts[0].action_num = 121;
                    self.parts[0].action_counter = 0;
                    self.parts[0].anim_num = 1;
                    self.parts[0].vel_x = 0;
                }

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter > 50 {
                    self.parts[0].action_num = 122;
                    self.parts[0].anim_num = 2;
                    self.parts[0].anim_counter = 0;
                }
            }
            122 => {
                self.parts[0].anim_counter += 1;
                if self.parts[0].anim_counter > 20 {
                    self.parts[0].action_num = 123;
                    self.parts[0].anim_num = 1;
                    self.parts[0].anim_counter = 0;
                }
            }
            123 => {
                self.parts[0].anim_counter += 1;
                if self.parts[0].anim_counter > 4 {
                    self.parts[0].action_num = 124;
                    self.parts[0].anim_num = 5;
                    self.parts[0].vel_y = -0xa00;
                    self.parts[0].display_bounds.top = 0x8000;
                    self.parts[0].display_bounds.bottom = 0x3000;

                    state.sound_manager.play_sfx(25);
                }
            }
            124 => {
                if self.parts[0].flags.hit_bottom_wall() {
                    self.parts[0].action_num = 100;
                    self.parts[0].anim_num = 1;
                    self.parts[0].display_bounds.top = 0x6000;
                    self.parts[0].display_bounds.bottom = 0x2000;

                    let mut npc = NPC::create(104, &state.npc_table);
                    for _ in 0..2 {
                        npc.cond.set_alive(true);
                        npc.x = self.parts[0].rng.range(4..16) as i32 * 0x2000;
                        npc.y = self.parts[0].rng.range(0..4) as i32 * 0x2000;
                        npc.direction = Direction::FacingPlayer;

                        let _ = npc_list.spawn(0x80, npc.clone());
                    }

                    let mut npc = NPC::create(110, &state.npc_table);
                    for _ in 0..6 {
                        npc.cond.set_alive(true);
                        npc.x = (self.parts[0].rng.range(4..16) + state.constants.game.tile_offset_x) as i32 * 0x2000;
                        npc.y = self.parts[0].rng.range(0..4) as i32 * 0x2000;
                        npc.direction = Direction::FacingPlayer;

                        let _ = npc_list.spawn(0x80, npc.clone());
                    }

                    let mut npc = NPC::create(4, &state.npc_table);
                    for _ in 0..8 {
                        npc.cond.set_alive(true);
                        npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                        npc.y = self.parts[0].y + self.parts[0].hit_bounds.bottom as i32;
                        npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
                        npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
                        npc.direction = Direction::Left;

                        let _ = npc_list.spawn(0x100, npc.clone());
                    }

                    let player = self.parts[0].get_closest_player_mut(players);
                    if self.parts[0].direction == Direction::Left && self.parts[0].x < player.x {
                        self.parts[0].action_num = 110;
                        self.parts[0].direction = Direction::Right;
                    }

                    if self.parts[0].direction == Direction::Right && self.parts[0].x > player.x {
                        self.parts[0].action_num = 110;
                        self.parts[0].direction = Direction::Left;
                    }

                    state.sound_manager.play_sfx(26);
                    state.quake_counter = 60;
                    state.quake_rumble_counter = 60;
                }
            }
            130 | 131 => {
                if self.parts[0].action_num == 130 {
                    self.parts[0].action_num = 131;
                    self.parts[0].action_counter = 0;
                    self.parts[0].anim_num = 3;
                    self.parts[0].vel_x = 0;

                    self.parts[1].cond.set_alive(false);
                    self.parts[2].cond.set_alive(false);

                    state.sound_manager.play_sfx(72);

                    let mut npc = NPC::create(4, &state.npc_table);
                    for _ in 0..8 {
                        npc.cond.set_alive(true);
                        npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                        npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                        npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
                        npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
                        npc.direction = Direction::Left;
                        let _ = npc_list.spawn(0x100, npc.clone());
                    }
                }

                self.parts[0].action_counter += 1;
                if (self.parts[0].action_counter % 5) == 0 {
                    let mut npc = NPC::create(4, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                    npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                    npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
                    npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
                    npc.direction = Direction::Left;
                    let _ = npc_list.spawn(0x100, npc);
                }

                self.parts[0].x += if (self.parts[0].action_counter / 2 % 2) != 0 { -0x200 } else { 0x200 };

                if self.parts[0].action_counter > 100 {
                    self.parts[0].action_num = 132;
                    self.parts[0].action_counter = 0;
                }
            }
            132 => {
                self.parts[0].action_counter += 1;
                if (self.parts[0].action_counter / 2) & 1 != 0 {
                    self.parts[0].anim_num = 6;
                    self.parts[0].display_bounds =
                        Rect { left: 20 * 0x200, top: 12 * 0x200, right: 20 * 0x200, bottom: 12 * 0x200 };
                } else {
                    self.parts[0].anim_num = 3;
                    self.parts[0].display_bounds =
                        Rect { left: 48 * 0x200, top: 48 * 0x200, right: 32 * 0x200, bottom: 0x2000 };
                }

                if (self.parts[0].action_counter % 9) == 0 {
                    let mut npc = NPC::create(4, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                    npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
                    npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
                    npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
                    npc.direction = Direction::Left;
                    let _ = npc_list.spawn(0x100, npc);
                }

                if self.parts[0].action_counter > 150 {
                    self.parts[0].action_num = 140;
                    self.parts[0].hit_bounds.bottom = 12 * 0x200;
                }
            }
            140 | 141 => {
                if self.parts[0].action_num == 140 {
                    self.parts[0].action_num = 141;
                }

                if self.parts[0].flags.hit_bottom_wall() {
                    self.parts[0].action_num = 142;
                    self.parts[0].action_counter = 0;
                    self.parts[0].anim_num = 7;
                }
            }
            142 => {
                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter > 30 {
                    self.parts[0].anim_num = 8;
                    self.parts[0].vel_y = -5 * 0x200;
                    self.parts[0].npc_flags.set_ignore_solidity(true);
                    self.parts[0].action_num = 143;
                }
            }
            143 => {
                self.parts[0].vel_y = -5 * 0x200;
                if self.parts[0].y < 0 {
                    self.parts[0].cond.set_alive(false);

                    state.sound_manager.play_sfx(26);
                    state.quake_counter = 30;
                    state.quake_rumble_counter = 30;
                }
            }
            _ => {}
        }

        self.parts[0].vel_y += 0x40;
        self.parts[0].clamp_fall_speed();

        self.parts[0].x += self.parts[0].vel_x;
        self.parts[0].y += self.parts[0].vel_y;

        let dir_offset = if self.parts[0].direction == Direction::Left { 0 } else { 9 };
        self.parts[0].anim_rect = state.constants.npc.b02_balfrog[self.parts[0].anim_num as usize + dir_offset];

        match self.parts[0].anim_num {
            0 => {
                self.hurt_sound[1] = 52;
                self.parts[1].size = 3;
                self.parts[1].npc_flags.set_invulnerable(true);
                self.parts[1].hit_bounds = Rect { left: 0x2000, top: 0x2000, right: 0x2000, bottom: 0x2000 };

                self.hurt_sound[2] = 52;
                self.parts[2].size = 3;
                self.parts[2].npc_flags.set_invulnerable(true);
                self.parts[2].hit_bounds = Rect { left: 24 * 0x200, top: 0x2000, right: 24 * 0x200, bottom: 0x2000 };
            }
            1 => {
                self.parts[1].x = self.parts[0].x + self.parts[0].direction.vector_x() * 24 * 0x200;
                self.parts[1].y = self.parts[0].y - 24 * 0x200;

                self.parts[2].x = self.parts[0].x;
                self.parts[2].y = self.parts[0].y;
            }
            2 => {
                self.parts[1].x = self.parts[0].x + self.parts[0].direction.vector_x() * 24 * 0x200;
                self.parts[1].y = self.parts[0].y - 20 * 0x200;

                self.parts[2].x = self.parts[0].x;
                self.parts[2].y = self.parts[0].y;
            }
            3 | 4 => {
                self.parts[1].x = self.parts[0].x + self.parts[0].direction.vector_x() * 24 * 0x200;
                self.parts[1].y = self.parts[0].y - 0x2000;

                self.parts[2].x = self.parts[0].x;
                self.parts[2].y = self.parts[0].y;
            }
            5 => {
                self.parts[1].x = self.parts[0].x + self.parts[0].direction.vector_x() * 24 * 0x200;
                self.parts[1].y = self.parts[0].y - 43 * 0x200;

                self.parts[2].x = self.parts[0].x;
                self.parts[2].y = self.parts[0].y;
            }
            _ => {}
        }
    }
}
