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

///|
/// Renderer scaffolding.
///
/// Ported from upstream `swash::scale::Render`.
pub struct Render {
  sources : Array[Source]
  format : @zeno.Format
  offset : @zeno.Vector
  transform : @zeno.Transform?
  embolden : Double
  foreground : Array[Byte]
  style : @zeno.Style
}

///|

///|
fn ensure_zeroed(buf : Array[Byte], len : Int) -> Array[Byte] {
  if len <= 0 {
    buf.clear()
    return buf
  }
  if buf.capacity() < len {
    return Array::makei(len, _ => b'\x00')
  }
  if buf.length() < len {
    for _ in buf.length().. len {
    buf.truncate(len)
  }
  // zeno subpixel masks only write RGB channels; always clear to avoid stale
  // alpha/unused channels when reusing buffers.
  for i in 0.. Render {
  Render::{
    sources,
    format: @zeno.Format::Alpha,
    offset: @zeno.Vector::Vector(0.0, 0.0),
    transform: None,
    embolden: 0.0,
    foreground: [b'\x80', b'\x80', b'\x80', b'\xFF'],
    style: @zeno.Style::default(),
  }
}

///|
pub fn Render::format(self : Render, format : @zeno.Format) -> Render {
  Render::{
    sources: self.sources,
    format,
    offset: self.offset,
    transform: self.transform,
    embolden: self.embolden,
    foreground: self.foreground,
    style: self.style,
  }
}

///|
pub fn Render::style(self : Render, style : @zeno.Style) -> Render {
  Render::{
    sources: self.sources,
    format: self.format,
    offset: self.offset,
    transform: self.transform,
    embolden: self.embolden,
    foreground: self.foreground,
    style,
  }
}

///|
pub fn Render::offset(self : Render, offset : @zeno.Vector) -> Render {
  Render::{
    sources: self.sources,
    format: self.format,
    offset,
    transform: self.transform,
    embolden: self.embolden,
    foreground: self.foreground,
    style: self.style,
  }
}

///|
pub fn Render::transform(self : Render, transform : @zeno.Transform?) -> Render {
  Render::{
    sources: self.sources,
    format: self.format,
    offset: self.offset,
    transform,
    embolden: self.embolden,
    foreground: self.foreground,
    style: self.style,
  }
}

///|
pub fn Render::embolden(self : Render, strength : Double) -> Render {
  Render::{
    sources: self.sources,
    format: self.format,
    offset: self.offset,
    transform: self.transform,
    embolden: strength,
    foreground: self.foreground,
    style: self.style,
  }
}

///|
pub fn Render::default_color(self : Render, color : Array[Byte]) -> Render {
  Render::{
    sources: self.sources,
    format: self.format,
    offset: self.offset,
    transform: self.transform,
    embolden: self.embolden,
    foreground: color,
    style: self.style,
  }
}

///|
pub fn Render::render_into(
  self : Render,
  scaler : Scaler,
  glyph_id : @swash.GlyphId,
  image : Image,
) -> Bool {
  for source in self.sources {
    match source {
      Source::Outline => {
        let outline = scaler.state.val.outline
        outline.clear()
        if !scaler.scale_outline_into(glyph_id, outline) {
          continue
        }
        if self.embolden != 0.0 {
          outline.embolden(self.embolden, self.embolden)
        }
        if self.transform is Some(t) {
          outline.transform(t)
        }
        let placement = @zeno.Mask::with_scratch(
            outline.path(),
            scaler.state.val.rcx,
          )
          .format(self.format)
          .origin(@zeno.Origin::BottomLeft)
          .style(self.style)
          .offset(self.offset)
          .render_offset(self.offset)
          .inspect((fmt, w, h) => {
            let n = fmt.buffer_size(w, h)
            image.data = ensure_zeroed(image.data, n)
          })
          .render_into(image.data)
        image.placement = placement
        image.content = match self.format {
          @zeno.Format::Alpha => Content::Mask
          _ => Content::SubpixelMask
        }
        image.source = Source::Outline
        return true
      }
      Source::ColorOutline(palette_index) => {
        let outline = scaler.state.val.outline
        outline.clear()
        if !scaler.scale_color_outline_into(glyph_id, outline) {
          continue
        }
        if self.transform is Some(t) {
          outline.transform(t)
        }
        let palettes = match to_skrifa_font(scaler.font) {
          None => None
          Some(skrifa_font) => Some(skrifa_font.color_palettes())
        }
        let total_bounds = outline.bounds()
        // Take offset into account when placing glyph.
        let base_x = (total_bounds.min.x() + self.offset.x()).floor().to_int()
        let base_y = (total_bounds.min.y() + self.offset.y()).ceil().to_int()
        let base_w = total_bounds.width().ceil().to_int().reinterpret_as_uint()
        let base_h = total_bounds.height().ceil().to_int().reinterpret_as_uint()
        let base_size = (base_w * base_h * 4U).reinterpret_as_int()
        image.data = ensure_zeroed(image.data, base_size)
        image.placement = Placement::{
          left: base_x,
          top: base_h.reinterpret_as_int() + base_y,
          width: base_w,
          height: base_h,
        }
        let mut ok = true
        for i in 0.. {
              ok = false
              break
            }
            Some(layer) => {
              scaler.state.val._scratch0.clear()
              let placement = @zeno.Mask::with_scratch(
                  layer.path(),
                  scaler.state.val.rcx,
                )
                .format(@zeno.Format::Alpha)
                .origin(@zeno.Origin::BottomLeft)
                .style(self.style)
                .offset(self.offset)
                .render_offset(self.offset)
                .inspect((fmt, w, h) => {
                  let n = fmt.buffer_size(w, h)
                  scaler.state.val._scratch0 = ensure_zeroed(
                    scaler.state.val._scratch0,
                    n,
                  )
                })
                .render_into(scaler.state.val._scratch0)
              let color = match layer.color_index() {
                None => self.foreground
                Some(ci) =>
                  match palettes {
                    None => self.foreground
                    Some(ps) =>
                      match ps.color(palette_index.to_int(), ci.to_int()) {
                        None => self.foreground
                        Some(c) => [c.r(), c.g(), c.b(), c.a()]
                      }
                  }
              }
              blit(
                scaler.state.val._scratch0,
                placement.width,
                placement.height,
                placement.left - base_x,
                base_h.reinterpret_as_int() + base_y - placement.top,
                color,
                image.data,
                base_w,
                base_h,
              )
            }
          }
        }
        if ok {
          image.source = Source::ColorOutline(palette_index)
          image.content = Content::Color
          return true
        }
      }
      Source::Bitmap(mode) => {
        if !scaler.has_bitmaps() {
          continue
        }
        if scaler.scale_bitmap_into(glyph_id, mode, image) {
          return true
        }
      }
      Source::ColorBitmap(mode) => {
        if !scaler.has_color_bitmaps() {
          continue
        }
        if scaler.scale_color_bitmap_into(glyph_id, mode, image) {
          return true
        }
      }
    }
  }
  false
}

///|
pub fn Render::render(
  self : Render,
  scaler : Scaler,
  glyph_id : @swash.GlyphId,
) -> Image? {
  let image = Image::Image()
  if self.render_into(scaler, glyph_id, image) {
    Some(image)
  } else {
    None
  }
}

///|
test "Render builder scaffolding" {
  let r = Render::Render([Source::Outline])
    .format(@zeno.Format::Subpixel)
    .offset(@zeno.Vector::Vector(0.25, 0.75))
  inspect(r.sources.length(), content="1")
}