use core::marker::PhantomData;

use embedded_graphics::{
    mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder},
    pixelcolor::Rgb565,
    prelude::{Point, RgbColor, Size},
    primitives::{Primitive, PrimitiveStyleBuilder, Rectangle},
    text::{Baseline, Text},
    Drawable,
};

use crate::keyboard::KeyCode;

use super::{
    scroll_tracker::{ScrollAction, ScrollTracker},
    Element,
};

const BACKGROUND_COLOR: Rgb565 = Rgb565::new(31, 41, 0); // #FFA500
const BORDER_COLOR: Rgb565 = Rgb565::new(31, 26, 0); // 0xFF6600
const CHAR_WIDTH: u32 = 10;
const CHAR_HEIGHT: u32 = 20;
const BORDER_WIDTH: u32 = 4;
const SCREEN_WIDTH: u32 = 320;
const SCREEN_HEIGHT: u32 = 240;

pub struct Selector<T: AsRef<str>, V: AsRef<[T]>> {
    options: V,
    width: u32,
    height: u32,
    selected: usize,
    tracker: ScrollTracker,
    _phantom: PhantomData<T>,
}

impl<T: AsRef<str>, V: AsRef<[T]>> Selector<T, V> {
    pub fn new(options: V, selected: usize) -> Self {
        assert!(selected < options.as_ref().len());

        let width = u32::try_from(
            options
                .as_ref()
                .iter()
                .map(|x| x.as_ref().len())
                .max()
                .unwrap_or(0),
        )
        .unwrap()
            * CHAR_WIDTH
            + BORDER_WIDTH * 2;
        let height = u32::try_from(options.as_ref().len()).unwrap() * (CHAR_HEIGHT + BORDER_WIDTH)
            + BORDER_WIDTH;
        Self {
            options,
            width,
            height,
            selected,
            tracker: ScrollTracker::new(),
            _phantom: PhantomData,
        }
    }

    pub fn selected(&self) -> usize {
        self.selected
    }
}

impl<T: AsRef<str>, V: AsRef<[T]>> Element for Selector<T, V> {
    type KeyPushReturn = ();

    fn render<
        E,
        DT: embedded_graphics::prelude::DrawTarget<
            Color = embedded_graphics::pixelcolor::Rgb565,
            Error = E,
        >,
    >(
        &mut self,
        dt: &mut DT,
    ) -> Result<(), E> {
        // corner
        let start_x =
            i32::try_from(SCREEN_WIDTH / 2).unwrap() - i32::try_from(self.width / 2).unwrap();
        let start_y =
            i32::try_from(SCREEN_HEIGHT / 2).unwrap() - i32::try_from(self.height / 2).unwrap();

        let unselected_rect_style = PrimitiveStyleBuilder::new()
            .stroke_color(BORDER_COLOR)
            .stroke_width(BORDER_WIDTH)
            .fill_color(BACKGROUND_COLOR)
            .build();

        let selected_rect_style = PrimitiveStyleBuilder::new()
            .stroke_color(BORDER_COLOR)
            .stroke_width(BORDER_WIDTH)
            .fill_color(Rgb565::WHITE)
            .build();

        let text_style = MonoTextStyleBuilder::new()
            .font(&FONT_10X20)
            .text_color(Rgb565::BLACK)
            .build();

        for (i, text) in self.options.as_ref().iter().enumerate() {
            // Draw our background rectangles
            let rect_style = if i == self.selected {
                selected_rect_style
            } else {
                unselected_rect_style
            };

            let i: u32 = i.try_into().unwrap();

            Rectangle::new(
                Point::new(
                    start_x,
                    start_y + i32::try_from(i * (CHAR_HEIGHT + BORDER_WIDTH)).unwrap(),
                ),
                Size::new(self.width, CHAR_HEIGHT + BORDER_WIDTH),
            )
            .into_styled(rect_style)
            .draw(dt)?;

            // Render the text
            Text::with_baseline(
                text.as_ref(),
                Point::new(
                    i32::try_from(BORDER_WIDTH).unwrap() + start_x,
                    start_y + i32::try_from(i * (CHAR_HEIGHT + BORDER_WIDTH) + 1).unwrap(),
                ),
                text_style,
                Baseline::Top,
            )
            .draw(dt)?;
        }

        Ok(())
    }

    fn key_push(&mut self, _k: KeyCode) {}

    fn touchpad_scroll(&mut self, _x: i8, y: i8) {
        match self.tracker.scroll(y) {
            ScrollAction::Previous => {
                if self.selected > 0 {
                    self.selected -= 1;
                }
            }
            ScrollAction::Next => {
                if self.selected < self.options.as_ref().len() - 1 {
                    self.selected += 1;
                }
            }
            ScrollAction::None => {}
        }
    }
}