///|
priv struct DrawingAxisMetrics {
  axis : String
  limit : Int
  default_px : Int
  override_indices : Array[Int]
  prefix_deltas : Array[Int64]
}

///|
priv struct DrawingMetrics {
  cols : DrawingAxisMetrics
  rows : DrawingAxisMetrics
}

///|
fn drawing_axis_metrics_for_sheet(
  sheet : Worksheet,
  axis : StringView,
) -> DrawingAxisMetrics raise XlsxError {
  let (limit, default_px) = if axis == "col" {
    (cell_ref_max_cols, sheet.default_col_width_pixels())
  } else if axis == "row" {
    (cell_ref_max_rows, sheet.default_row_height_pixels())
  } else {
    raise InvalidXml(msg="drawing axis invalid")
  }
  if default_px < 0 {
    raise InvalidXml(msg="drawing cell dimension invalid")
  }
  let overrides : Array[(Int, Int)] = []
  if axis == "col" {
    for idx1, _ in sheet.col_dimensions {
      if idx1 <= 0 || idx1 > limit {
        raise InvalidXml(msg="drawing cell dimension invalid")
      }
      let actual_px = sheet.col_width_pixels(idx1)
      if actual_px < 0 {
        raise InvalidXml(msg="drawing cell dimension invalid")
      }
      overrides.push((idx1 - 1, actual_px - default_px))
    }
  } else {
    for idx1, _ in sheet.row_dimensions {
      if idx1 <= 0 || idx1 > limit {
        raise InvalidXml(msg="drawing cell dimension invalid")
      }
      let actual_px = sheet.row_height_pixels(idx1)
      if actual_px < 0 {
        raise InvalidXml(msg="drawing cell dimension invalid")
      }
      overrides.push((idx1 - 1, actual_px - default_px))
    }
  }
  overrides.sort_by((left, right) => {
    let (left_index, _) = left
    let (right_index, _) = right
    if left_index < right_index {
      -1
    } else if left_index > right_index {
      1
    } else {
      0
    }
  })
  let override_indices : Array[Int] = []
  let prefix_deltas : Array[Int64] = [0L]
  let mut delta_sum = 0L
  for entry in overrides {
    let (index, delta) = entry
    override_indices.push(index)
    delta_sum = delta_sum + Int64::from_int(delta)
    prefix_deltas.push(delta_sum)
  }
  { axis: axis.to_owned(), limit, default_px, override_indices, prefix_deltas }
}

///|
fn drawing_metrics_for_sheet(
  sheet : Worksheet,
) -> DrawingMetrics raise XlsxError {
  {
    cols: drawing_axis_metrics_for_sheet(sheet, "col"),
    rows: drawing_axis_metrics_for_sheet(sheet, "row"),
  }
}

///|
fn DrawingAxisMetrics::delta_before(
  self : DrawingAxisMetrics,
  idx0 : Int,
) -> Int64 {
  let mut low = 0
  let mut high = self.override_indices.length()
  while low < high {
    let mid = low + (high - low) / 2
    if self.override_indices[mid] < idx0 {
      low = mid + 1
    } else {
      high = mid
    }
  }
  self.prefix_deltas[low]
}

///|
fn DrawingAxisMetrics::pixel_span(
  self : DrawingAxisMetrics,
  start_idx0 : Int,
  end_idx0 : Int,
) -> Int64 raise XlsxError {
  if start_idx0 < 0 || end_idx0 < start_idx0 || end_idx0 > self.limit {
    raise InvalidXml(msg="drawing " + self.axis + " invalid")
  }
  let count = end_idx0 - start_idx0
  let base = Int64::from_int(self.default_px) * Int64::from_int(count)
  let delta = self.delta_before(end_idx0) - self.delta_before(start_idx0)
  let total = base + delta
  if total < 0L {
    raise InvalidXml(msg="drawing span invalid")
  }
  total
}

///|
test "drawing metrics wb: sparse overrides use logarithmic range sums" {
  let sheet = Worksheet::new("Sheet1")
  sheet.set_col_width(501, 10.0)
  let metrics = drawing_metrics_for_sheet(sheet)
  inspect(metrics.cols.pixel_span(0, 1000), content="64016")
  inspect(metrics.cols.pixel_span(500, 501), content="80")
}