// el-layout — Responsive layout engine for el-ui.
//
// Primitives:
//   VStack, HStack, ZStack   — stack layouts (wrap by default)
//   GridLayout               — responsive grid
//   ScrollView               — scrollable container
//   Responsive<T>            — value that changes by breakpoint

// ── Breakpoints ──────────────────────────────────────────────────────────────

let BP_XS:  String = "xs"   // <  640px
let BP_SM:  String = "sm"   // >= 640px
let BP_MD:  String = "md"   // >= 768px
let BP_LG:  String = "lg"   // >= 1024px
let BP_XL:  String = "xl"   // >= 1280px
let BP_XXL: String = "xxl"  // >= 1536px

let BP_SM_PX:  Int = 640
let BP_MD_PX:  Int = 768
let BP_LG_PX:  Int = 1024
let BP_XL_PX:  Int = 1280
let BP_XXL_PX: Int = 1536

fn breakpoint_for_width(width_px: Int) -> String {
    if width_px >= BP_XXL_PX { return BP_XXL }
    if width_px >= BP_XL_PX  { return BP_XL }
    if width_px >= BP_LG_PX  { return BP_LG }
    if width_px >= BP_MD_PX  { return BP_MD }
    if width_px >= BP_SM_PX  { return BP_SM }
    BP_XS
}

// Cascade index — used by Responsive<T> to find the most-specific value <= bp.
fn breakpoint_index(bp: String) -> Int {
    if str_eq(bp, "xs")  { return 0 }
    if str_eq(bp, "sm")  { return 1 }
    if str_eq(bp, "md")  { return 2 }
    if str_eq(bp, "lg")  { return 3 }
    if str_eq(bp, "xl")  { return 4 }
    if str_eq(bp, "xxl") { return 5 }
    0
}

// ── Responsive<T> ────────────────────────────────────────────────────────────
//
// Stored as a JSON object: { "xs": v0, "md": v1, "lg": v2 }
// resolve(bp) walks down from bp until it finds a defined value.

fn responsive_fixed(value: String) -> String {
    "{\"xs\":" + value + "}"
}

fn responsive_set(rv: String, bp: String, value: String) -> String {
    json_set(rv, bp, value)
}

fn responsive_resolve(rv: String, bp: String) -> String {
    let order: String = "xxl,xl,lg,md,sm,xs"
    let target_idx: Int = breakpoint_index(bp)
    // Try each breakpoint <= target, most-specific first.
    let probe: String = bp
    while !str_eq(probe, "") {
        let v: String = json_get(rv, probe)
        if !str_eq(v, "") { return v }
        let idx: Int = breakpoint_index(probe)
        if idx == 0 { return "" }
        let probe = breakpoint_step_down(probe)
    }
    ""
}

fn breakpoint_step_down(bp: String) -> String {
    if str_eq(bp, "xxl") { return "xl" }
    if str_eq(bp, "xl")  { return "lg" }
    if str_eq(bp, "lg")  { return "md" }
    if str_eq(bp, "md")  { return "sm" }
    if str_eq(bp, "sm")  { return "xs" }
    ""
}

// ── Constraints / Size ───────────────────────────────────────────────────────

type Size {
    width:  Int
    height: Int
}

type LayoutConstraints {
    min_width:  Int
    max_width:  Int
    min_height: Int
    max_height: Int
}

fn constraints_unbounded() -> LayoutConstraints {
    { "min_width": 0, "max_width": 999999, "min_height": 0, "max_height": 999999 }
}

fn constraints_tight(w: Int, h: Int) -> LayoutConstraints {
    { "min_width": w, "max_width": w, "min_height": h, "max_height": h }
}

// ── Flex axes ────────────────────────────────────────────────────────────────

let FLEX_ROW:    String = "row"
let FLEX_COLUMN: String = "column"

let MAIN_START:    String = "start"
let MAIN_END:      String = "end"
let MAIN_CENTER:   String = "center"
let MAIN_BETWEEN:  String = "space-between"
let MAIN_AROUND:   String = "space-around"
let MAIN_EVENLY:   String = "space-evenly"

let CROSS_START:    String = "start"
let CROSS_END:      String = "end"
let CROSS_CENTER:   String = "center"
let CROSS_STRETCH:  String = "stretch"
let CROSS_BASELINE: String = "baseline"

type FlexLayout {
    direction:        String
    main_alignment:   String
    cross_alignment:  String
    gap_px:           Int
    wrap:             Bool
}

fn flex_default() -> FlexLayout {
    { "direction": "row", "main_alignment": "start",
      "cross_alignment": "stretch", "gap_px": 8, "wrap": true }
}

// ── Stacks ───────────────────────────────────────────────────────────────────
//
// VStack / HStack / ZStack are component classes in the JS runtime; the
// El side here exposes their layout descriptor — the bag of values the
// el-ui-compiler emits into JSX/HTML attributes.

type StackLayout {
    direction:       String   // row | column | depth
    main_alignment:  String
    cross_alignment: String
    gap_px:          Int
    wrap:            Bool
    spacing_token:   String   // semantic spacing token (md, lg, ...)
}

fn vstack(spacing_token: String) -> StackLayout {
    { "direction": "column", "main_alignment": "start",
      "cross_alignment": "stretch", "gap_px": 0, "wrap": true,
      "spacing_token": spacing_token }
}

fn hstack(spacing_token: String) -> StackLayout {
    { "direction": "row", "main_alignment": "start",
      "cross_alignment": "center", "gap_px": 0, "wrap": true,
      "spacing_token": spacing_token }
}

fn zstack() -> StackLayout {
    { "direction": "depth", "main_alignment": "center",
      "cross_alignment": "center", "gap_px": 0, "wrap": false,
      "spacing_token": "" }
}

// ── Grid ─────────────────────────────────────────────────────────────────────

type GridLayout {
    columns:     String   // "auto" or "1fr 1fr 1fr" etc
    rows:        String
    gap_px:      Int
    auto_fit_min: Int     // for `repeat(auto-fit, minmax(<min>, 1fr))`
}

fn grid_auto(min_col_px: Int, gap: Int) -> GridLayout {
    { "columns": "auto-fit", "rows": "auto", "gap_px": gap, "auto_fit_min": min_col_px }
}

fn grid_fixed(num_cols: Int, gap: Int) -> GridLayout {
    let cols: String = repeat_str("1fr ", num_cols)
    { "columns": cols, "rows": "auto", "gap_px": gap, "auto_fit_min": 0 }
}

fn grid_to_css(g: GridLayout) -> String {
    let cols: String = g.columns
    if str_eq(cols, "auto-fit") {
        let cols = "repeat(auto-fit, minmax(" + int_to_str(g.auto_fit_min) + "px, 1fr))"
    }
    "display: grid; grid-template-columns: " + cols + "; gap: " + int_to_str(g.gap_px) + "px;"
}

// ── ScrollView ──────────────────────────────────────────────────────────────

let SCROLL_X: String = "x"
let SCROLL_Y: String = "y"
let SCROLL_BOTH: String = "both"

type ScrollView {
    axis:                 String
    show_indicator:       Bool
    bounce:               Bool   // iOS-style overscroll
}

fn scroll_view_y() -> ScrollView {
    { "axis": "y", "show_indicator": true, "bounce": true }
}

// ── Platform sizing ──────────────────────────────────────────────────────────

let PLATFORM_PHONE:   String = "phone"
let PLATFORM_TABLET:  String = "tablet"
let PLATFORM_DESKTOP: String = "desktop"

type SafeAreaInsets {
    top:    Int
    right:  Int
    bottom: Int
    left:   Int
}

fn safe_area_zero() -> SafeAreaInsets {
    { "top": 0, "right": 0, "bottom": 0, "left": 0 }
}

fn platform_for_width(width: Int) -> String {
    if width < 768 { return "phone" }
    if width < 1024 { return "tablet" }
    "desktop"
}

// ── HTML emit ────────────────────────────────────────────────────────────────
//
// Server-side render: layout descriptor → HTML string.
// Children is a pre-rendered HTML string passed in by the caller.
// class_extra is an optional additional CSS class string (pass "" for none).

fn stack_direction_to_css(direction: String) -> String {
    if str_eq(direction, "column") { return "column" }
    if str_eq(direction, "depth")  { return "unset" }
    "row"
}

fn stack_align_to_css(align: String) -> String {
    if str_eq(align, "center")   { return "center" }
    if str_eq(align, "end")      { return "flex-end" }
    if str_eq(align, "baseline") { return "baseline" }
    if str_eq(align, "stretch")  { return "stretch" }
    "flex-start"
}

fn stack_justify_to_css(align: String) -> String {
    if str_eq(align, "center")       { return "center" }
    if str_eq(align, "end")          { return "flex-end" }
    if str_eq(align, "space-between") { return "space-between" }
    if str_eq(align, "space-around")  { return "space-around" }
    if str_eq(align, "space-evenly")  { return "space-evenly" }
    "flex-start"
}

fn stack_to_html(layout: StackLayout, children: String, class_extra: String) -> String {
    let direction: String = stack_direction_to_css(layout.direction)
    let justify:   String = stack_justify_to_css(layout.main_alignment)
    let align:     String = stack_align_to_css(layout.cross_alignment)
    let gap:       String = int_to_str(layout.gap_px) + "px"
    let wrap_val:  String = if layout.wrap { "wrap" } else { "nowrap" }
    let style:     String = "display:flex;flex-direction:" + direction
        + ";justify-content:" + justify
        + ";align-items:" + align
        + ";gap:" + gap
        + ";flex-wrap:" + wrap_val
    let base_class: String = "el-stack el-stack--" + layout.direction
    let cls: String = if str_eq(class_extra, "") { base_class } else { base_class + " " + class_extra }
    "<div class=\"" + cls + "\" style=\"" + style + "\">" + children + "</div>"
}

fn vstack_to_html(spacing_px: Int, children: String, class_extra: String) -> String {
    let layout: StackLayout = { "direction": "column", "main_alignment": "start",
        "cross_alignment": "stretch", "gap_px": spacing_px, "wrap": true, "spacing_token": "" }
    stack_to_html(layout, children, class_extra)
}

fn hstack_to_html(spacing_px: Int, children: String, class_extra: String) -> String {
    let layout: StackLayout = { "direction": "row", "main_alignment": "start",
        "cross_alignment": "center", "gap_px": spacing_px, "wrap": true, "spacing_token": "" }
    stack_to_html(layout, children, class_extra)
}

fn zstack_to_html(children: String, class_extra: String) -> String {
    let base_class: String = "el-stack el-stack--depth"
    let cls: String = if str_eq(class_extra, "") { base_class } else { base_class + " " + class_extra }
    "<div class=\"" + cls + "\" style=\"display:grid;place-items:center\">" + children + "</div>"
}

fn grid_to_html(layout: GridLayout, children: String, class_extra: String) -> String {
    let css: String = grid_to_css(layout)
    let base_class: String = "el-grid"
    let cls: String = if str_eq(class_extra, "") { base_class } else { base_class + " " + class_extra }
    "<div class=\"" + cls + "\" style=\"" + css + "\">" + children + "</div>"
}

fn scroll_to_html(layout: ScrollView, children: String, class_extra: String) -> String {
    let overflow: String = if str_eq(layout.axis, "x") { "overflow-x:auto;overflow-y:hidden" }
        else if str_eq(layout.axis, "both") { "overflow:auto" }
        else { "overflow-x:hidden;overflow-y:auto" }
    let base_class: String = "el-scroll"
    let cls: String = if str_eq(class_extra, "") { base_class } else { base_class + " " + class_extra }
    "<div class=\"" + cls + "\" style=\"" + overflow + "\">" + children + "</div>"
}

// ── Entry — smoke test ──────────────────────────────────────────────────────

let r: String = responsive_fixed("1")
let r = responsive_set(r, "md", "2")
let r = responsive_set(r, "lg", "3")
println("[el-layout] cols at lg = " + responsive_resolve(r, "lg"))
let v: String = vstack_to_html(16, "<p>hello</p>", "")
println("[el-layout] vstack = " + v)
