//! Fixed Penpot geometry for the color picker's RGBA and HSV channel modes.

use bevy_egui::egui::{self, ecolor::Hsva};

use super::super::{byte, set_current, PickerSession};
use super::{caption, text, Geometry};
use crate::ui::design_system::typography::TypeRole;

/// Each 186 px channel group owns a 178 px row: a single-letter channel label,
/// a 104 px track, and a 44 px value field.
struct SliderGeometry;

impl SliderGeometry {
    const RGBA_X: f32 = 12.0;
    const HSV_X: f32 = 206.0;
    const TITLE_Y: f32 = 74.0;
    const FIRST_ROW_Y: f32 = 90.0;
    const ROW_STRIDE: f32 = 30.0;
    const LABEL_WIDTH: f32 = 18.0;
    const TRACK_X: f32 = 24.0;
    const TRACK_WIDTH: f32 = 104.0;
    const VALUE_X: f32 = 134.0;
    const VALUE_WIDTH: f32 = 44.0;
    const ROW_HEIGHT: f32 = 24.0;
}

pub(super) fn show(ui: &mut egui::Ui, root: egui::Rect, session: &mut PickerSession) {
    let palette = crate::ui::design_system::palette(ui);
    let content = Geometry::rect(root, Geometry::CONTENT);
    ui.painter().rect_filled(content, 4.0, palette.recessed);
    caption(
        ui,
        root,
        SliderGeometry::RGBA_X,
        SliderGeometry::TITLE_Y,
        "RGBA CHANNELS",
    );
    caption(
        ui,
        root,
        SliderGeometry::HSV_X,
        SliderGeometry::TITLE_Y,
        "HSV CHANNELS",
    );

    let mut bytes = session.current.map(|component| f32::from(byte(component)));
    let before_bytes = bytes;
    for (index, label) in ["R", "G", "B", "A"].into_iter().enumerate() {
        slider_channel(
            ui,
            root,
            SliderGeometry::RGBA_X,
            index,
            label,
            &mut bytes[index],
            0.0..=255.0,
            "",
        );
    }
    if bytes != before_bytes {
        set_current(session, bytes.map(|component| component / 255.0));
    }

    let mut hsva = Hsva::from_rgba_unmultiplied(
        session.current[0],
        session.current[1],
        session.current[2],
        session.current[3],
    );
    let mut values = [
        hsva.h * 360.0,
        hsva.s * 100.0,
        hsva.v * 100.0,
        hsva.a * 100.0,
    ];
    let before = values;
    for (index, label) in ["H", "S", "V", "A"].into_iter().enumerate() {
        let range = if index == 0 { 0.0..=360.0 } else { 0.0..=100.0 };
        slider_channel(
            ui,
            root,
            SliderGeometry::HSV_X,
            index,
            label,
            &mut values[index],
            range,
            if index == 0 { "°" } else { "%" },
        );
    }
    if values != before {
        hsva.h = values[0] / 360.0;
        hsva.s = values[1] / 100.0;
        hsva.v = values[2] / 100.0;
        hsva.a = values[3] / 100.0;
        let [r, g, b] = hsva.to_rgb();
        set_current(session, [r, g, b, hsva.a]);
    }
}

#[allow(clippy::too_many_arguments)]
fn slider_channel(
    ui: &mut egui::Ui,
    root: egui::Rect,
    group_x: f32,
    index: usize,
    label: &str,
    value: &mut f32,
    range: std::ops::RangeInclusive<f32>,
    suffix: &str,
) {
    let palette = crate::ui::design_system::palette(ui);
    let y = SliderGeometry::FIRST_ROW_Y + index as f32 * SliderGeometry::ROW_STRIDE;
    let label_rect = Geometry::at(
        root,
        group_x,
        y,
        SliderGeometry::LABEL_WIDTH,
        SliderGeometry::ROW_HEIGHT,
    );
    text(
        ui,
        label_rect.left_center(),
        egui::Align2::LEFT_CENTER,
        label,
        TypeRole::Body.font(),
        palette.text_secondary,
    );

    ui.put(
        Geometry::at(
            root,
            group_x + SliderGeometry::TRACK_X,
            y,
            SliderGeometry::TRACK_WIDTH,
            SliderGeometry::ROW_HEIGHT,
        ),
        egui::Slider::new(value, range.clone()).show_value(false),
    );
    ui.put(
        Geometry::at(
            root,
            group_x + SliderGeometry::VALUE_X,
            y,
            SliderGeometry::VALUE_WIDTH,
            SliderGeometry::ROW_HEIGHT,
        ),
        egui::DragValue::new(value)
            .range(range)
            .suffix(suffix)
            .max_decimals(0),
    );
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sliders_mode_matches_the_two_exported_channel_groups() {
        assert_eq!(SliderGeometry::HSV_X - SliderGeometry::RGBA_X, 194.0);
        assert_eq!(SliderGeometry::TRACK_WIDTH, 104.0);
        assert_eq!(SliderGeometry::VALUE_WIDTH, 44.0);
        assert_eq!(SliderGeometry::VALUE_X + SliderGeometry::VALUE_WIDTH, 178.0);
        assert_eq!(SliderGeometry::ROW_STRIDE, 30.0);
        assert_eq!(SliderGeometry::ROW_HEIGHT, 24.0);
    }
}
