use crate::renderer::TextRenderer;
use crate::{TextAlign, TextFormat, TextOverflow};
use agb::display::tiled::RegularBackground;
use agb::fixnum::Vector2D;
use alloc::vec::Vec;
use gba_agb_font_eb::AgbFont;

/// Wraps [`TextRenderer`] to reveal text one chunk at a time, like a typewriter
///
/// Call [`update`](TypewriterRenderer::update) once per frame and
/// [`draw`](TypewriterRenderer::draw) whenever you want to flush newly revealed
/// characters to the background
///
/// ```ignore
///# use agb::fixnum::vec2;
///
/// let format = TextFormat::new(TextOverflow::Wrap(200, true), TextAlign::Left, (0, 0));
/// let mut tw = TypewriterRenderer::new(1, 2, &format); // 1 char every 2 frames, wrap at 200px
/// tw.load_text(b"Hello, world!", vec2(8, 16));
///
/// loop {
///     tw.update();
///     tw.draw(&FONT, &mut bg, 15);
///     if tw.is_complete() { break; }
///     vblank.wait_for_vblank();
/// }
/// ```
#[derive(Debug)]
pub struct TypewriterRenderer {
    renderer: TextRenderer,
    text: Vec<u8>,
    chars_revealed: usize,
    last_drawn: usize,
    frame_counter: u8,
    frames_per_update: u8,
    chars_per_update: u8,
    start: Vector2D<i32>,
    /// pixel position of the next character to be drawn (Left alignment only)
    cursor: Vector2D<i32>,
    overflow: TextOverflow,
    alignment: TextAlign,
    /// pixels used on the current line (Left alignment only)
    line_width: i32,
    text_wrapped: bool,
}

impl TypewriterRenderer {
    /// * `chars_per_update` – characters revealed each time the timer fires
    /// * `frames_per_update` – frames between each reveal (1 = every frame)
    pub fn new(chars_per_update: u8, frames_per_update: u8, format: &TextFormat) -> Self {
        Self {
            renderer: TextRenderer::default(),
            text: Vec::new(),
            chars_revealed: 0,
            last_drawn: 0,
            frame_counter: 0,
            frames_per_update,
            chars_per_update,
            start: Vector2D::default(),
            cursor: Vector2D::default(),
            overflow: format.overflow,
            alignment: format.align,
            line_width: 0,
            text_wrapped: false,
        }
    }

    /// Load new text and reset all state. Does **not** clear the background —
    /// call [`clear`](TypewriterRenderer::clear) first if needed.
    pub fn load_text(&mut self, text: &[u8], pos: Vector2D<i32>) {
        self.text.clear();
        self.text.extend_from_slice(text);
        self.chars_revealed = 0;
        self.last_drawn = 0;
        self.frame_counter = 0;
        self.start = pos;
        self.cursor = pos;
        self.line_width = 0;
        self.text_wrapped = false;
    }

    /// Advance the frame counter by one. Call once per frame.
    /// Has no effect once all characters are revealed.
    pub fn update(&mut self) {
        if self.chars_revealed >= self.text.len() {
            return;
        }
        self.frame_counter += 1;
        if self.frame_counter >= self.frames_per_update {
            self.frame_counter = 0;
            let remaining = self.text.len() - self.chars_revealed;
            self.chars_revealed += (self.chars_per_update as usize).min(remaining);
        }
    }

    /// Blit any characters that have been revealed since the last draw call.
    /// Cheap to call even when nothing has changed.
    ///
    /// For [`TextAlign::Left`] only newly revealed characters are blitted.
    /// For [`TextAlign::Center`] and [`TextAlign::Right`] the full revealed
    /// portion is redrawn each call so that line offsets stay correct.
    pub fn draw<T: AgbFont>(
        &mut self,
        font: &T,
        background: &mut RegularBackground,
        palette_id: u8,
    ) {
        if self.last_drawn >= self.chars_revealed {
            return;
        }
        if !self.text_wrapped {
            if let TextOverflow::Wrap(w, true) = self.overflow {
                font.word_wrap_in_place(&mut self.text, w as u32);
            }
            self.text_wrapped = true;
        }
        if self.alignment == TextAlign::Left {
            let new_chars = &self.text[self.last_drawn..self.chars_revealed];
            let (cx, cy, _) = self.renderer.int_draw_text(
                new_chars,
                font,
                background,
                self.cursor,
                self.overflow,
                TextAlign::Left,
                palette_id,
                (0, 0),
                self.line_width,
                self.start.x,
            );
            self.cursor.x += cx;
            self.cursor.y += cy;
            self.line_width += cx;
        } else {
            let column_w = match self.alignment {
                TextAlign::Center(w) | TextAlign::Right(w) => w,
                TextAlign::Left => unreachable!(),
            };
            let size_wrap = match self.overflow {
                TextOverflow::Wrap(w, _) => Some(w),
                _ => None,
            };
            let (_, h) = font.size_of(&self.text, size_wrap.map(|w| w as u32));
            self.renderer.int_draw_text(
                &self.text[..self.chars_revealed],
                font,
                background,
                self.start,
                self.overflow,
                self.alignment,
                palette_id,
                (column_w as i32, h as i32),
                0,
                self.start.x,
            );
        }
        self.last_drawn = self.chars_revealed;
    }

    /// Immediately reveal all characters. The next [`draw`](TypewriterRenderer::draw)
    /// call will flush them all to the background.
    pub fn complete(&mut self) {
        self.chars_revealed = self.text.len();
    }

    /// Returns `true` once all characters have been revealed.
    pub fn is_complete(&self) -> bool {
        self.chars_revealed >= self.text.len()
    }

    /// Clear the pixel region occupied by the current text.
    /// Call before [`load_text`](TypewriterRenderer::load_text) to erase the
    /// previous message.
    pub fn clear<T: AgbFont>(&mut self, font: &T) {
        if self.text.is_empty() {
            return;
        }
        let (w, h) = match self.overflow {
            TextOverflow::Nothing => {
                let (tw, th) = font.size_of(&self.text, None);
                (tw as i32, th as i32)
            }
            TextOverflow::Wrap(ow, _) => {
                let (_, th) = font.size_of(&self.text, Some(ow as u32));
                (ow as i32, th as i32)
            }
            TextOverflow::Cutoff(ow) => {
                let (_, th) = font.size_of(&self.text, None);
                (ow as i32, th as i32)
            }
        };
        self.renderer.clear_pixel_rect(self.start, w, h);
    }

    /// Access the inner [`TextRenderer`] directly, e.g. to call
    /// [`clear_pixel_rect`](TextRenderer::clear_pixel_rect) with custom bounds.
    pub fn renderer_mut(&mut self) -> &mut TextRenderer {
        &mut self.renderer
    }
}
