use std::cell::RefCell;
use std::rc::Rc;

use super::Rom;
use super::battle_background::BattleBackground;
use crate::rom::background_graphics::BackgroundGraphics;
use crate::rom::distorter::Distorter;
use crate::rom::distortion_effect::DistortionEffect;
use crate::rom::palette_cycle::PaletteCycle;

const WIDTH: usize = 256;
const HEIGHT: usize = 256;

#[derive(Debug)]
pub struct BackgroundLayer {
    rom: Rc<RefCell<Rom>>,
    graphics: Option<Rc<RefCell<BackgroundGraphics>>>,
    palette_cycle: Option<PaletteCycle>,
    pixels: Rc<RefCell<Vec<i16>>>,
    distorter: Distorter,
    pub entry: usize,
}

impl BackgroundLayer {
    pub fn new(entry: usize, rom: Rc<RefCell<Rom>>) -> Self {
        let pixels = Rc::new(RefCell::new(vec![0i16; WIDTH * HEIGHT * 4]));

        let mut layer = BackgroundLayer {
            rom,
            graphics: None,
            palette_cycle: None,
            pixels: pixels.clone(),
            distorter: Distorter::new(pixels),
            entry: 0,
        };

        layer.load_entry(entry);

        layer
    }

    pub fn overlay_frame(
        &mut self,
        bitmap: &mut [u8],
        letterbox: u8,
        ticks: u64,
        alpha: f32,
        erase: bool,
    ) {
        if let Some(palette_cycle) = &mut self.palette_cycle {
            palette_cycle.cycle();
            self.graphics
                .as_ref()
                .unwrap()
                .borrow_mut()
                .draw(&mut self.pixels.borrow_mut(), palette_cycle);
        }
        self.distorter
            .overlay_frame(bitmap, letterbox, ticks, alpha, erase);
    }

    fn load_graphics(&mut self, index: usize) {
        self.graphics = Some(self.rom.borrow_mut().get_background_graphics(index));
    }

    fn load_palette(&mut self, background: &BattleBackground) {
        self.palette_cycle = Some(PaletteCycle::new(
            background,
            self.rom
                .borrow_mut()
                .get_background_palette(background.palette_index()),
        ))
    }

    fn load_effect(&mut self, index: usize) {
        *self.distorter.effect.borrow_mut() = DistortionEffect::new(index);
    }

    fn load_entry(&mut self, index: usize) {
        self.entry = index;
        let background = self.rom.borrow_mut().get_battle_background(index);
        // Set graphics/palette
        self.load_graphics(background.borrow().graphics_index());
        self.load_palette(&background.borrow());
        let animation = background.borrow().animation();
        let e1 = (animation >> 24) & 0xFF;
        let e2 = (animation >> 16) & 0xFF;

        if e2 != 0 {
            self.load_effect(e2 as usize);
        } else {
            self.load_effect(e1 as usize);
        }
    }
}
