/*
    Hurry Curry! - a game about cooking
    Copyright 2024 metamuffin
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
*/
use crate::render::{sprite::SpriteDraw, Renderer};
use hurrycurry_protocol::glam::{IVec2, Vec2};
use sdl2::{
    keyboard::{KeyboardState, Keycode, Scancode},
    mouse::MouseState,
};
#[derive(Default)]
pub struct FocusDevice {
    focus: usize,
    pressing: Option,
    interact_just_pressed: bool,
    interact_just_released: bool,
    interact_down: bool,
}
#[derive(Default)]
pub struct UiState {
    mouse_position: Vec2,
    ui_scale: Vec2,
    backspace: bool,
    text_input: String,
    keyboard_focus: FocusDevice,
    mouse_focus: FocusDevice,
}
pub struct Ui<'a, 'b> {
    cursor: Vec2,
    size: Vec2,
    cross_height: f32,
    index: usize,
    direction_horizontal: bool,
    pub renderer: &'a mut Renderer<'b>,
    state: &'a mut UiState,
}
impl UiState {
    pub fn update(&mut self, keyboard: &KeyboardState, mouse: &MouseState, _dt: f32) {
        self.mouse_position = IVec2::new(mouse.x(), mouse.y()).as_vec2() / self.ui_scale;
        self.mouse_focus.update(mouse.left());
        self.keyboard_focus
            .update(keyboard.is_scancode_pressed(Scancode::Space));
    }
    pub fn text_input(&mut self, text: String) {
        self.text_input = text;
    }
    pub fn keyboard_event(&mut self, keycode: Keycode, down: bool) {
        if down {
            match keycode {
                Keycode::DOWN => self.keyboard_focus.focus += 1,
                Keycode::UP if self.keyboard_focus.focus > 0 => self.keyboard_focus.focus -= 1,
                Keycode::BACKSPACE => self.backspace = true,
                _ => (),
            }
        }
    }
    pub fn draw(&mut self, renderer: &mut Renderer, ui: impl FnOnce(&mut Ui)) {
        self.ui_scale = renderer.ui_scale;
        self.mouse_focus.focus = usize::MAX;
        let mut u = Ui {
            cursor: Vec2::ZERO,
            direction_horizontal: false,
            size: renderer.ui_size,
            renderer,
            state: self,
            cross_height: 0.,
            index: 0,
        };
        ui(&mut u);
        self.end()
    }
    fn end(&mut self) {
        if self.mouse_focus.interact_just_released {
            self.mouse_focus.pressing = None;
        }
        if self.keyboard_focus.interact_just_released {
            self.keyboard_focus.pressing = None;
        }
        self.text_input.clear();
        self.backspace = false;
    }
}
impl FocusDevice {
    pub fn update(&mut self, interact: bool) {
        self.interact_just_pressed = interact && !self.interact_down;
        self.interact_just_released = !interact && self.interact_down;
        self.interact_down = interact;
    }
    pub fn element(&mut self, index: usize) -> (bool, bool, bool) {
        let focus = self.focus == index;
        if focus && self.interact_just_pressed {
            self.pressing = Some(index)
        };
        let pressing = self.pressing == Some(index);
        let released = self.interact_just_released && pressing && focus;
        (focus, pressing, released)
    }
}
impl<'a, 'b> Ui<'a, 'b> {
    pub fn vertical(&mut self, content: impl FnOnce(&mut Ui)) {
        self.flow(false, content)
    }
    pub fn horizontal(&mut self, content: impl FnOnce(&mut Ui)) {
        self.flow(true, content)
    }
    pub fn flow(&mut self, dir: bool, content: impl FnOnce(&mut Ui)) {
        let d = self.direction_horizontal;
        let ch = self.cross_height;
        let c = self.cursor;
        self.direction_horizontal = dir;
        self.cross_height = 0.;
        content(self);
        let size = (self.cursor - c).max(if dir { Vec2::Y } else { Vec2::X } * self.cross_height);
        self.direction_horizontal = d;
        self.cross_height = ch;
        self.cursor = c;
        self.advance(size);
    }
    pub fn text(&mut self, text: &str) {
        self.scaled_text(text, 1.)
    }
    pub fn small_text(&mut self, text: &str) {
        self.scaled_text(text, 0.5)
    }
    pub fn scaled_text(&mut self, text: &str, scale: f32) {
        let margin = Vec2::splat(2.);
        let size = margin
            + self
                .renderer
                .draw_text(self.cursor + margin, text, scale, None)
            + margin;
        self.advance(size);
    }
    pub fn button(&mut self, w: f32, label: &str) -> bool {
        let c = self.cursor;
        let margin = Vec2::splat(4.);
        let text_size = self
            .renderer
            .draw_text(self.cursor + margin, label, 1., None);
        let size = margin + Vec2::new(w, text_size.y) + margin;
        self.index += 1;
        let mouse_rel = self.state.mouse_position - c;
        if mouse_rel.x >= 0. && mouse_rel.y >= 0. && mouse_rel.x < size.x && mouse_rel.y < size.y {
            self.state.mouse_focus.focus = self.index;
        }
        let (focus, pressing, released) = {
            let (mfocus, mpressing, mreleased) = self.state.mouse_focus.element(self.index);
            let (kfocus, kpressing, kreleased) = self.state.keyboard_focus.element(self.index);
            (
                mfocus || kfocus,
                mpressing || kpressing,
                mreleased || kreleased,
            )
        };
        let l = if pressing {
            100
        } else if focus {
            50
        } else {
            30
        };
        self.renderer.draw_ui(SpriteDraw::screen(
            self.renderer.misc_textures.solid,
            i32::MAX - 1,
            c,
            size,
            Some([l, l, l, 200]),
        ));
        self.advance(size);
        released
    }
    pub fn textedit(&mut self, w: f32, content: &mut String) {
        let c = self.cursor;
        let margin = Vec2::splat(4.);
        let text_size = self
            .renderer
            .draw_text(self.cursor + margin, content, 1., None);
        let size = margin + Vec2::new(w, text_size.y) + margin;
        self.index += 1;
        let mouse_rel = self.state.mouse_position - c;
        if mouse_rel.x >= 0. && mouse_rel.y >= 0. && mouse_rel.x < size.x && mouse_rel.y < size.y {
            self.state.mouse_focus.focus = self.index;
        }
        if self.state.mouse_focus.interact_just_pressed
            && self.state.mouse_focus.focus == self.index
        {
            self.state.keyboard_focus.focus = self.index;
        }
        let keyboard_focus = self.state.keyboard_focus.focus == self.index;
        if keyboard_focus {
            *content += &self.state.text_input;
            self.state.text_input.clear();
            if self.state.backspace {
                content.pop();
            }
        }
        let focus = self.state.mouse_focus.focus == self.index || keyboard_focus;
        let l = if focus { 50 } else { 30 };
        self.renderer.draw_ui(SpriteDraw::screen(
            self.renderer.misc_textures.solid,
            i32::MAX - 1,
            c,
            size,
            Some([l, l, l, 200]),
        ));
        self.advance(size);
    }
    pub fn fill(&mut self) {
        self.renderer.draw_ui(SpriteDraw::screen(
            self.renderer.misc_textures.solid,
            i32::MAX - 1,
            self.cursor,
            self.get_remaining(),
            Some([30, 30, 30, 200]),
        ));
    }
    pub fn get_remaining(&self) -> Vec2 {
        if self.direction_horizontal {
            Vec2::new(self.size.x - self.cursor.x, self.cross_height)
        } else {
            Vec2::new(self.cross_height, self.size.y - self.cursor.y)
        }
    }
    pub fn advance(&mut self, size: Vec2) {
        if self.direction_horizontal {
            self.cursor.x += size.x;
            self.cross_height = self.cross_height.max(size.y);
        } else {
            self.cursor.y += size.y;
            self.cross_height = self.cross_height.max(size.x);
        }
    }
}