///|
/// One unique image source; counts use x-low,x-high,y-low,y-high,z-low,z-high.
pub struct ImageSource {
  image : Vec3
  bounces : Array[Int32]
  order : Int32
  reflection : Double
}

///|
fn axis_images(
  length : Double,
  source : Double,
  order : Int32,
) -> Array[(Double, Int32, Int32)] {
  let out : Array[(Double, Int32, Int32)] = []
  for n = -order; n <= order; n = n + 1 {
    for p = 0; p < 2; p = p + 1 {
      let low = (n - p).abs()
      let high = n.abs()
      if low + high <= order {
        out.push(
          (
            (1 - 2 * p).to_double() * source + 2.0 * n.to_double() * length,
            low,
            high,
          ),
        )
      }
    }
  }
  out
}

///|
fn power(base : Double, count : Int32) -> Double {
  let mut result = 1.0
  for i = 0; i < count; i = i + 1 {
    result = result * base
  }
  result
}

///|
/// Enumerate distinct images by total reflection order, including zero-gain paths.
pub fn images(s : Scene) -> Array[ImageSource] {
  let xs = axis_images(s.room.x, s.source.x, s.max_order)
  let ys = axis_images(s.room.y, s.source.y, s.max_order)
  let zs = axis_images(s.room.z, s.source.z, s.max_order)
  let beta = s.absorption.map(fn(a) { (1.0 - a).sqrt() })
  let out : Array[ImageSource] = []
  for x in xs {
    for y in ys {
      for z in zs {
        let (xp, xl, xh) = x
        let (yp, yl, yh) = y
        let (zp, zl, zh) = z
        let order = xl + xh + yl + yh + zl + zh
        if order > s.max_order {
          continue
        }
        let bounces = [xl, xh, yl, yh, zl, zh]
        let mut reflection = 1.0
        for i = 0; i < 6; i = i + 1 {
          reflection = reflection * power(beta[i], bounces[i])
        }
        out.push({
          image: { x: xp, y: yp, z: zp, },
          bounces,
          order,
          reflection,
        })
      }
    }
  }
  out.sort_by(fn(a, b) {
    if a.order != b.order {
      a.order - b.order
    } else if a.image.x < b.image.x {
      -1
    } else if a.image.x > b.image.x {
      1
    } else if a.image.y < b.image.y {
      -1
    } else if a.image.y > b.image.y {
      1
    } else if a.image.z < b.image.z {
      -1
    } else if a.image.z > b.image.z {
      1
    } else {
      0
    }
  })
  out
}