use crate::{Color, Modifier, Rect, Transform};
use std::rc::Rc;

pub type ViewId = u64;

pub type ImageHandle = u64;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ImageFit {
    Contain,
    Cover,
    FitWidth,
    FitHeight,
}

pub type Callback = Rc<dyn Fn()>;
pub type ScrollCallback = Rc<dyn Fn(crate::Vec2) -> crate::Vec2>;

#[derive(Clone)]
pub enum ViewKind {
    Surface,
    Box,
    Row,
    Column,
    Stack,
    ScrollV {
        on_scroll: Option<ScrollCallback>,
        set_viewport_height: Option<Rc<dyn Fn(f32)>>,
        set_content_height: Option<Rc<dyn Fn(f32)>>,
        get_scroll_offset: Option<Rc<dyn Fn() -> f32>>,
        set_scroll_offset: Option<Rc<dyn Fn(f32)>>,
    },
    ScrollXY {
        on_scroll: Option<ScrollCallback>,
        set_viewport_width: Option<Rc<dyn Fn(f32)>>,
        set_viewport_height: Option<Rc<dyn Fn(f32)>>,
        set_content_width: Option<Rc<dyn Fn(f32)>>,
        set_content_height: Option<Rc<dyn Fn(f32)>>,
        get_scroll_offset_xy: Option<Rc<dyn Fn() -> (f32, f32)>>,
        set_scroll_offset_xy: Option<Rc<dyn Fn(f32, f32)>>,
    },
    Text {
        text: String,
        color: Color,
        font_size: f32,
        soft_wrap: bool,
        max_lines: Option<usize>,
        overflow: TextOverflow,
    },
    Button {
        on_click: Option<Callback>,
    },
    TextField {
        state_key: ViewId,
        hint: String,
        on_change: Option<Rc<dyn Fn(String)>>,
        on_submit: Option<Rc<dyn Fn(String)>>,
    },
    Checkbox {
        checked: bool,
        on_change: Option<Rc<dyn Fn(bool)>>,
    },
    RadioButton {
        selected: bool,
        on_select: Option<Callback>,
    },
    Switch {
        checked: bool,
        on_change: Option<Rc<dyn Fn(bool)>>,
    },
    Slider {
        value: f32,
        min: f32,
        max: f32,
        step: Option<f32>,
        on_change: Option<CallbackF32>,
    },
    RangeSlider {
        start: f32,
        end: f32,
        min: f32,
        max: f32,
        step: Option<f32>,
        on_change: Option<CallbackRange>,
    },
    ProgressBar {
        value: f32,
        min: f32,
        max: f32,
        circular: bool,
    },
    Image {
        handle: ImageHandle,
        tint: Color, // multiplicative (WHITE = no tint)
        fit: ImageFit,
    },
    Ellipse {
        rect: Rect,
        color: Color,
    },
    EllipseBorder {
        rect: Rect,
        color: Color,
        width: f32, // screen-space width (px)
    },
}

impl std::fmt::Debug for ViewKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ViewKind::Checkbox { checked, .. } => f
                .debug_struct("Checkbox")
                .field("checked", checked)
                .finish(),
            ViewKind::RadioButton { selected, .. } => f
                .debug_struct("RadioButton")
                .field("selected", selected)
                .finish(),
            ViewKind::Switch { checked, .. } => {
                f.debug_struct("Switch").field("checked", checked).finish()
            }
            ViewKind::Surface => write!(f, "Surface"),
            ViewKind::Box => write!(f, "Box"),
            ViewKind::Row => write!(f, "Row"),
            ViewKind::Column => write!(f, "Column"),
            ViewKind::Stack => write!(f, "Stack"),
            ViewKind::ScrollV { .. } => write!(f, "ScrollV"),
            ViewKind::ScrollXY { .. } => write!(f, "ScrollXY"),
            ViewKind::Text {
                text,
                color,
                font_size,
                soft_wrap,
                max_lines,
                overflow,
            } => f
                .debug_struct("Text")
                .field("text", text)
                .field("color", color)
                .field("font_size", font_size)
                .field("soft_wrap", soft_wrap)
                .field("max_lines", max_lines)
                .field("overflow", overflow)
                .finish(),
            ViewKind::Image { handle, tint, fit } => f
                .debug_struct("Image")
                .field("handle", handle)
                .field("tint", tint)
                .field("fit", fit)
                .finish(),
            ViewKind::Button { .. } => f
                .debug_struct("Button")
                .field("on_click", &"<callback>")
                .finish(),
            ViewKind::TextField {
                state_key,
                hint,
                on_change,
                on_submit,
            } => f
                .debug_struct("TextField")
                .field("state_key", state_key)
                .field("hint", hint)
                .finish(),
            ViewKind::Slider {
                value,
                min,
                max,
                step,
                ..
            } => f
                .debug_struct("Slider")
                .field("value", value)
                .field("min", min)
                .field("max", max)
                .field("step", step)
                .finish(),
            ViewKind::RangeSlider {
                start,
                end,
                min,
                max,
                step,
                ..
            } => f
                .debug_struct("RangeSlider")
                .field("start", start)
                .field("end", end)
                .field("min", min)
                .field("max", max)
                .field("step", step)
                .finish(),
            ViewKind::ProgressBar {
                value,
                min,
                max,
                circular,
            } => f
                .debug_struct("ProgressBar")
                .field("value", value)
                .field("min", min)
                .field("max", max)
                .field("circular", circular)
                .finish(),
            ViewKind::Ellipse { rect, color } => f
                .debug_struct("Ellipse")
                .field("rect", rect)
                .field("color", color)
                .finish(),
            ViewKind::EllipseBorder { rect, color, width } => f
                .debug_struct("EllipseBorder")
                .field("rect", rect)
                .field("color", color)
                .field("width", width)
                .finish(),
        }
    }
}

#[derive(Clone, Debug)]
pub struct View {
    pub id: ViewId,
    pub kind: ViewKind,
    pub modifier: Modifier,
    pub children: Vec<View>,
    pub semantics: Option<crate::semantics::Semantics>,
}

impl View {
    pub fn new(id: ViewId, kind: ViewKind) -> Self {
        View {
            id,
            kind,
            modifier: Modifier::default(),
            children: vec![],
            semantics: None,
        }
    }
    pub fn modifier(mut self, m: Modifier) -> Self {
        self.modifier = m;
        self
    }
    pub fn with_children(mut self, kids: Vec<View>) -> Self {
        self.children = kids;
        self
    }
    pub fn semantics(mut self, s: crate::semantics::Semantics) -> Self {
        self.semantics = Some(s);
        self
    }
}

/// Renderable scene
#[derive(Clone, Debug, Default)]
pub struct Scene {
    pub clear_color: Color,
    pub nodes: Vec<SceneNode>,
}

#[derive(Clone, Debug)]
pub enum SceneNode {
    Rect {
        rect: Rect,
        color: Color,
        radius: f32,
    },
    Border {
        rect: Rect,
        color: Color,
        width: f32,
        radius: f32,
    },
    Text {
        rect: Rect,
        text: String,
        color: Color,
        size: f32,
    },
    Ellipse {
        rect: Rect,
        color: Color,
    },
    EllipseBorder {
        rect: Rect,
        color: Color,
        width: f32, // screen-space width (px)
    },
    PushClip {
        rect: Rect,
        radius: f32,
    },
    PopClip,
    PushTransform {
        transform: Transform,
    },
    PopTransform,
    Image {
        rect: Rect,
        handle: ImageHandle,
        tint: Color,
        fit: ImageFit,
    },
}

pub type CallbackF32 = Rc<dyn Fn(f32)>;
pub type CallbackRange = Rc<dyn Fn(f32, f32)>;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TextOverflow {
    Visible,
    Clip,
    Ellipsis,
}
