// Copyright 2026 Ling71671
//
// 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.

///|
pub struct FrameOutput {
  draw_list : @draw.DrawList
  cam_x : Double
  cam_y : Double
  zoom : Double
  grid_dim : Int
  visible_cells : Int
  selected_id : Int
  show_studio : Bool
  is_vector : Bool
  /// Cursor icon the engine wants while the pointer sits where it does, so the
  /// host page can mirror it onto `canvas.style.cursor`.
  cursor : String
  navigate_url : String
}

///|
/// Core benchmark frame update function supporting 128x128, 256x256, 512x512, 1024x1024.
///
/// Input crosses the JS boundary as primitives - pointer, the text typed this
/// frame, the named keys pressed and released this frame, and a modifier
/// bitmask - and `host_input.mbt` turns them into the library's events.
pub fn step(
  mouse_x : Double,
  mouse_y : Double,
  mouse_down : Bool,
  pan_dx : Double,
  pan_dy : Double,
  zoom_delta : Double,
  switch_mode : Int,
  vp_w : Double,
  vp_h : Double,
  text_input : String,
  keys_pressed : Array[String],
  keys_released : Array[String],
  mod_flags : Int,
) -> FrameOutput {
  state.frame_tick = state.frame_tick + 1.0

  // 1. External UI buttons & Mode Switcher
  apply_switch_mode(state, switch_mode, vp_w, vp_h)

  let cur_w = if vp_w > 100.0 { vp_w } else { 920.0 }
  let cur_h = if vp_h > 100.0 { vp_h } else { 540.0 }
  let win_w = 264.0
  let win_h = 340.0

  let scale = ctx.style.scale
  let top_bar_h = 38.0 * scale
  let vp_x = 0.0
  let vp_y = top_bar_h
  let ruler_h = 22.0
  let ruler_w = 48.0
  let cv_x = vp_x + ruler_w
  let cv_y = vp_y + ruler_h
  let cv_w = cur_w - ruler_w
  let cv_h = cur_h - top_bar_h - ruler_h
  let sc_x = cv_x + cv_w * 0.5
  let sc_y = cv_y + cv_h * 0.5

  // 2. Camera Pan & Zoom updates (pinning world point under mouse cursor).
  // The floating panel now owns its own drag and viewport clamping.
  if mouse_y >= top_bar_h {
    update_camera_pan(state, ctx, pan_dx, pan_dy)
  }
  if !mouse_down {
    state.is_dragging_item = false
  }
  update_camera_zoom(state, ctx, zoom_delta, mouse_x, mouse_y, sc_x, sc_y)

  // 4. Begin GUI Frame
  let raw = host_raw_input(
    mouse_x, mouse_y, mouse_down, text_input, keys_pressed, keys_released, mod_flags,
  )
  ctx.begin_frame(raw)
  let dl = ctx.draw_list()

  // 5. Native In-Canvas Application Header Bar
  let nav_url = render_benchmark_header_bar(ctx, state, cur_w, cur_h, top_bar_h)

  let grid_dim = state.grid_dim
  let matrix_world_size = 10000.0
  let pitch = matrix_world_size / grid_dim.to_double()
  let cell_size = pitch

  let in_canvas = mouse_x >= cv_x &&
    mouse_x <= cv_x + cv_w &&
    mouse_y >= cv_y &&
    mouse_y <= cv_y + cv_h

  let mwx = state.cam_x + (mouse_x - sc_x) / state.zoom
  let mwy = state.cam_y + (mouse_y - sc_y) / state.zoom

  // 6. Node Hit Test & Ripple Physics
  handle_node_hit_test(
    state, ctx, mouse_down, in_canvas, mwx, mwy, pitch, grid_dim, matrix_world_size,
  )
  update_ripple_physics(state, matrix_world_size, pitch)

  // 7. Spatial Viewport Culling Bounds
  let (c_start, c_end, r_start, r_end, total_vis, min_wx, max_wx, min_wy, max_wy
  ) = calc_culling_bounds(state, cv_w, cv_h, pitch, grid_dim)

  // 8. Background Major Coordinate Aisles
  dl.push_clip(@math.Rect::new(cv_x, cv_y, cv_w, cv_h))
  if state.show_grid {
    render_grid_aisles(dl, state, cv_x, cv_y, cv_w, cv_h, sc_x, sc_y)
  }
  dl.pop_clip()

  // 9. Matrix Nodes (LOD Vector streaming or Background probe)
  let is_vector = render_matrix_nodes(
    dl, state, in_canvas, mouse_x, mouse_y, cv_x, cv_y, cv_w, cv_h, sc_x, sc_y, grid_dim,
    pitch, cell_size, mwx, mwy, c_start, c_end, r_start, r_end, total_vis, matrix_world_size,
  )

  // 10. Selected Item Transform Box & Floating Inspector Tag
  render_selected_transform_frame(
    dl, state, cv_x, cv_y, cv_w, cv_h, sc_x, sc_y, pitch, cell_size, grid_dim,
  )

  // 11. CAD Rulers & Projection Indicators
  render_cad_rulers(
    dl, state, cv_x, cv_y, cv_w, cv_h, vp_x, vp_y, ruler_w, ruler_h, min_wx, max_wx,
    min_wy, max_wy, sc_x, sc_y,
  )
  render_ruler_projections(
    dl, state, cv_x, cv_y, cv_w, cv_h, vp_x, vp_y, ruler_w, ruler_h, sc_x, sc_y,
    pitch, cell_size, grid_dim,
  )

  // 12. Floating In-Canvas IMGUI Controls Window
  render_studio_window(ctx, state, cur_w, cur_h, win_w, win_h)

  // 13. End Frame & Output DrawList
  let final_dl = ctx.end_frame()
  {
    draw_list: final_dl,
    cam_x: state.cam_x,
    cam_y: state.cam_y,
    zoom: state.zoom,
    grid_dim: state.grid_dim,
    visible_cells: state.visible_cells,
    selected_id: state.selected_id,
    show_studio: state.show_studio,
    is_vector,
    cursor: ctx.cursor_icon(),
    navigate_url: nav_url,
  }
}

///|
fn main {
  let frame = step(
    580.0,
    270.0,
    false,
    0.0,
    0.0,
    1.0,
    -1,
    920.0,
    540.0,
    "",
    [],
    [],
    0,
  )
  // keep mines_step reachable so the JS build emits it for minesweeper.html
  let _ = mines_step(0.0, 0.0, false, false, 0.0, 0.0, 0.0, -1, 920.0, 540.0)
  let _ = gallery_step(
    "knob",
    0.0,
    0.0,
    false,
    0.0,
    800.0,
    600.0,
    "",
    [],
    [],
    0,
  )
  let _ = get_gallery_status()
  let _ = get_gallery_cursor()
  let _ = studio_step(1200.0, 800.0, 0.0, 0.0, false, 0.0, "", [], [], 0)
  let _ = get_studio_cursor()
  println(
    "Benchmark initialized, total commands: \{frame.draw_list.len()}, zoom: \{frame.zoom}",
  )
}