///|
/// Scale planning helpers for screen recordings and sticker exports.
pub(all) struct ScalePlan {
  source_width : Int
  source_height : Int
  target_width : Int
  target_height : Int
  scale_per_mille : Int
  crop : Bool
} derive(Debug)

///|
pub fn ScalePlan::source(self : ScalePlan) -> (Int, Int) {
  (self.source_width, self.source_height)
}

///|
pub fn ScalePlan::target(self : ScalePlan) -> (Int, Int) {
  (self.target_width, self.target_height)
}

///|
pub fn ScalePlan::scale(self : ScalePlan) -> Int {
  self.scale_per_mille
}

///|
pub fn ScalePlan::crop(self : ScalePlan) -> Bool {
  self.crop
}

///|
/// Choose a contain or cover plan with integer aspect comparison.
pub fn make_scale_plan(
  source_width : Int,
  source_height : Int,
  target_width : Int,
  target_height : Int,
  crop : Bool,
) -> ScalePlan {
  let sw = if source_width < 1 { 1 } else { source_width }
  let sh = if source_height < 1 { 1 } else { source_height }
  let tw = if target_width < 1 { 1 } else { target_width }
  let th = if target_height < 1 { 1 } else { target_height }
  let horizontal = tw * 1000 / sw
  let vertical = th * 1000 / sh
  let scale = if crop {
    if horizontal > vertical {
      horizontal
    } else {
      vertical
    }
  } else if horizontal < vertical {
    horizontal
  } else {
    vertical
  }
  ScalePlan::{
    source_width: sw,
    source_height: sh,
    target_width: tw,
    target_height: th,
    scale_per_mille: scale,
    crop,
  }
}

///|
/// Execute a scale plan with stable nearest-neighbor behavior.
pub fn Canvas::apply_scale_plan(self : Canvas, plan : ScalePlan) -> Canvas {
  let scaled = self.resize_nearest(
    width=self.width * plan.scale_per_mille / 1000,
    height=self.height * plan.scale_per_mille / 1000,
  )
  if plan.crop {
    scaled.crop(
      x=(scaled.width - plan.target_width) / 2,
      y=(scaled.height - plan.target_height) / 2,
      width=plan.target_width,
      height=plan.target_height,
    )
  } else {
    contain_layout(
      scaled,
      plan.target_width,
      plan.target_height,
      rgba(0, 0, 0, 0),
    )
  }
}

///|
/// Apply one scale plan to every frame and reset offsets to the new screen.
pub fn Animation::apply_scale_plan(
  self : Animation,
  plan : ScalePlan,
) -> Animation {
  let result = Animation::new(
      width=plan.target_width,
      height=plan.target_height,
    )
    .with_loop_count(loop_count=self.loop_count)
    .with_background(background=self.background)
  for frame in self.frames {
    ignore(
      result.add_frame(
        frame
        .with_canvas(frame.canvas().apply_scale_plan(plan))
        .with_offset(left=0, top=0),
      ),
    )
  }
  result
}