// 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.

///|
/// Helpers for rendering buffers and editors.
///
/// Ported from `cosmic-text/src/render.rs`.

///|
/// Custom renderer for buffers and editors.
pub(open) trait Renderer {
  /// Render a rectangle at x, y with size w, h and the provided `Color`.
  fn rectangle(Self, x : Int, y : Int, w : UInt, h : UInt, color : Color) -> Unit

  /// Render a `PhysicalGlyph` with the provided `Color`.
  ///
  /// For performance, consider using `SwashCache` directly.
  fn glyph(Self, physical_glyph : PhysicalGlyph, color : Color) -> Unit
}

///|
fn minf(a : Float, b : Float) -> Float {
  if a < b {
    a
  } else {
    b
  }
}

///|
fn maxf(a : Float, b : Float) -> Float {
  if a > b {
    a
  } else {
    b
  }
}

///|
fn render_int_from_float(v : Float) -> Int {
  v.to_double().to_int()
}

///|
fn render_uint_from_float(v : Float) -> UInt {
  if v <= 0.0F {
    0U
  } else {
    v.to_double().to_int().reinterpret_as_uint()
  }
}

///|
fn pick_decoration_color(
  span_color_opt : Color?,
  override_color_opt : Color?,
  default_color : Color,
) -> Color {
  match override_color_opt {
    Some(c) => c
    None =>
      match span_color_opt {
        Some(c) => c
        None => default_color
      }
  }
}

///|
fn deco_thickness(v : Float) -> Float {
  let t = if v < 1.0F { 1.0F } else { v }
  Float::from_double(t.to_double().ceil())
}

///|
fn[R : Renderer] draw_decoration_span(
  renderer : R,
  run : LayoutRun,
  span : DecorationSpan,
  default_color : Color,
) -> Unit {
  let glyph_count = run.glyphs.length()
  if glyph_count == 0 {
    return
  }
  let start = if span.glyph_start < 0 {
    0
  } else if span.glyph_start > glyph_count {
    glyph_count
  } else {
    span.glyph_start
  }
  let end = if span.glyph_end < start {
    start
  } else if span.glyph_end > glyph_count {
    glyph_count
  } else {
    span.glyph_end
  }
  if end <= start {
    return
  }

  let mut x_min = run.glyphs[start].x
  let mut x_max = run.glyphs[start].x + run.glyphs[start].w
  for i in start.. ()
    UnderlineStyle::Single => {
      let color = pick_decoration_color(
        span.color_opt,
        td.underline_color_opt,
        default_color,
      )
      let thickness = deco_thickness(
        deco.underline_metrics.thickness * font_size,
      )
      let y = run.line_y - deco.underline_metrics.offset * font_size
      renderer.rectangle(
        x_start,
        render_int_from_float(y),
        w,
        render_uint_from_float(thickness),
        color,
      )
    }
    UnderlineStyle::Double => {
      let color = pick_decoration_color(
        span.color_opt,
        td.underline_color_opt,
        default_color,
      )
      let thickness = deco_thickness(
        deco.underline_metrics.thickness * font_size,
      )
      let gap = thickness
      let y = run.line_y - deco.underline_metrics.offset * font_size
      renderer.rectangle(
        x_start,
        render_int_from_float(y),
        w,
        render_uint_from_float(thickness),
        color,
      )
      renderer.rectangle(
        x_start,
        render_int_from_float(y + thickness + gap),
        w,
        render_uint_from_float(thickness),
        color,
      )
    }
  }

  if td.strikethrough {
    let color = pick_decoration_color(
      span.color_opt,
      td.strikethrough_color_opt,
      default_color,
    )
    let thickness = deco_thickness(
      deco.strikethrough_metrics.thickness * font_size,
    )
    let y = run.line_y - deco.strikethrough_metrics.offset * font_size
    renderer.rectangle(
      x_start,
      render_int_from_float(y),
      w,
      render_uint_from_float(thickness),
      color,
    )
  }

  if td.overline {
    let color = pick_decoration_color(
      span.color_opt,
      td.overline_color_opt,
      default_color,
    )
    let thickness = deco_thickness(deco.underline_metrics.thickness * font_size)
    let y = maxf(run.line_y - deco.ascent * font_size, run.line_top)
    renderer.rectangle(
      x_start,
      render_int_from_float(y),
      w,
      render_uint_from_float(thickness),
      color,
    )
  }
}

///|
/// Draw text decoration lines (underline, strikethrough, overline) for a layout run.
pub fn[R : Renderer] render_decoration(
  renderer : R,
  run : LayoutRun,
  default_color : Color,
) -> Unit {
  for span in run.decorations {
    draw_decoration_span(renderer, run, span, default_color)
  }
}

///|
/// Helper to migrate from the legacy callback-based renderer.
///
/// This matches upstream `LegacyRenderer`: rectangles are forwarded to the callback,
/// and glyphs are rasterized through `SwashCache::with_pixels`.
pub struct LegacyRenderer {
  font_system : FontSystem
  cache : SwashCache
  callback : (Int, Int, UInt, UInt, Color) -> Unit
}

///|
pub fn LegacyRenderer::new(
  font_system : FontSystem,
  cache : SwashCache,
  callback : (Int, Int, UInt, UInt, Color) -> Unit,
) -> LegacyRenderer {
  LegacyRenderer::{ font_system, cache, callback }
}

///|
pub impl Renderer for LegacyRenderer with fn rectangle(self, x, y, w, h, color) {
  (self.callback)(x, y, w, h, color)
}

///|
pub impl Renderer for LegacyRenderer with fn glyph(self, physical_glyph, color) {
  self.cache.with_pixels(self.font_system, physical_glyph.cache_key, color, fn(
    px,
    py,
    pixel_color,
  ) {
    (self.callback)(
      physical_glyph.x + px,
      physical_glyph.y + py,
      1U,
      1U,
      pixel_color,
    )
  })
}