// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
fn[C] compute_block_absolute_children(
  view : ChicleView[C],
  abs_child_ids : Array[NodeId],
  abs_child_static_y : Array[Double],
  padding_origin : Point[Double],
  padding : Rect[Double],
  border : Rect[Double],
  final_border_box_width : Double,
  final_border_box_height : Double,
  scrollbar_x : Double,
  scrollbar_y : Double,
) -> Unit raise ChicleError {
  let tree = view.tree
  // Absolute children: positioned relative to the final padding box.
  let final_padding_box_width = @util.max_double(
    final_border_box_width - border.left - border.right - scrollbar_y,
    0.0,
  )
  let final_padding_box_height = @util.max_double(
    final_border_box_height - border.top - border.bottom - scrollbar_x,
    0.0,
  )
  let abs_available = Size(
    width=@geometry.AvailDefinite(final_padding_box_width),
    height=AvailDefinite(final_padding_box_height),
  )
  for i in 0.. c
      None => raise InvalidNodeId(child_id)
    }
    let margin = child.style.margin
    // CSS compatibility: margin percentages are resolved against the width.
    let margin_left_auto = match margin.left {
      DimAuto => true
      _ => false
    }
    let margin_right_auto = match margin.right {
      DimAuto => true
      _ => false
    }
    let margin_top_auto = match margin.top {
      DimAuto => true
      _ => false
    }
    let margin_bottom_auto = match margin.bottom {
      DimAuto => true
      _ => false
    }
    let margin_left_fixed = @util.resolve_dimension_width_basis(
      margin.left,
      final_padding_box_width,
    )
    let margin_right_fixed = @util.resolve_dimension_width_basis(
      margin.right,
      final_padding_box_width,
    )
    let margin_top_fixed = @util.resolve_dimension_width_basis(
      margin.top,
      final_padding_box_width,
    )
    let margin_bottom_fixed = @util.resolve_dimension_width_basis(
      margin.bottom,
      final_padding_box_width,
    )
    let inset = child.style.inset
    let left = @util.resolve_optional_dimension(inset.left, abs_available.width)
    let right = @util.resolve_optional_dimension(
      inset.right,
      abs_available.width,
    )
    let top = @util.resolve_optional_dimension(inset.top, abs_available.height)
    let bottom = @util.resolve_optional_dimension(
      inset.bottom,
      abs_available.height,
    )

    // Compute the used size for absolutely positioned items.
    let mut used_width = @util.resolve_optional_dimension(
      child.style.size.width,
      abs_available.width,
    )
    let mut used_height = @util.resolve_optional_dimension(
      child.style.size.height,
      abs_available.height,
    )
    let width_was_auto = match used_width {
      Some(_) => false
      None => true
    }
    let height_was_auto = match used_height {
      Some(_) => false
      None => true
    }
    let mut width_from_inset = false
    let mut height_from_inset = false
    // If size is auto and both insets are definite, the size is determined by the inset constraints.
    match used_width {
      Some(_) => ()
      None =>
        match (left, right) {
          (Some(l), Some(r)) =>
            used_width = Some(
              @util.max_double(
                final_padding_box_width -
                l -
                r -
                margin_left_fixed -
                margin_right_fixed,
                0.0,
              ),
            )
          _ => ()
        }
    }
    match used_height {
      Some(_) => ()
      None =>
        match (top, bottom) {
          (Some(t), Some(b)) =>
            used_height = Some(
              @util.max_double(
                final_padding_box_height -
                t -
                b -
                margin_top_fixed -
                margin_bottom_fixed,
                0.0,
              ),
            )
          _ => ()
        }
    }
    match (used_width, used_height) {
      (Some(_), _) =>
        if width_was_auto {
          match (left, right) {
            (Some(_), Some(_)) => width_from_inset = true
            _ => ()
          }
        } else {
          ()
        }
      _ => ()
    }
    match (used_width, used_height) {
      (_, Some(_)) =>
        if height_was_auto {
          match (top, bottom) {
            (Some(_), Some(_)) => height_from_inset = true
            _ => ()
          }
        } else {
          ()
        }
      _ => ()
    }
    // Apply aspect ratio when only one axis is known.
    match child.style.aspect_ratio {
      Some(ratio) =>
        if ratio > 0.0 {
          match (used_width, used_height) {
            (Some(w), None) => used_height = Some((w / ratio).round())
            (None, Some(h)) => used_width = Some((h * ratio).round())
            (Some(w), Some(_h)) =>
              if width_was_auto &&
                height_was_auto &&
                width_from_inset &&
                height_from_inset {
                used_height = Some((w / ratio).round())
              } else {
                ()
              }
            _ => ()
          }
        }
      None => ()
    }

    // Apply aspect ratio to min/max constraints (chicle 0.5 behavior).
    let mut min_width = @util.resolve_optional_dimension(
      child.style.min_size.width,
      abs_available.width,
    )
    let mut min_height = @util.resolve_optional_dimension(
      child.style.min_size.height,
      abs_available.height,
    )
    let mut max_width = @util.resolve_optional_dimension(
      child.style.max_size.width,
      abs_available.width,
    )
    let mut max_height = @util.resolve_optional_dimension(
      child.style.max_size.height,
      abs_available.height,
    )
    match child.style.aspect_ratio {
      Some(ratio) =>
        if ratio > 0.0 {
          match (min_width, min_height) {
            (None, Some(h)) => min_width = Some((h * ratio).round())
            (Some(w), None) => min_height = Some((w / ratio).round())
            _ => ()
          }
          match (max_width, max_height) {
            (None, Some(h)) => max_width = Some((h * ratio).round())
            (Some(w), None) => max_height = Some((w / ratio).round())
            _ => ()
          }
        } else {
          ()
        }
      None => ()
    }

    // First pass: determine intrinsic size under the containing block constraints, if needed.
    let intrinsic = match (used_width, used_height) {
      (Some(w), Some(h)) => Size(width=w, height=h)
      _ => {
        view.perform_child_layout(
          child_id,
          Size(width=None, height=None),
          abs_available,
          Point::zero(),
          false,
        )
        tree.nodes[child_id].unrounded_layout.size
      }
    }
    let final_width = match used_width {
      Some(w) => w
      None => intrinsic.width
    }
    let final_height = match used_height {
      Some(h) => h
      None => intrinsic.height
    }
    let mut clamped_final_width = final_width
    let mut clamped_final_height = final_height
    match min_width {
      Some(m) => clamped_final_width = @util.max_double(clamped_final_width, m)
      None => ()
    }
    match max_width {
      Some(m) =>
        if clamped_final_width > m {
          clamped_final_width = m
        } else {
          ()
        }
      None => ()
    }
    match min_height {
      Some(m) =>
        clamped_final_height = @util.max_double(clamped_final_height, m)
      None => ()
    }
    match max_height {
      Some(m) =>
        if clamped_final_height > m {
          clamped_final_height = m
        } else {
          ()
        }
      None => ()
    }

    // Second pass: compute final size (may be clamped by min/max).
    view.perform_child_layout(
      child_id,
      Size(width=Some(clamped_final_width), height=Some(clamped_final_height)),
      abs_available,
      Point::zero(),
      false,
    )
    let final_size = tree.nodes[child_id].unrounded_layout.size
    let mut final_margin_left = if margin_left_auto {
      0.0
    } else {
      margin_left_fixed
    }
    let mut final_margin_right = if margin_right_auto {
      0.0
    } else {
      margin_right_fixed
    }
    let mut final_margin_top = if margin_top_auto {
      0.0
    } else {
      margin_top_fixed
    }
    let mut final_margin_bottom = if margin_bottom_auto {
      0.0
    } else {
      margin_bottom_fixed
    }

    // Absolute auto margins: distribute remaining space when both inset sides are definite.
    match (left, right) {
      (Some(l), Some(r)) => {
        let fixed = (if margin_left_auto { 0.0 } else { final_margin_left }) +
          (if margin_right_auto { 0.0 } else { final_margin_right })
        let remaining = final_padding_box_width -
          l -
          r -
          final_size.width -
          fixed
        let auto_count = (if margin_left_auto { 1 } else { 0 }) +
          (if margin_right_auto { 1 } else { 0 })
        match auto_count {
          2 =>
            if remaining >= 0.0 {
              final_margin_left = remaining / 2.0
              final_margin_right = remaining / 2.0
            } else {
              final_margin_left = 0.0
              final_margin_right = 0.0
            }
          1 =>
            if margin_left_auto {
              final_margin_left = remaining
            } else {
              final_margin_right = remaining
            }
          _ => ()
        }
      }
      _ => ()
    }
    match (top, bottom) {
      (Some(t), Some(b)) => {
        let fixed = (if margin_top_auto { 0.0 } else { final_margin_top }) +
          (if margin_bottom_auto { 0.0 } else { final_margin_bottom })
        let remaining = final_padding_box_height -
          t -
          b -
          final_size.height -
          fixed
        let auto_count = (if margin_top_auto { 1 } else { 0 }) +
          (if margin_bottom_auto { 1 } else { 0 })
        match auto_count {
          2 =>
            if remaining >= 0.0 {
              final_margin_top = remaining / 2.0
              final_margin_bottom = remaining / 2.0
            } else {
              final_margin_top = 0.0
              final_margin_bottom = 0.0
            }
          1 =>
            if margin_top_auto {
              final_margin_top = remaining
            } else {
              final_margin_bottom = remaining
            }
          _ => ()
        }
      }
      _ => ()
    }
    let x_in_padding = match left {
      Some(v) => v + final_margin_left
      None =>
        match right {
          Some(v) =>
            final_padding_box_width - v - final_margin_right - final_size.width
          None => padding.left + final_margin_left
        }
    }
    let y_in_padding = match top {
      Some(v) => v + final_margin_top
      None =>
        match bottom {
          Some(v) =>
            final_padding_box_height -
            v -
            final_margin_bottom -
            final_size.height
          None => padding.top + static_y + final_margin_top
        }
    }
    view.perform_child_layout(
      child_id,
      Size(width=Some(final_size.width), height=Some(final_size.height)),
      abs_available,
      Point(
        x=padding_origin.x + x_in_padding,
        y=padding_origin.y + y_in_padding,
      ),
      false,
    )
  }
}