// 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] baseline_offset_y(
  tree : ChicleTree[C],
  node_id : NodeId,
) -> Double raise ChicleError {
  let node = match tree.nodes.get(node_id) {
    Some(n) => n
    None => raise InvalidNodeId(node_id)
  }
  let node_layout = node.unrounded_layout
  match node.style.display {
    DisplayBlock | DisplayGrid => node_layout.size.height
    _ =>
      if tree.children[node_id].length() == 0 {
        node_layout.size.height
      } else {
        if node.style.display is DisplayFlex &&
          !is_column(node.style.flex_direction) {
          let default_align = match node.style.align_items {
            Some(v) => v
            None => ItemsStretch
          }
          let mut first_line_top : Double? = None
          for child_id in tree.children[node_id] {
            let child = match tree.nodes.get(child_id) {
              Some(c) => c
              None => raise InvalidNodeId(child_id)
            }
            match (child.style.display, child.style.position) {
              (DisplayNone, _) | (_, PosAbsolute) => ()
              _ => {
                let rel_top = child.unrounded_layout.location.y -
                  node_layout.location.y
                first_line_top = match first_line_top {
                  Some(v) => Some(@util.min_double(v, rel_top))
                  None => Some(rel_top)
                }
              }
            }
          }
          match first_line_top {
            Some(first_line_top) => {
              let mut first_candidate : NodeId? = None
              let mut baseline_candidate : NodeId? = None
              for child_id in tree.children[node_id] {
                let child = match tree.nodes.get(child_id) {
                  Some(c) => c
                  None => raise InvalidNodeId(child_id)
                }
                match (child.style.display, child.style.position) {
                  (DisplayNone, _) | (_, PosAbsolute) => ()
                  _ => {
                    let rel_top = child.unrounded_layout.location.y -
                      node_layout.location.y
                    if @util.abs_double(rel_top - first_line_top) < 0.0001 {
                      if first_candidate is None {
                        first_candidate = Some(child_id)
                      }
                      let align = match child.style.align_self {
                        Some(v) => v
                        None => default_align
                      }
                      if baseline_candidate is None && align is ItemsBaseline {
                        baseline_candidate = Some(child_id)
                      }
                    }
                  }
                }
              }
              let chosen = match baseline_candidate {
                Some(id) => Some(id)
                None => first_candidate
              }
              match chosen {
                Some(chosen_id) => {
                  let chosen_layout = tree.nodes[chosen_id].unrounded_layout
                  return chosen_layout.location.y -
                    node_layout.location.y +
                    baseline_offset_y(tree, chosen_id)
                }
                None => ()
              }
            }
            None => ()
          }
        }
        for child_id in tree.children[node_id] {
          let child = match tree.nodes.get(child_id) {
            Some(c) => c
            None => raise InvalidNodeId(child_id)
          }
          match child.style.display {
            DisplayNone => ()
            _ =>
              match child.style.position {
                PosAbsolute => ()
                PosRelative => {
                  let child_layout = child.unrounded_layout
                  let child_margin_top = if node.style.display is DisplayFlex &&
                    is_column(node.style.flex_direction) {
                    match child.style.margin.top {
                      DimPercent(_) =>
                        @util.resolve_dimension_width_basis(
                          child.style.margin.top,
                          node_layout.size.width,
                        )
                      _ => 0.0
                    }
                  } else {
                    0.0
                  }
                  return child_layout.location.y -
                    node_layout.location.y +
                    baseline_offset_y(tree, child_id) -
                    child_margin_top
                }
              }
          }
        }
        node_layout.size.height
      }
  }
}

///|
fn[C] grid_item_baseline_offset_y(
  tree : ChicleTree[C],
  node_id : NodeId,
) -> Double raise ChicleError {
  let node = match tree.nodes.get(node_id) {
    Some(n) => n
    None => raise InvalidNodeId(node_id)
  }
  let node_layout = node.unrounded_layout
  if tree.children[node_id].length() == 0 {
    node_layout.size.height
  } else {
    for child_id in tree.children[node_id] {
      let child = match tree.nodes.get(child_id) {
        Some(c) => c
        None => raise InvalidNodeId(child_id)
      }
      match child.style.display {
        DisplayNone => ()
        _ =>
          match child.style.position {
            PosAbsolute => ()
            PosRelative => {
              let child_layout = child.unrounded_layout
              return child_layout.location.y -
                node_layout.location.y +
                grid_item_baseline_offset_y(tree, child_id)
            }
          }
      }
    }
    node_layout.size.height
  }
}