use crate::common::Rect;
use crate::entity::GameEntity;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::game::frame::Frame;
use crate::game::shared_game_state::SharedGameState;
use crate::game::npc::boss::BossNPC;
use crate::game::npc::list::NPCList;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum BossLifeTarget {
    None,
    NPC(u16),
    Boss,
}

pub struct BossLifeBar {
    target: BossLifeTarget,
    life: u16,
    max_life: u16,
    prev_life: u16,
    counter: u16,
}

impl BossLifeBar {
    pub fn new() -> BossLifeBar {
        BossLifeBar { target: BossLifeTarget::None, life: 0, max_life: 0, prev_life: 0, counter: 0 }
    }

    pub fn set_npc_target(&mut self, npc_id: u16, npc_list: &NPCList) {
        if let Some(npc) = npc_list.get_npc(npc_id as usize) {
            self.target = BossLifeTarget::NPC(npc.id);
            self.life = npc.life;
            self.max_life = self.life;
            self.prev_life = self.life;
        }
    }

    pub fn set_boss_target(&mut self, boss: &BossNPC) {
        self.target = BossLifeTarget::Boss;
        self.life = boss.parts[0].life;
        self.max_life = self.life;
        self.prev_life = self.life;
    }

    fn draw_regular(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult {
        let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;

        let box_length = 256;
        let bar_length = box_length - 58;

        let text_rect = Rect::new_size(0, 48, 32, 8);
        let box_rect1 = Rect::new_size(0, 0, 244, 8);
        let box_rect2 = Rect::new_size(0, 16, 244, 8);
        let mut rect_prev_bar = Rect::new_size(0, 32, 232, 8);
        let mut rect_life_bar = Rect::new_size(0, 24, 232, 8);

        rect_prev_bar.right = ((self.prev_life as u32 * bar_length) / self.max_life as u32).min(bar_length) as u16;
        rect_life_bar.right = ((self.life as u32 * bar_length) / self.max_life as u32).min(bar_length) as u16;

        batch.add_rect(
            ((state.canvas_size.0 - box_length as f32) / 2.0).floor(),
            state.canvas_size.1 - 20.0,
            &box_rect1,
        );
        batch.add_rect(
            ((state.canvas_size.0 - box_length as f32) / 2.0).floor(),
            state.canvas_size.1 - 12.0,
            &box_rect2,
        );
        batch.add_rect(
            ((state.canvas_size.0 - box_length as f32) / 2.0).floor(),
            state.canvas_size.1 - 20.0,
            &box_rect1,
        );
        batch.add_rect(
            ((state.canvas_size.0 - box_length as f32) / 2.0 + 40.0).floor(),
            state.canvas_size.1 - 16.0,
            &rect_prev_bar,
        );
        batch.add_rect(
            ((state.canvas_size.0 - box_length as f32) / 2.0 + 40.0).floor(),
            state.canvas_size.1 - 16.0,
            &rect_life_bar,
        );
        batch.add_rect(
            ((state.canvas_size.0 - box_length as f32) / 2.0 + 8.0).floor(),
            state.canvas_size.1 - 16.0,
            &text_rect,
        );

        batch.draw(ctx)?;

        Ok(())
    }

    fn draw_nx(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult {
        let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;

        let box_length = 148;
        let bar_length = box_length - 52;

        let text_rect = Rect::new_size(0, 48, 32, 8);
        let box_rect1 = Rect::new_size(0, 0, 136, 8);
        let box_rect2 = Rect::new_size(0, 16, 136, 8);
        let box_rect3 = Rect::new_size(238, 0, 6, 8);
        let box_rect4 = Rect::new_size(238, 16, 6, 8);
        let mut rect_prev_bar = Rect::new_size(0, 32, 124, 8);
        let mut rect_life_bar = Rect::new_size(0, 24, 124, 8);

        rect_prev_bar.right = ((self.prev_life as u32 * bar_length) / self.max_life as u32).min(bar_length) as u16;
        rect_life_bar.right = ((self.life as u32 * bar_length) / self.max_life as u32).min(bar_length) as u16;

        let base_x = state.canvas_size.0 - box_length as f32;

        batch.add_rect((base_x - 6.0).floor(), state.canvas_size.1 - 20.0, &box_rect1);
        batch.add_rect((base_x - 6.0).floor(), state.canvas_size.1 - 12.0, &box_rect2);
        batch.add_rect((base_x - 6.0).floor(), state.canvas_size.1 - 20.0, &box_rect1);
        batch.add_rect((state.canvas_size.0 - 18.0).floor(), state.canvas_size.1 - 20.0, &box_rect3);
        batch.add_rect((state.canvas_size.0 - 18.0).floor(), state.canvas_size.1 - 12.0, &box_rect4);
        batch.add_rect((state.canvas_size.0 - 18.0).floor(), state.canvas_size.1 - 20.0, &box_rect3);
        batch.add_rect((base_x + 34.0).floor(), state.canvas_size.1 - 16.0, &rect_prev_bar);
        batch.add_rect((base_x + 34.0).floor(), state.canvas_size.1 - 16.0, &rect_life_bar);
        batch.add_rect((base_x + 2.0).floor(), state.canvas_size.1 - 16.0, &text_rect);

        batch.draw(ctx)?;

        Ok(())
    }
}

impl GameEntity<(&NPCList, &BossNPC)> for BossLifeBar {
    fn tick(&mut self, _state: &mut SharedGameState, (npc_list, boss): (&NPCList, &BossNPC)) -> GameResult<()> {
        match self.target {
            BossLifeTarget::NPC(npc_id) => {
                if let Some(npc) = npc_list.get_npc(npc_id as usize) {
                    self.life = npc.life;
                }
            }
            BossLifeTarget::Boss => {
                self.life = boss.parts[0].life;
            }
            _ => {
                return Ok(());
            }
        }

        if self.life == 0 {
            self.target = BossLifeTarget::None;
        } else if self.prev_life > self.life {
            self.counter += 1;
            if self.counter > 30 {
                self.prev_life = self.prev_life.saturating_sub(1);
            }
        } else {
            self.counter = 0;
        }

        Ok(())
    }

    fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult<()> {
        if self.max_life == 0 || self.target == BossLifeTarget::None {
            return Ok(());
        }

        match state.constants.is_switch {
            true => self.draw_nx(state, ctx, _frame),
            false => self.draw_regular(state, ctx, _frame),
        }
    }
}
